1#!/usr/bin/env python3 2# 3# Copyright 2016 - The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17from enum import Enum 18from time import sleep 19 20from acts.controllers.relay_lib.errors import RelayConfigError 21 22 23class RelayState(Enum): 24 """Enum for possible Relay States.""" 25 # Pretend this means 'OFF' 26 NO = 'NORMALLY_OPEN' 27 # Pretend this means 'ON' 28 NC = 'NORMALLY_CLOSED' 29 30 31class SynchronizeRelays: 32 """A class that allows for relays to change state nearly simultaneously. 33 34 Can be used with the 'with' statement in Python: 35 36 with SynchronizeRelays(): 37 relay1.set_no() 38 relay2.set_nc() 39 40 Note that the thread will still wait for RELAY_TRANSITION_WAIT_TIME 41 after execution leaves the 'with' statement. 42 """ 43 _sync_sleep_flag = False 44 45 def __enter__(self): 46 self.prev_toggle_time = Relay.transition_wait_time 47 self.prev_sync_flag = SynchronizeRelays._sync_sleep_flag 48 Relay.transition_wait_time = 0 49 SynchronizeRelays._sync_sleep_flag = False 50 51 def __exit__(self, type, value, traceback): 52 if SynchronizeRelays._sync_sleep_flag: 53 sleep(Relay.transition_wait_time) 54 55 Relay.transition_wait_time = self.prev_toggle_time 56 SynchronizeRelays._sync_sleep_flag = self.prev_sync_flag 57 58 59class Relay(object): 60 """A class representing a single relay switch on a RelayBoard. 61 62 References to these relays are stored in both the RelayBoard and the 63 RelayDevice classes under the variable "relays". GenericRelayDevice can also 64 access these relays through the subscript ([]) operator. 65 66 At the moment, relays only have a valid state of 'ON' or 'OFF'. This may be 67 extended in a subclass if needed. Keep in mind that if this is done, changes 68 will also need to be made in the RelayRigParser class to initialize the 69 relays. 70 71 """ 72 """How long to wait for relays to transition state.""" 73 transition_wait_time = .2 74 button_press_time = .25 75 76 def __init__(self, relay_board, position): 77 self.relay_board = relay_board 78 self.position = position 79 self._original_state = None 80 self.relay_id = "%s/%s" % (self.relay_board.name, self.position) 81 82 def set_no(self): 83 """Sets the relay to the 'NO' state. Shorthand for set(RelayState.NO). 84 85 Blocks the thread for Relay.transition_wait_time. 86 """ 87 self.set(RelayState.NO) 88 89 def set_nc(self): 90 """Sets the relay to the 'NC' state. Shorthand for set(RelayState.NC). 91 92 Blocks the thread for Relay.transition_wait_time. 93 94 """ 95 self.set(RelayState.NC) 96 97 def toggle(self): 98 """Swaps the state from 'NO' to 'NC' or 'NC' to 'NO'. 99 Blocks the thread for Relay.transition_wait_time. 100 """ 101 if self.get_status() == RelayState.NO: 102 self.set(RelayState.NC) 103 else: 104 self.set(RelayState.NO) 105 106 def set(self, state): 107 """Sets the relay to the 'NO' or 'NC' state. 108 109 Blocks the thread for Relay.transition_wait_time. 110 111 Args: 112 state: either 'NO' or 'NC'. 113 114 Raises: 115 ValueError if state is not 'NO' or 'NC'. 116 117 """ 118 if self._original_state is None: 119 self._original_state = self.relay_board.get_relay_status( 120 self.position) 121 122 if state is not RelayState.NO and state is not RelayState.NC: 123 raise ValueError( 124 'Invalid state. Received "%s". Expected any of %s.' % 125 (state, [state for state in RelayState])) 126 if self.get_status() != state: 127 self.relay_board.set(self.position, state) 128 SynchronizeRelays._sync_sleep_flag = True 129 sleep(Relay.transition_wait_time) 130 131 def set_no_for(self, seconds=button_press_time): 132 """Sets the relay to 'NORMALLY_OPEN' for seconds. Blocks the thread. 133 134 Args: 135 seconds: The number of seconds to sleep for. 136 """ 137 self.set_no() 138 sleep(seconds) 139 self.set_nc() 140 141 def set_nc_for(self, seconds=button_press_time): 142 """Sets the relay to 'NORMALLY_CLOSED' for seconds. Blocks the thread. 143 144 Respects Relay.transition_wait_time for toggling state. 145 146 Args: 147 seconds: The number of seconds to sleep for. 148 """ 149 self.set_nc() 150 sleep(seconds) 151 self.set_no() 152 153 def get_status(self): 154 return self.relay_board.get_relay_status(self.position) 155 156 def clean_up(self): 157 """Does any clean up needed to allow the next series of tests to run. 158 159 For now, all this does is switches to its previous state. Inheriting 160 from this class and overriding this method would be the best course of 161 action to allow a more complex clean up to occur. If you do this, be 162 sure to make the necessary modifications in RelayRig.initialize_relay 163 and RelayRigParser.parse_json_relays. 164 """ 165 if self._original_state is not None: 166 self.set(self._original_state) 167 168 def is_dirty(self): 169 return self._original_state is not None 170 171 172class RelayDict(object): 173 """A wrapped dictionary that gives config errors upon failure. 174 175 Has the same interface as a dictionary, but when getting the key fails, the 176 dictionary returns a RelayConfigError, letting the user know that the reason 177 the dict failed to return a relay is because the relay was not found in the 178 config. 179 180 Also prevents modification of elements, because changing the relays here 181 does not change what they are in hardware. 182 """ 183 ERROR_MESSAGE = ('Error: Attempted to get relay "%s" in %s "%s" but the ' 184 'relay does not exist.\nExisting relays are: %s.\nMake ' 185 'sure the missing relay is added to the config file, and ' 186 'is properly setup.') 187 188 def __init__(self, relay_device, input_dict): 189 self.relay_device = relay_device 190 self._store = input_dict 191 192 def __getitem__(self, key): 193 try: 194 return self._store[key] 195 except KeyError: 196 raise RelayConfigError(self.ERROR_MESSAGE % 197 (key, type(self.relay_device), 198 self.relay_device.name, self._store)) 199 200 def __iter__(self): 201 return iter(self._store) 202 203 def __len__(self): 204 return len(self._store) 205 206 def __repr__(self): 207 return repr(self._store) 208