class DeviceRoutine(CustomThread): def __init__(self, relay: int, gpio:int, ip_address: str, port: int, time_period_ms:int, start_after_ms:int): super().__init__(n_cycles=1, time_period_ms=time_period_ms, start_after_ms=start_after_ms) print(f'Created instance of DeviceRoutine@relay: {relay}, gpio: {gpio}, ip: {ip_address}, port: {port}') self._relay = relay self._gpio = gpio self._ip_address = ip_address self._port = port self._gpio_manager = None # [GpioManager] # State Machine self._routine_fun_dict = { DeviceRoutineStateEnum.DR_STATE_INIT: self._init_state_manager, DeviceRoutineStateEnum.DR_STATE_POLL_DEVICE: self._poll_device_state_manager, DeviceRoutineStateEnum.DR_STATE_DEVICE_DISCONNECTED: self._device_disconnected_state_manager, DeviceRoutineStateEnum.DR_STATE_WAIT_FOR_RECONNECTION: self._wait_for_reconnection_state_manager, DeviceRoutineStateEnum.DR_STATE_STOP: self._stop_state_manager, DeviceRoutineStateEnum.DR_STATE_EXIT: self._exit_state_manager } self._routine_state = { "current" : None, #[MainAppStateEnum] "last" : None #[MainAppStateEnum] } self._tcpip_client_server = None #[TcpIpClientServer] self._device_polling_timer = None #[Timer] --> poll the device to get if alive or not self._device_alive_timeout_timer = None #[Timer] --> if timer elapsed the device is disconnected self._wait_for_reconnection_timeout_timer = None #[Timer] --> if the device is disconnected for a long time exit from test self._reset_timer = None #[Timer] --> if the timer elapsed switch-on relay self._reset_time_min = None #[int] self._cycle_counter = None #[int] # Initialize all self._init_class() return # ---------------------------------------------------------------- # # ----------------------- Private Methods ------------------------ # # ---------------------------------------------------------------- # def _init_class(self): """ """ # State Machine self._routine_state["current"] = DeviceRoutineStateEnum.DR_STATE_INIT self._routine_state["last"] = DeviceRoutineStateEnum.DR_STATE_INIT # TcpIp self._tcpip_client_server = TcpIpClientServer(loop =True, time_period_ms =TCPIP_SERVER_TIME_PERIOD_MS, start_after_ms =0, socket_port =self._port) # Relay Manager self._gpio_manager = GpioManager.get_instance() # Cycle Counter self._cycle_counter = 0 pass # --------- State Machine --------- # def _store_last_state(self): # Store last state self._routine_state["last"] = self._routine_state["current"] return def _go_to_next_state(self, state): """""" # Store last state self._store_last_state() # Go to next state self._routine_state["current"] = state return def _init_state_manager(self): # Go to wait connection state print (f'[{self._timestamp()}] [{self._ip_address}] Init state') #Start tcpipserver self._tcpip_client_server.start() self._device_polling_timer = Timer() self._device_alive_timeout_timer = Timer() self._wait_for_reconnection_timeout_timer = Timer() self._reset_timer = Timer() # Go to next state self._go_to_next_state(DeviceRoutineStateEnum.DR_STATE_POLL_DEVICE) return def _poll_device_state_manager(self): if (self._routine_state["current"] == DeviceRoutineStateEnum.DR_STATE_POLL_DEVICE and self._routine_state["last"] != DeviceRoutineStateEnum.DR_STATE_POLL_DEVICE): # Increment cycle counter self._cycle_counter +=1 self._device_polling_timer.start() self._device_alive_timeout_timer.start() # Store last state self._store_last_state() else: if self._device_polling_timer.elapsed_time_s(1) >= DEVICE_POLLING_TIME_PERIOD_S: self._tcpip_client_server.send_message_to(ip_address = self._ip_address, port = self._port, message = f'{cc.ALIVE}?') self._device_polling_timer.reset() if self._reset_time_min is None: self._tcpip_client_server.send_message_to(ip_address=self._ip_address, port=self._port, message=f'{cc.RESET_TIME}?') if self._tcpip_client_server.is_data_available(): ip, port, msg = self._tcpip_client_server.read() # It's a query if msg.find("?") > 0: header, body = msg.split("?") if header == cc.RELAY_OFF: print (f'[{self._timestamp()}] [{self._ip_address}] [Channel {self._relay}] relay off') self._gpio_manager.relay_off(self._relay) self._tcpip_client_server.send_message_to(ip_address=self._ip_address, port=self._port, message=f'{cc.RELAY_OFF}:{cc.YES}') elif header == cc.RELAY_ON: print (f'[{self._timestamp()}] [{self._ip_address}] [Channel {self._relay}] relay on') self._gpio_manager.relay_on(self._relay) self._tcpip_client_server.send_message_to(ip_address=self._ip_address, port=self._port, message=f'{cc.RELAY_ON}:{cc.YES}') elif header == cc.STOP_TEST: self._tcpip_client_server.send_message_to(ip_address=self._ip_address, port=self._port, message=f'{cc.STOP_TEST}:{cc.YES}') self._go_to_next_state(DeviceRoutineStateEnum.DR_STATE_STOP) elif msg.find(":") > 0: header, body = msg.split(":") if header == cc.ALIVE and body == cc.YES: print (f'[{self._timestamp()}] [{self._ip_address}] [Channel {self._relay}] [Cycle {self._cycle_counter}] alive') #Device is alive, reset "alive" timer self._device_alive_timeout_timer.reset() elif header == f'{cc.RESET_TIME}': # Set reset time with the value provided by device if self._reset_time_min == None: try: self._reset_time_min = int(body) # print (f'[{self._timestamp()}] reset time set to {self._reset_time_min} min.') except: pass # Device alive timeout timer elapsed: the device is off if self._device_alive_timeout_timer.elapsed_time_s(1) > DEVICE_ALIVE_TIMEOUT_S: print (f'[{self._timestamp()}] [{self._ip_address}] [Channel {self._relay}] not alive') # Stop timers self._device_polling_timer.stop() self._device_alive_timeout_timer.stop() #Go to device disconnected state self._go_to_next_state(DeviceRoutineStateEnum.DR_STATE_DEVICE_DISCONNECTED) return def _device_disconnected_state_manager(self): if (self._routine_state["current"] == DeviceRoutineStateEnum.DR_STATE_DEVICE_DISCONNECTED and self._routine_state["last"] != DeviceRoutineStateEnum.DR_STATE_DEVICE_DISCONNECTED): print (f'[{self._timestamp()}] [{self._ip_address}] [Channel {self._relay}] device disconnected state') # If the device hasn't provide the default value for reset, use a default. if self._reset_time_min is None: self._reset_time_min = DEFAULT_RESET_TIME_MIN # Start device reset timer self._reset_timer.start() # Store last state self._store_last_state() else: if self._reset_timer.elapsed_time_min(1) >= self._reset_time_min: print (f'[{self._timestamp()}] [{self._ip_address}] [Channel {self._relay}] reset time ({self._reset_timer.elapsed_time_min(1)} min.) elapsed') #Relay on print(f'\n[{self._timestamp()}] [{self._ip_address}] [Channel {self._relay}] relay on') self._gpio_manager.relay_on(self._relay) self._reset_timer.stop() self._go_to_next_state(DeviceRoutineStateEnum.DR_STATE_WAIT_FOR_RECONNECTION) return def _wait_for_reconnection_state_manager(self): if (self._routine_state["current"] == DeviceRoutineStateEnum.DR_STATE_WAIT_FOR_RECONNECTION and self._routine_state["last"] != DeviceRoutineStateEnum.DR_STATE_WAIT_FOR_RECONNECTION): print (f'[{self._timestamp()}] [{self._ip_address}] [Channel {self._relay}] wait for reconnection state') # Start device disconnected timeout and polling timer self._wait_for_reconnection_timeout_timer.start() self._device_polling_timer.start() # Store last state self._store_last_state() else: if self._device_polling_timer.elapsed_time_s(1) >= DEVICE_DISC_POLLING_TIME_PERIOD_S: self._tcpip_client_server.send_message_to(ip_address=self._ip_address, port=self._port, message=f'{cc.ALIVE}?') self._device_polling_timer.reset() if (self._wait_for_reconnection_timeout_timer.timer_status() == TimerStatusEnum.TS_START and self._wait_for_reconnection_timeout_timer.elapsed_time_min(1) >= WAIT_FOR_RECONNECTION_TIMEOUT_MIN): print (f'[{self._timestamp()}] [{self._ip_address}] [Channel {self._relay}] reconnection timeout ({self._reset_timer.elapsed_time_min(1)} min.) expired') # Stop timer self._device_polling_timer.stop() self._wait_for_reconnection_timeout_timer.stop() # Device off for too much time. Exit! self._go_to_next_state(DeviceRoutineStateEnum.DR_STATE_STOP) if self._tcpip_client_server.is_data_available(): ip, port, msg = self._tcpip_client_server.read() if msg == f'{cc.ALIVE}:{cc.YES}': print (f'[{self._timestamp()}] [{self._ip_address}] [Channel {self._relay}] alive: elapsed ({self._wait_for_reconnection_timeout_timer.elapsed_time_min(1)} min.)') # Stop timers self._device_polling_timer.stop() self._wait_for_reconnection_timeout_timer.stop() # Go to poll device self._go_to_next_state(DeviceRoutineStateEnum.DR_STATE_POLL_DEVICE) return def _stop_state_manager(self): if (self._routine_state["current"] == DeviceRoutineStateEnum.DR_STATE_STOP and self._routine_state["last"] != DeviceRoutineStateEnum.DR_STATE_STOP): print (f'[{self._timestamp()}] [{self._ip_address}] [Channel {self._relay}] stop state') # Stop tcpip client-server self._tcpip_client_server.stop() # Relay on self._gpio_manager.relay_on(self._relay) # Store last state self._store_last_state() else: # Go to exit state self._go_to_next_state(DeviceRoutineStateEnum.DR_STATE_EXIT) pass return def _exit_state_manager(self): print (f'[{self._timestamp()}] [{self._ip_address}] [Channel {self._relay}] exit state') self._store_last_state() return def _main_app_state_machine_manager(self): # Get Function from Dictionary fun = self._routine_fun_dict.get(self._routine_state["current"]) # Execute function fun() return def _timestamp(self) -> str: return datetime.now().strftime("%m/%d/%Y, %H:%M:%S") # ---------------------------------------------------------------- # # ------------------------ Public Methods ------------------------ # # ---------------------------------------------------------------- # def runnable(self): """ override runnable of its superclass """ # print("*****************************************************") # print("*** Start State Machine ***") # print("*****************************************************") exit_condition = False while not exit_condition: # Execute State Machine self._main_app_state_machine_manager() exit_condition = (self._routine_state["current"] == DeviceRoutineStateEnum.DR_STATE_EXIT and self._routine_state["last"] == DeviceRoutineStateEnum.DR_STATE_EXIT) time.sleep(STATE_MACHINE_TIME_PERIOD_MS/1000) # print("*****************************************************") # print("*** End of State Machine ***") # print("*****************************************************") return @property def relay(self): return self._relay @property def gpio(self): return self._gpio @property def ip_address(self): return self._ip_address @property def port(self): return self._port
def bytes_available_rx(self): """ Check available bytes of data """ self._bytes_available_rx = self.inWaiting() return self._bytes_available_rx if __name__ == '__main__': import time from datetime import datetime from Timer import Timer test_serial = CustomSerial(port="COM19", baudrate=115200) test_serial.serial_init() #test_serial.serial_write("init") #test_serial.serial_write('read_tempiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii') counter = 0 tm = Timer() tm.start() while True: test_serial.serial_write('read_temp\r') while not test_serial.bytes_available_rx: pass x = test_serial.serial_read().strip("\r\n") print((counter, tm.elapsed_time_s(2), x)) counter = counter + 1 time.sleep(0.1)