def _init(self): """Initialize the module. """ self.__logger = get_logger(__name__) self.__logger.info("Starting up the {} {}".format(self.name, self.__identifier)) # Create thermal mode. self.__thermal_mode = StateMachine(ThermalMode.NONE) self.__thermal_mode.on_change(self.__thermal_mode_on_change) # Create update timer. self.__update_timer = Timer(5) # Stop timer. self.__stop_timer = Timer(10) # Create temperature processor. self.__temp_proc = TemperatureProcessor() # Create temperature queue. self.__queue_temperatures = deque([], maxlen=20) # Create registers callbacks. self.__init_registers() # Shutting down all the devices. self.__set_thermal_force(0)
def __init__(self, **config): """Constructor """ super().__init__(config) self._vendor = "Flowx" self._model = "FLX-05F" # Create logger. self.__logger = get_logger(__name__) self.__move_timer = Timer() # 13 seconds absolute time to react the valve. # Number is measured by emperic way. self.__limit_timer = Timer(13) self.__calibration_state = StateMachine(CalibrationState.NONE) if "output_cw" in config: self.__output_cw = config["output_cw"] if "output_ccw" in config: self.__output_ccw = config["output_ccw"] if "limit_cw" in config: self.__limit_cw = config["limit_cw"] if "limit_ccw" in config: self.__limit_ccw = config["limit_ccw"] if "close_on_shutdown" in config: self.__close_on_shutdown = config["close_on_shutdown"]
def __init__(self, config): """Convector Args: config (dict): Configuration """ super().__init__(config) self._state = StateMachine(ValveState.NONE)
def __init__(self, config): super().__init__(config) self._state = StateMachine(CardReaderState.NONE) if "port_name" in config: self._port_name = config["port_name"] if "serial_number" in config: self._serial_number = config["serial_number"]
def __init__(self, **config): """Constructor""" super().__init__(config) self._vendor = "POLYGONTeam" self._model = "Blind" self.__logger = get_logger(__name__) self.__blinds_state = StateMachine(BlindsState.Wait) self.__move_timer = Timer() self.__calibration_state = StateMachine(CalibrationState.NONE) options = self._config["options"] if "output_cw" in options: self.__output_cw = options["output_cw"] if "output_ccw" in options: self.__output_ccw = options["output_ccw"] if "feedback" in options: self.__input_fb = options["feedback"] if "feedback_tresh" in options: self.__feedback_treshold = options["feedback_tresh"] if "deg_per_sec" in options: self.__deg_per_sec = options["deg_per_sec"] # Set the feedback type. if self.__input_fb.startswith("AI"): self.__feedback_type = FeedbackType.Analog elif self.__input_fb.startswith("DI"): self.__feedback_type = FeedbackType.Digital else: GlobalErrorHandler.log_missing_resource(self.__logger, "{}: feedback not set correct or incomparable. Check feedback settings.".format(self.name))
def __init_erp(self): """Setup the ERP. """ # Take ERP info from settings. erp_host = self.__app_settings.get_erp_service["host"] erp_timeout = self.__app_settings.get_erp_service["timeout"] # Create ERP. self.__erp = bgERP(host=erp_host, timeout=erp_timeout) # Set callbacks. self.__erp.set_registers_cb( get_cb=self.__erp_get_registers, set_cb=self.__erp_set_registers) # Set the ERP update timer. self.__erp_service_update_timer = Timer(self.__erp_service_update_rate) # Set zone state machine. self.__erp_state_machine = StateMachine() self.__erp_state_machine.on_change(self.__on_erp_state_change_cb) self.__erp_state_machine.set_state(ERPState.NONE)
class FLX05F(BaseValve): """Hydro Valve. Model: FLX-05F""" #region Attributes __logger = None """Logger """ __move_timer = None """Move timer. """ __calibration_state = None """Calibration state machine. """ __output_cw = verbal_const.OFF """Valve output CW GPIO. """ __output_ccw = verbal_const.OFF """Valve output CCW GPIO. """ __limit_cw = verbal_const.OFF """Limit switch for CW direction. """ __limit_ccw = verbal_const.OFF """Limit switch for CCW direction. """ __t0 = 0 """T0 moment. """ __t1 = 0 """T1 moment. """ __dt = 0 """Delta time consumed for one full open and close cycle. """ __limit_timer = None """Limit timer. """ __close_on_shutdown = True """Close on shutdown flag. """ #endregion #region Constructor / Destructor def __init__(self, **config): """Constructor """ super().__init__(config) self._vendor = "Flowx" self._model = "FLX-05F" # Create logger. self.__logger = get_logger(__name__) self.__move_timer = Timer() # 13 seconds absolute time to react the valve. # Number is measured by emperic way. self.__limit_timer = Timer(13) self.__calibration_state = StateMachine(CalibrationState.NONE) if "output_cw" in config: self.__output_cw = config["output_cw"] if "output_ccw" in config: self.__output_ccw = config["output_ccw"] if "limit_cw" in config: self.__limit_cw = config["limit_cw"] if "limit_ccw" in config: self.__limit_ccw = config["limit_ccw"] if "close_on_shutdown" in config: self.__close_on_shutdown = config["close_on_shutdown"] def __del__(self): """Destructor """ super().__del__() if self.__logger is not None: del self.__logger #endregion #region Properties @property def is_calibrating(self): """Return is it in calibration state. Returns: bool: True if calibrating, else false. """ return self._state.is_state(ValveState.Calibrate) #endregion #region Private Methods def __to_time(self, position): """Convert position in to time. Args: position (float): Position in %. Returns: float: Time in seconds. """ return position * (self.__dt / 100.0) #endregion #region Private Messatages (PLC I/O) def __stop(self): """Stop the valve motor. """ if self._controller.is_valid_gpio(self.__output_cw): self._controller.digital_write(self.__output_cw, 0) if self._controller.is_valid_gpio(self.__output_ccw): self._controller.digital_write(self.__output_ccw, 0) def __close_valve(self): """Turn to CW direction. """ if self._controller.is_valid_gpio(self.__output_cw): self._controller.digital_write(self.__output_cw, 1) def __open_valve(self): """Turn to CCW direction. """ if self._controller.is_valid_gpio(self.__output_ccw): self._controller.digital_write(self.__output_ccw, 1) def __get_open_limit(self): state = False if self._controller.is_valid_gpio(self.__limit_ccw): state = self._controller.digital_read(self.__limit_ccw) else: state = True return state def __get_close_limit(self): state = False if self._controller.is_valid_gpio(self.__limit_cw): state = self._controller.digital_read(self.__limit_cw) else: state = True return state #endregion #region Public Methods def init(self): """Initialize the device. """ self.target_position = 0 while self.current_position != self.target_position: self.update() self.__logger.debug("Starting up the: {}".format(self.name)) def shutdown(self): """Shutdown the valve. """ if self.__close_on_shutdown: self.__close_valve() while self.__get_close_limit() == False: self.update() self.__stop() self.__logger.debug("Shutdown the: {}".format(self.name)) def update(self): """Update the valve state. """ if self._state.is_state(ValveState.Prepare): delta_pos = self.target_position - self.current_position if delta_pos == 0: self.__stop() self._state.set_state(ValveState.Wait) return time_to_move = self.__to_time(abs(delta_pos)) self.__logger.debug("Time: {}".format(time_to_move)) self.__move_timer.expiration_time = time_to_move self.__move_timer.update_last_time() if delta_pos > 0: self.__open_valve() elif delta_pos < 0: self.__close_valve() self._state.set_state(ValveState.Execute) elif self._state.is_state(ValveState.Execute): self.__move_timer.update() if self.__move_timer.expired: self.__move_timer.clear() self.__stop() self._current_position = self.target_position self._state.set_state(ValveState.Wait) cw_limit_state = False # self.__get_close_limit() ccw_limit_state = False # self.__get_open_limit() if cw_limit_state or ccw_limit_state: self.__stop() GlobalErrorHandler.log_hardware_limit( self.__logger, "{} has raised end position.".format(self.name)) self._current_position = self.target_position self._state.set_state(ValveState.Wait) elif self._state.is_state(ValveState.Calibrate): # Wait to start. if self.__calibration_state.is_state(CalibrationState.NONE): self.__calibration_state.set_state(CalibrationState.OpenValve) self.__stop() # Open the valve. if self.__calibration_state.is_state(CalibrationState.OpenValve): self.__stop() self.__open_valve() self.__calibration_state.set_state(CalibrationState.EnsureOpen) self.__limit_timer.update_last_time() # Wait until it si open at 100%. if self.__calibration_state.is_state(CalibrationState.EnsureOpen): # Get CCW limit switch state. ccw_limit_state = self.__get_open_limit() if ccw_limit_state: self.__t0 = time.time() self.__calibration_state.set_state( CalibrationState.CloseValve) # Prevent with timer, # if the valve is not reacting properly. self.__limit_timer.update() if self.__limit_timer.expired: self.__limit_timer.clear() self.__calibration_state.set_state(CalibrationState.Error) # Close the valve. if self.__calibration_state.is_state(CalibrationState.CloseValve): self.__stop() self.__close_valve() self.__calibration_state.set_state( CalibrationState.EnsureClose) self.__limit_timer.update_last_time() # Wait until it si open at 100%. if self.__calibration_state.is_state(CalibrationState.EnsureClose): # Get CW limit switch state. cw_limit_state = self.__get_close_limit() if cw_limit_state: self.__t1 = time.time() self.__calibration_state.set_state( CalibrationState.YouDoTheMath) # Prevent with timer, # if the valve is not reacting properly. self.__limit_timer.update() if self.__limit_timer.expired: self.__limit_timer.clear() self.__calibration_state.set_state(CalibrationState.Error) # Make calculations. if self.__calibration_state.is_state( CalibrationState.YouDoTheMath): self.__stop() self.__dt = self.__t1 - self.__t0 self._state.set_state(ValveState.Wait) # Close the valve. if self.__calibration_state.is_state(CalibrationState.Error): GlobalErrorHandler.log_hardware_malfunction( self.__logger, "The valve {} can not calibrated.".format(self.name)) self._state.set_state(ValveState.Wait) # - Close the valve. # - Ensure that the valve is closed 0deg. # - Record the time in T0. # - Open the valve. # - Ensure the the valve is opened 90deg. # - Record the time in T1. # - Store (T0 - T1) in dT # - Use dT and end position contacts to ensure that the valve is closed and opened. def calibrate(self): self._state.set_state(ValveState.Calibrate) def update_sync(self): """Update synchronious. """ while self.current_position != self.target_position: self.update()
class Zone(BasePlugin): """Air conditioner control logic. """ #region Attributes __logger = None """Logger""" __identifier = 0 """Number identifier.""" __temp_proc = None """Temperature processor.""" __air_temp_upper_dev = None """Air thermometer upper.""" __air_temp_cent_dev = None """Air thermometer central.""" __air_temp_lower_dev = None """Air thermometer lower.""" __queue_temperatures = None """Queue of the temperatures.""" __convector_dev = None """Convector device.""" __loop1_temp_dev = None """Loop 2 thermometer.""" __loop1_valve_dev = None """Loop 1 valve device.""" __loop1_flowmeter = None """Loop 1 flow metter.""" __loop1_leak_test = None """Loop 1 leak test.""" __loop2_temp_dev = None """Loop 2 thermometer.""" __loop2_valve_dev = None """Loop 2 valve device.""" __loop2_flowmeter = None """Loop 2 flow metter.""" __loop2_leak_teat = None """Loop 2 leak test.""" __update_timer = None """Main process update timer.""" __stop_timer = None """Stop timer.""" __thermal_mode = None """Thermal mode of the HVAC.""" __thermal_force_limit = 0 """Limit thermal force.""" __delta_time = 1 """Конфигурационен параметър, показващ за какво време назад се отчита изменението на температурата. Limits: (1 - 3)""" __adjust_temp = 0 """Зададено отклонение от температурата (задава се от дисплея до вратата или през мобилен телефон, вързан в локалната мрежа) Limits: (-2.5 : 2.5)""" __goal_building_temp = 0 """Целева температура на сградата. (подава се от централния сървър) Limits: (18-26)""" __delta_temp = 0 """Изменението на температурата от последните минути. Limits: (-3 : 3)""" __thermal_force = 0 """Каква топлинна сила трябва да приложим към системата (-100% означава максимално да охлаждаме, +100% - максимално да отопляваме)""" __stop_flag = False """HVAC Stop flag.""" __window_closed_input = verbal_const.OFF """Window closed sensor input.""" #endregion #region Constructor / Destructor def __init__(self, **config): """Constructor""" super().__init__(config) if "identifier" in config: self.__identifier = config["identifier"] def __del__(self): """Destructor""" if self.__loop1_temp_dev is not None: del self.__loop1_temp_dev if self.__loop1_valve_dev is not None: del self.__loop1_valve_dev if self.__loop1_flowmeter is not None: del self.__loop1_flowmeter if self.__loop2_temp_dev is not None: del self.__loop2_temp_dev if self.__loop2_valve_dev is not None: del self.__loop2_valve_dev if self.__loop2_flowmeter is not None: del self.__loop2_flowmeter if self.__thermal_mode is not None: del self.__thermal_mode if self.__update_timer is not None: del self.__update_timer if self.__stop_timer is not None: del self.__stop_timer if self.__loop1_leak_test is not None: del self.__loop1_leak_test if self.__loop2_leak_teat is not None: del self.__loop2_leak_teat if self.__queue_temperatures is not None: del self.__queue_temperatures super().__del__() if self.__logger is not None: del self.__logger #endregion #region Properties @property def temperature(self): """Measure temperature from the sensors. Средна температура на стаята. (измерва се от датчиците) Limits: (0-40) Returns ------- float Actual temperature in the room. """ # return the temperature. return self.__temp_proc.value #endregion #region Private Methods (Registers Parameters) def __update_rate_cb(self, register): # Check data type. if not (register.data_type == "float" or register.data_type == "int"): GlobalErrorHandler.log_bad_register_data_type(self.__logger, register) return # Check value. if register.value < 0: GlobalErrorHandler.log_bad_register_value(self.__logger, register) return if self.__update_timer.expiration_time != register.value: self.__update_timer.expiration_time = register.value def __delta_time_cb(self, register): # Check data type. if not (register.data_type == "float" or register.data_type == "int"): GlobalErrorHandler.log_bad_register_data_type(self.__logger, register) return # Check value. if register.value < 0: GlobalErrorHandler.log_bad_register_value(self.__logger, register) return if self.__delta_time != register.value: self.__delta_time = register.value def __thermal_mode_cb(self, register): # Check data type. if not register.data_type == "int": GlobalErrorHandler.log_bad_register_data_type(self.__logger, register) return mode = ThermalMode(register.value) self.__thermal_mode.set_state(mode) def __thermal_force_limit_cb(self, register): # Check data type. if not (register.data_type == "float" or register.data_type == "int"): GlobalErrorHandler.log_bad_register_data_type(self.__logger, register) return # Check value. if register.value < 0: GlobalErrorHandler.log_bad_register_value(self.__logger, register) return if register.value > 100: GlobalErrorHandler.log_bad_register_value(self.__logger, register) return self.__thermal_force_limit = register.value def __adjust_temp_cb(self, register): # Check data type. if not (register.data_type == "float" or register.data_type == "int"): GlobalErrorHandler.log_bad_register_data_type(self.__logger, register) return if self.__adjust_temp == register.value: return # @see https://experta.bg/L/S/122745/m/Fwntindd min_temp = 2.5 max_temp = -2.5 min_temp_reg = self._registers.by_name("{}.temp_{}.min".format(self.key, self.__identifier)) if min_temp_reg is not None: min_temp = min_temp_reg.value max_temp_reg = self._registers.by_name("{}.temp_{}.max".format(self.key, self.__identifier)) if max_temp_reg is not None: max_temp = max_temp_reg.value actual_temp = register.value if actual_temp < min_temp: actual_temp = min_temp if actual_temp > max_temp: actual_temp = max_temp self.__adjust_temp = actual_temp def __goal_building_temp_cb(self, register): # Check data type. if not (register.data_type == "float" or register.data_type == "int"): GlobalErrorHandler.log_bad_register_data_type(self.__logger, register) return # @see https://experta.bg/L/S/122745/m/Fwntindd min_temp = 18 max_temp = 26 actual_temp = register.value if actual_temp < min_temp: actual_temp = min_temp if actual_temp > max_temp: actual_temp = max_temp if self.__goal_building_temp != actual_temp: self.__goal_building_temp = actual_temp def __window_closed_input_cb(self, register): # Check data type. if not register.data_type == "str": GlobalErrorHandler.log_bad_register_data_type(self.__logger, register) return self.__window_closed_input = register.value #endregion #region Private Methods (Registers Thermometers) def __air_temp_cent_settings_cb(self, register): # Check data type. if not register.data_type == "json": GlobalErrorHandler.log_bad_register_value(self.__logger, register) return if register.value != {} and self.__air_temp_cent_dev is None: self.__air_temp_cent_dev = ThermometersFactory.create( name="Air temperature center", controller=self._controller, vendor=register.value['vendor'], model=register.value['model'], options=register.value['options']) if self.__air_temp_cent_dev is not None: self.__air_temp_cent_dev.init() self.__temp_proc.add(self.__air_temp_cent_dev) elif register.value == {} and self.__air_temp_cent_dev is not None: self.__temp_proc.add(self.__air_temp_cent_dev) self.__air_temp_cent_dev.shutdown() del self.__air_temp_cent_dev def __air_temp_lower_settings_cb(self, register): # Check data type. if not register.data_type == "json": GlobalErrorHandler.log_bad_register_value(self.__logger, register) return if register.value != {} and self.__air_temp_lower_dev is None: self.__air_temp_lower_dev = ThermometersFactory.create( controller=self._controller, name="Air temperature lower", vendor=register.value['vendor'], model=register.value['model'], options=register.value['options']) if self.__air_temp_lower_dev is not None: self.__air_temp_lower_dev.init() self.__temp_proc.add(self.__air_temp_lower_dev) elif register.value == {} and self.__air_temp_lower_dev is not None: self.__temp_proc.remove(self.__air_temp_lower_dev) self.__air_temp_lower_dev.shutdown() del self.__air_temp_lower_dev def __air_temp_upper_settings_cb(self, register): # Check data type. if not register.data_type == "json": GlobalErrorHandler.log_bad_register_value(self.__logger, register) return if register.value != {} and self.__air_temp_upper_dev is None: self.__air_temp_upper_dev = ThermometersFactory.create( controller=self._controller, name="Air temperature upper", vendor=register.value['vendor'], model=register.value['model'], options=register.value['options']) if self.__air_temp_upper_dev is not None: self.__air_temp_upper_dev.init() self.__temp_proc.add(self.__air_temp_upper_dev) elif register.value == {} and self.__air_temp_upper_dev is not None: self.__temp_proc.remove(self.__air_temp_upper_dev) self.__air_temp_upper_dev.shutdown() del self.__air_temp_upper_dev #endregion #region Private Methods (Registers Convector) def __convector_settings_cb(self, register): # Check data type. if not register.data_type == "json": GlobalErrorHandler.log_bad_register_data_type(self.__logger, register) return if register.value != {} and self.__convector_dev is None: self.__convector_dev = ConvectorsFactory.create( name="Convector {}".format(self.__identifier), controller=self._controller, vendor=register.value['vendor'], model=register.value['model'], options=register.value['options']) if self.__convector_dev is not None: self.__convector_dev.init() elif register.value == {} and self.__convector_dev is not None: self.__convector_dev.shutdown() del self.__convector_dev #endregion #region Private Methods (Registers Loop 1) def __loop1_flowmeter_settings_cb(self, register): # Check data type. if not register.data_type == "json": GlobalErrorHandler.log_bad_register_data_type(self.__logger, register) return if register.value != {} and self.__loop1_flowmeter is None: self.__loop1_flowmeter = FlowmetersFactory.create( name="Loop 1 flowmeter", controller=self._controller, vendor=register.value['vendor'], model=register.value['model'], options=register.value['options']) if self.__loop1_flowmeter is not None: self.__loop1_flowmeter.init() # 20 seconds is time for leak testing. self.__loop1_leak_test = LeakTest(self.__loop1_flowmeter, 20) self.__loop1_leak_test.on_result(self.__loop1_leaktest_result) elif register.value == {} and self.__loop1_flowmeter is not None: self.__loop1_flowmeter.shutdown() del self.__loop1_flowmeter del self.__loop1_leak_test def __loop1_temp_settings_cb(self, register): # Check data type. if not register.data_type == "json": GlobalErrorHandler.log_bad_register_data_type(self.__logger, register) return if register.value != {} and self.__loop1_temp_dev is None: self.__loop1_temp_dev = ThermometersFactory.create( controller=self._controller, name="Loop 1 temperature", vendor=register.value['vendor'], model=register.value['model'], options=register.value['options']) if self.__loop1_temp_dev is not None: self.__loop1_temp_dev.init() elif register.value == {} and self.__loop1_temp_dev is not None: self.__loop1_temp_dev.shutdown() del self.__loop1_temp_dev def __loop1_valve_settings_cb(self, register): # Check data type. if not register.data_type == "json": GlobalErrorHandler.log_bad_register_data_type(self.__logger, register) return if register.value != {} and self.__loop1_valve_dev is None: self.__loop1_valve_dev = ValveFactory.create( name="Loop 1 valve", controller=self._controller, vendor=register.value['vendor'], model=register.value['model'], options=register.value['options']) if self.__loop1_valve_dev is not None: self.__loop1_valve_dev.init() elif register.value == {} and self.__loop1_valve_dev is not None: self.__loop1_valve_dev.shutdown() del self.__loop1_valve_dev #endregion #region Private Methods (Registers Loop 2) def __loop2_flowmeter_settings_cb(self, register): # Check data type. if not register.data_type == "json": GlobalErrorHandler.log_bad_register_data_type(self.__logger, register) return if register.value != {} and self.__loop2_flowmeter is None: self.__loop2_flowmeter = FlowmetersFactory.create( name="Loop 2 flowmeter", controller=self._controller, vendor=register.value['vendor'], model=register.value['model'], options=register.value['options']) if self.__loop2_flowmeter is not None: self.__loop2_flowmeter.init() self.__loop2_leak_teat = LeakTest(self.__loop2_flowmeter, 20) self.__loop2_leak_teat.on_result(self.__loop2_leaktest_result) elif register.value == {} and self.__loop2_flowmeter is not None: self.__loop2_flowmeter.shutdown() del self.__loop2_flowmeter del self.__loop2_leak_teat def __loop2_temp_settings_cb(self, register): # Check data type. if not register.data_type == "json": GlobalErrorHandler.log_bad_register_data_type(self.__logger, register) return if register.value != {} and self.__loop2_temp_dev is None: self.__loop2_temp_dev = ThermometersFactory.create( controller=self._controller, name="Loop 2 temperature", vendor=register.value['vendor'], model=register.value['model'], options=register.value['options']) if self.__loop2_temp_dev is not None: self.__loop2_temp_dev.init() elif register.value == {} and self.__loop2_temp_dev is not None: self.__loop2_temp_dev.shutdown() del self.__loop2_temp_dev def __loop2_valve_settings_cb(self, register): # Check data type. if not register.data_type == "json": GlobalErrorHandler.log_bad_register_data_type(self.__logger, register) return if register.value != {} and self.__loop2_valve_dev is None: self.__loop2_valve_dev = ValveFactory.create( name="Loop 2 valve", controller=self._controller, vendor=register.value['vendor'], model=register.value['model'], options=register.value['options']) if self.__loop2_valve_dev is not None: self.__loop2_valve_dev.init() elif register.value == {} and self.__loop2_valve_dev is not None: self.__loop2_valve_dev.shutdown() del self.__loop2_valve_dev #endregion #region Private Methods (Registers envm) def __envm_energy_cb(self, register): # Check data type. if not ((register.data_type == "int") or (register.data_type == "float")): GlobalErrorHandler.log_bad_register_data_type(self.__logger, register) return # TODO: Get energy mode for the building. pass #endregion #region Private Methods (Ventilation Interface) def __set_ventilation(self, value): # Set the ventilation. self._registers.write("vent.hvac_setpoint_{}".format(self.__identifier), value) #endregion #region Private Methods (Registers Interface) def __init_registers(self): """Initialize the registers callbacks. """ # Air temperatures. air_temp_cent_settings = self._registers.by_name("{}.air_temp_cent_{}.settings".format(self.key, self.__identifier)) if air_temp_cent_settings is not None: air_temp_cent_settings.update_handlers = self.__air_temp_cent_settings_cb air_temp_cent_settings.update() air_temp_lower_settings = self._registers.by_name("{}.air_temp_lower_{}.settings".format(self.key, self.__identifier)) if air_temp_lower_settings is not None: air_temp_lower_settings.update_handlers = self.__air_temp_lower_settings_cb air_temp_lower_settings.update() air_temp_upper_settings = self._registers.by_name("{}.air_temp_upper_{}.settings".format(self.key, self.__identifier)) if air_temp_upper_settings is not None: air_temp_upper_settings.update_handlers = self.__air_temp_upper_settings_cb air_temp_upper_settings.update() # Convector convector_enable = self._registers.by_name("{}.convector_{}.settings".format(self.key, self.__identifier)) if convector_enable is not None: convector_enable.update_handlers = self.__convector_settings_cb convector_enable.update() # Loop 1 loop1_flowmeter = self._registers.by_name("{}.loop1_{}.flowmeter.settings".format(self.key, self.__identifier)) if loop1_flowmeter is not None: loop1_flowmeter.update_handlers = self.__loop1_flowmeter_settings_cb loop1_flowmeter.update() loop1_temp_settings = self._registers.by_name("{}.loop1_{}.temp.settings".format(self.key, self.__identifier)) if loop1_temp_settings is not None: loop1_temp_settings.update_handlers = self.__loop1_temp_settings_cb loop1_temp_settings.update() loop1_valve_enabled = self._registers.by_name("{}.loop1_{}.valve.settings".format(self.key, self.__identifier)) if loop1_valve_enabled is not None: loop1_valve_enabled.update_handlers = self.__loop1_valve_settings_cb loop1_valve_enabled.update() # Loop 2 loop2_flowmeter_settings = self._registers.by_name("{}.loop2_{}.flowmeter.settings".format(self.key, self.__identifier)) if loop2_flowmeter_settings is not None: loop2_flowmeter_settings.update_handlers = self.__loop2_flowmeter_settings_cb loop2_flowmeter_settings.update() loop2_temp_settings = self._registers.by_name("{}.loop2_{}.temp.settings".format(self.key, self.__identifier)) if loop2_temp_settings is not None: loop2_temp_settings.update_handlers = self.__loop2_temp_settings_cb loop2_temp_settings.update() loop2_valve_settings = self._registers.by_name("{}.loop2_{}.valve.settings".format(self.key, self.__identifier)) if loop2_valve_settings is not None: loop2_valve_settings.update_handlers = self.__loop2_valve_settings_cb loop2_valve_settings.update() # Create window closed sensor. window_closed_input = self._registers.by_name("{}.window_closed_{}.input".format("ac", self.__identifier)) if window_closed_input is not None: window_closed_input.update_handlers = self.__window_closed_input_cb window_closed_input.update() # Region parameters update_rate = self._registers.by_name("{}.update_rate_{}".format(self.key, self.__identifier)) if update_rate is not None: update_rate.update_handlers = self.__update_rate_cb update_rate.update() delta_time = self._registers.by_name("{}.delta_time_{}".format(self.key, self.__identifier)) if delta_time is not None: delta_time.update_handlers = self.__delta_time_cb delta_time.update() thermal_mode = self._registers.by_name("{}.thermal_mode_{}".format(self.key, self.__identifier)) if thermal_mode is not None: thermal_mode.update_handlers = self.__thermal_mode_cb thermal_mode.update() thermal_force_limit = self._registers.by_name("{}.thermal_force_limit_{}".format(self.key, self.__identifier)) if thermal_force_limit is not None: thermal_force_limit.update_handlers = self.__thermal_force_limit_cb thermal_force_limit.update() adjust_temp = self._registers.by_name("{}.adjust_temp_{}".format(self.key, self.__identifier)) if adjust_temp is not None: adjust_temp.update_handlers = self.__adjust_temp_cb adjust_temp.update() goal_building_temp = self._registers.by_name("{}.goal_building_temp".format(self.key)) if goal_building_temp is not None: goal_building_temp.update_handlers = self.__goal_building_temp_cb goal_building_temp.update() # Get the power mode of the building. envm_energy = self._registers.by_name("envm.energy") if envm_energy is not None: envm_energy.update_handlers = self.__envm_energy_cb envm_energy.update() def __update_thermometers_values(self): # 1. If thermometer is available, gets its value. air_temp_lower_value = 0 if self.__air_temp_lower_dev is not None: air_temp_lower_value = self.__air_temp_lower_dev.get_temp() # 1. If thermometer is available, gets its value. air_temp_cent_value = 0 if self.__air_temp_cent_dev is not None: air_temp_cent_value = self.__air_temp_cent_dev.get_temp() # 1. If thermometer is available, gets its value. air_temp_upper_value = 0 if self.__air_temp_upper_dev is not None: air_temp_upper_value = self.__air_temp_upper_dev.get_temp() # 1. If thermometer is available, gets its value. loop1_temp_value = 0 if self.__loop1_temp_dev is not None: loop1_temp_value = self.__loop1_temp_dev.get_temp() # 1. If thermometer is available, gets its value. loop2_temp_value = 0 if self.__loop2_temp_dev is not None: loop2_temp_value = self.__loop2_temp_dev.get_temp() # 2. If the folowing register is available then set ist value to the thermometers value. self._registers.write("{}.air_temp_lower_{}.value".format(self.key, self.__identifier), air_temp_lower_value) # 2. If the folowing register is available then set ist value to the thermometers value. self._registers.write("{}.air_temp_cent_{}.value".format(self.key, self.__identifier), air_temp_cent_value) # 2. If the folowing register is available then set ist value to the thermometers value. self._registers.write("{}.air_temp_upper_{}.value".format(self.key, self.__identifier), air_temp_upper_value) # 2. If the folowing register is available then set ist value to the thermometers value. self._registers.write("{}.loop1_{}.temp.value".format(self.key, self.__identifier), loop1_temp_value) # 2. If the folowing register is available then set ist value to the thermometers value. self._registers.write("{}.loop2_{}.temp.value".format(self.key, self.__identifier), loop2_temp_value) def __is_empty(self): value = False is_empty = self._registers.by_name("envm.is_empty") if is_empty is not None: value = is_empty.value return value def __get_down_limit_temp(self): # Request: Eml6419 value = 10 down_limit = self._registers.by_name("{}.loop1_{}.temp.down_limit".format(self.key, self.__identifier)) if down_limit is not None: down_limit_value = down_limit.value return value #endregion #region Private Methods (PLC) def __read_window_tamper(self): state = False if self._controller.is_valid_gpio(self.__window_closed_input): state = self._controller.digital_read(self.__window_closed_input) if self.__window_closed_input == verbal_const.OFF: state = True return state #endregion #region Private Methods (Leak tests) def __loop1_leaktest_result(self, leaked_liters): if leaked_liters > 0: self.__logger.error("Loop 1 leak detected liters: {}".format(leaked_liters)) def __loop2_leaktest_result(self, leaked_liters): if leaked_liters > 0: self.__logger.error("Loop 2 leak detected liters: {}".format(leaked_liters)) #endregion #region Private Methods def __is_hot_water(self): down_limit_value = self.__get_down_limit_temp() temperature = 0 if self.__loop1_temp_dev is not None: temperature = self.__loop1_temp_dev.get_temp() return temperature >= down_limit_value def __thermal_mode_on_change(self, machine): self.__logger.info("Thermal mode: {}".format(machine.get_state())) def __set_thermal_force(self, thermal_force): """ Apply thermal force to the devices. """ # 6. Ако модула на пределната термална сила е по-малък от модула на термалната сила, # тогава Термалата сила = Пределната термала сила if thermal_force > abs(self.__thermal_force_limit): thermal_force = self.__thermal_force_limit elif thermal_force < -abs(self.__thermal_force_limit): thermal_force = -abs(self.__thermal_force_limit) # 7. Лимитираме Термалната сила в интервала -100 : + 100: if thermal_force < -100: thermal_force = -100 if thermal_force > 100: thermal_force = 100 self.__logger.debug("Mode: {}; TForce: {:3.3f}"\ .format(self.__thermal_mode.get_state(), thermal_force)) if self.__thermal_mode.is_state(ThermalMode.ColdSeason): if thermal_force > 0: self.__loop1_valve_dev.target_position = 0 self.__loop2_valve_dev.target_position = 0 elif thermal_force <= 0: self.__loop1_valve_dev.target_position = 100 self.__loop2_valve_dev.target_position = 100 elif self.__thermal_mode.is_state(ThermalMode.TransisionSeason): if thermal_force < 0: self.__loop1_valve_dev.target_position = 100 self.__loop2_valve_dev.target_position = 0 elif thermal_force > 0: self.__loop1_valve_dev.target_position = 0 self.__loop2_valve_dev.target_position = 100 else: self.__loop1_valve_dev.target_position = 0 self.__loop2_valve_dev.target_position = 0 elif self.__thermal_mode.is_state(ThermalMode.WarmSeason): if thermal_force < 0: self.__loop1_valve_dev.target_position = 100 self.__loop2_valve_dev.target_position = 100 elif thermal_force > 0: self.__loop1_valve_dev.target_position = 0 self.__loop2_valve_dev.target_position = 0 # If thermal mode set properly apply thermal force if not self.__thermal_mode.is_state(ThermalMode.NONE): self.__set_ventilation(thermal_force) # Set convector fan. conv_tf = l_scale(thermal_force, [0, 100], [0, 3]) conv_tf = abs(conv_tf) conv_tf = int(conv_tf) self.__convector_dev.set_state(conv_tf) #endregion #region Protected Methods def _init(self): """Initialize the module. """ self.__logger = get_logger(__name__) self.__logger.info("Starting up the {} {}".format(self.name, self.__identifier)) # Create thermal mode. self.__thermal_mode = StateMachine(ThermalMode.NONE) self.__thermal_mode.on_change(self.__thermal_mode_on_change) # Create update timer. self.__update_timer = Timer(5) # Stop timer. self.__stop_timer = Timer(10) # Create temperature processor. self.__temp_proc = TemperatureProcessor() # Create temperature queue. self.__queue_temperatures = deque([], maxlen=20) # Create registers callbacks. self.__init_registers() # Shutting down all the devices. self.__set_thermal_force(0) def _update(self): """ Update cycle. """ # Update thermometres values. self.__update_thermometers_values() # Update occupation flags. is_empty = self.__is_empty() # If the window is opened, just turn off the HVAC. window_tamper_state = self.__read_window_tamper() # If temperature is less then 10 deg on loop 1. is_hot_water = self.__is_hot_water() # Take all necessary condition for normal operation of the HVAC. # stop_flag = (not is_empty or not window_tamper_state or not is_hot_water) stop_flag = False if stop_flag: self.__stop_timer.update() if self.__stop_timer.expired: self.__stop_timer.clear() if self.__stop_flag != stop_flag: self.__stop_flag = stop_flag self.__set_thermal_force(0) if not stop_flag: self.__stop_flag = False self.__stop_timer.update_last_time() # Main update rate at ~ 20 second. # На всеки 20 секунди се правят следните стъпки: self.__update_timer.update() if self.__update_timer.expired and not self.__stop_flag: self.__update_timer.clear() # Recalculate the temperatures. self.__temp_proc.update() crg_temp = 0 expected_room_temp = 0 # Update current room temperature. temperature = self.temperature # Add temperature to the queue. self.__queue_temperatures.append(temperature) self.__logger.debug("ROOM: {:3.3f}".format(temperature)) # 1. Изчислява се целевата температура на стаята: goal_room_temp = self.__goal_building_temp + self.__adjust_temp # 2. Изчислява се очакваната температура на стаята: expected_room_temp = temperature + self.__delta_temp # 3. Намира се коригиращата разлика между # Целевата температура и Очакваната температура на стаята: crg_temp = goal_room_temp - expected_room_temp self.__logger.debug("GRT {:3.3f}\t ERT {:3.3f}\t CRG: {:3.3f}"\ .format(goal_room_temp, expected_room_temp, crg_temp)) # 4. # Колкото е по-отрицателна температурата, толкова повече трябва да охлаждаме, # колкото е по-положителна - толкова повече трябва да отопляваме. # Определяме минималната термална сила, на база Корекционната температура: #self.__thermal_force_limit = aprox(-5 => -100, 5 => 100) # 5. Интегрираме термалната сила: self.__thermal_force += crg_temp # Apply the integrated force. self.__set_thermal_force(self.__thermal_force) if self.__loop1_valve_dev is not None: if self.__loop1_valve_dev.current_position <= 0: if self.__loop1_leak_test is not None: self.__loop1_leak_test.run() if self.__loop2_valve_dev is not None: if self.__loop2_valve_dev.current_position <= 0: if self.__loop2_leak_teat is not None: self.__loop2_leak_teat.run() self._registers.write("{}.temp_{}.actual".format(self.key, self.__identifier), temperature) # Recalculate delta time. # pass_time = time.time() - self.__lastupdate_delta_time # if pass_time > self.__delta_time: # self.__delta_temp = temperature - self.__delta_temp # self.__logger.debug("DT: {:3.3f}".format(self.__delta_temp)) # # Update current time. # self.__lastupdate_delta_time = time.time() self.__loop1_valve_dev.update() self.__loop2_valve_dev.update() self.__convector_dev.update() def _shutdown(self): """Shutdown the tamper. """ self.__logger.info("Shutting down the {} {}".format(self.name, self.__identifier)) self.__set_thermal_force(0) if not self.__loop1_valve_dev is None: self.__loop1_valve_dev.shutdown() if not self.__loop2_valve_dev is None: self.__loop2_valve_dev.shutdown() if not self.__convector_dev is None: self.__convector_dev.shutdown()
class BaseValve(BaseDevice): """Valve base class.""" #region Attributes _state = None """Valve state. """ _min_pos = 0 """Minimum allowed position. """ _max_pos = 100 """Maximum allowed position. """ __target_position = 0 """Target position. """ _current_position = 0 """Current position. """ _in_place_err = 0.1 """In place error. """ #endregion #region Constructor def __init__(self, config): """Convector Args: config (dict): Configuration """ super().__init__(config) self._state = StateMachine(ValveState.NONE) #endregion #region Properties @property def min_pos(self): """Minimum position. Returns: float: Minimum position. """ return self._min_pos @min_pos.setter def min_pos(self, value): """Minimum position. Args: value (float): Minimum position. """ in_value = value if value > 100: in_value = 100 if value < 0: in_value = 0 if in_value > self.max_pos: in_value = self.max_pos self._min_pos = value @property def max_pos(self): """Maximum position. Returns: float: Maximum position. """ return self._max_pos @max_pos.setter def max_pos(self, value): """Maximum position. Args: value (float): Maximum position. """ in_value = value if value > 100: in_value = 100 if value < 0: in_value = 0 if value < self.min_pos: in_value = self.min_pos self._max_pos = in_value @property def current_position(self): """Current position. Returns: float: Current position [%] """ return self._current_position @property def target_position(self): """Target postion. Returns: float: Target position [%] """ return self.__target_position @target_position.setter def target_position(self, position): """Set the position of the valve. Args: position (int): Position of the valve. """ if position == self.__target_position: return if self.target_position > 100: position = 100 if self.target_position > self.max_pos: position = self.max_pos if self.target_position < 0: position = 0 if self.target_position < self.min_pos: position = self.min_pos self.__target_position = position self._state.set_state(ValveState.Prepare) @property def in_place(self): """Returns if the valve is in place. Returns: bool: In place flag. """ # Calculate the delta between target and current positions. delta = abs(self.current_position - self.target_position) # If the delta is less then in place error we acpt that the valve is in position. return delta < self._in_place_err #endregion #region Protected Methods #endregion #region Public Methods def calibrate(self): """Calibrate the valve. """ pass def shutdown(self): if self.current_position == 0: return self.target_position = 0 while not self._state.is_state(ValveState.Wait): self.update()
class BaseCardReader(BaseDevice): """Card reader base class.""" #region Attributes _serial_number = None """Card reader ID.""" _port_name = None """Port name.""" _cb_read_card = None """Read card callback.""" _state = None """Reader state.""" #endregion #region Constructor def __init__(self, config): super().__init__(config) self._state = StateMachine(CardReaderState.NONE) if "port_name" in config: self._port_name = config["port_name"] if "serial_number" in config: self._serial_number = config["serial_number"] #endregion #region Properties @property def serial_number(self): """Returns the card reader ID. Returns ------- str Returns the card reader ID. """ return self._serial_number @property def port_name(self): """Returns the card reader port name. Returns ------- Enum Returns the card reader port name. """ if self._port_name is None: return "" return self._port_name @property def reader_state(self): """Start the reader.""" return self._state.get_state() #endregion #region Public Methods def cb_read_card(self, callback): """Add the card reader callback when card is inserted. Parameters ---------- callback : function pointer Called function. """ if callback is None: raise ValueError("Callback can not be None.") self._cb_read_card = callback
class ElectricalPerformance: """Electrical performance test.""" #region Attributes __test_state = None """Test state machine.""" __check_timer = None """Check test timer.""" __result_cb = None """Callback result device.""" __target_device = None """Target device.""" __registers = None """Registers""" __consumption_1 = 0 __consumption_2 = 0 #endregion #region Constructor / Destructor def __init__(self): """Constructor""" self.__test_state = StateMachine(TestState.NONE) self.__check_timer = Timer(0) self.__registers = Registers.from_csv() def __del__(self): """Destructor""" if self.__check_timer is not None: del self.__check_timer if self.__test_state is not None: del self.__test_state #endregion #region Private Methods def __turn_target_off(self): if isinstance(self.__target_device, F3P146EC072600): self.__target_device.set_speed(0) def __turn_target_on(self): if isinstance(self.__target_device, F3P146EC072600): self.__target_device.set_speed(10) def __take_current_consumption(self): current_power = 0 register = self.__registers.by_name("monitoring.pa.l1") if register is not None: register_data = json.loads(register.value) current_power = register_data["Current"] return current_power #endregion #region Public Methods def start(self): """Start the test.""" self.__test_state.set_state(TestState.Init) def stop(self): """Start the test.""" self.__turn_target_off() self.__test_state.set_state(TestState.NONE) def set_test_target(self, target_device): """Set test target device.""" if target_device is None: return self.__target_device = target_device if isinstance(self.__target_device, F3P146EC072600): self.__target_device.max_speed = 10 self.__target_device.min_speed = 0 def on_result(self, callback): """On result registration.""" if callback is None: return self.__result_cb = callback def run(self): """Run the test.""" if self.__test_state.is_state(TestState.Init): self.__turn_target_off() self.__consumption_1 = 0 self.__consumption_2 = 0 self.__test_state.set_state(TestState.TakeFirstConsumption) elif self.__test_state.is_state(TestState.TakeFirstConsumption): self.__consumption_1 = self.__take_current_consumption() self.__test_state.set_state(TestState.TurnTargetOn) elif self.__test_state.is_state(TestState.TurnTargetOn): self.__turn_target_on() self.__check_timer.expiration_time = 10 self.__check_timer.update_last_time() self.__test_state.set_state(TestState.Wait) elif self.__test_state.is_state(TestState.Wait): self.__check_timer.update() if self.__check_timer.expired: self.__check_timer.clear() self.__test_state.set_state(TestState.TakeSecondConsumption) elif self.__test_state.is_state(TestState.TakeSecondConsumption): self.__consumption_1 = self.__take_current_consumption() self.__test_state.set_state(TestState.TurnTargetOff) elif self.__test_state.is_state(TestState.TurnTargetOff): self.__turn_target_off() self.__test_state.set_state(TestState.ReturnResults) elif self.__test_state.is_state(TestState.ReturnResults): consuption_w = self.__consumption_2 - self.__consumption_1 if self.__result_cb is not None: self.__result_cb(consuption_w) self.__test_state.set_state(TestState.NONE)
def __init__(self): """Constructor""" self.__test_state = StateMachine(TestState.NONE) self.__check_timer = Timer(0) self.__registers = Registers.from_csv()
class MODV1(BaseBlind): """Electronic blinds""" #region Attributes __input_fb = verbal_const.OFF """Input feedback.""" __output_ccw = verbal_const.OFF """Output CCW""" __output_cw = verbal_const.OFF """Output CW""" __deg_per_sec = 0 """Degreases per sec.""" __feedback_treshold = 0 """Feedback threshold.""" __blinds_state = None """Blinds state.""" __move_timer = None """Move timer.""" __current_position = 0 """Current position of the blinds.""" __new_position = 0 """New position of the blinds.""" __calibration_state = None """"Calibration state.""" __feedback_type = FeedbackType.NONE """Feedback type.""" __timout_counter = 0 """Timeout counter.""" __t1 = 0 """T1 moment of first limit.""" __t2 = 0 """T2 moment of second limit.""" #endregion #region constructor / Destructor def __init__(self, **config): """Constructor""" super().__init__(config) self._vendor = "POLYGONTeam" self._model = "Blind" self.__logger = get_logger(__name__) self.__blinds_state = StateMachine(BlindsState.Wait) self.__move_timer = Timer() self.__calibration_state = StateMachine(CalibrationState.NONE) options = self._config["options"] if "output_cw" in options: self.__output_cw = options["output_cw"] if "output_ccw" in options: self.__output_ccw = options["output_ccw"] if "feedback" in options: self.__input_fb = options["feedback"] if "feedback_tresh" in options: self.__feedback_treshold = options["feedback_tresh"] if "deg_per_sec" in options: self.__deg_per_sec = options["deg_per_sec"] # Set the feedback type. if self.__input_fb.startswith("AI"): self.__feedback_type = FeedbackType.Analog elif self.__input_fb.startswith("DI"): self.__feedback_type = FeedbackType.Digital else: GlobalErrorHandler.log_missing_resource(self.__logger, "{}: feedback not set correct or incomparable. Check feedback settings.".format(self.name)) #endregion #region Private Methods (PLC I/O) def __stop(self): """Stop the engine.""" if self._controller.is_valid_gpio(self.__output_cw): self._controller.digital_write(self.__output_cw, 0) if self._controller.is_valid_gpio(self.__output_ccw): self._controller.digital_write(self.__output_ccw, 0) def __turn_cw(self): """Turn the motor CW.""" if self._controller.is_valid_gpio(self.__output_cw): self._controller.digital_write(self.__output_cw, 1) if self._controller.is_valid_gpio(self.__output_ccw): self._controller.digital_write(self.__output_ccw, 0) def __turn_ccw(self): """Turn the motor CCW.""" if self._controller.is_valid_gpio(self.__output_cw): self._controller.digital_write(self.__output_cw, 0) if self._controller.is_valid_gpio(self.__output_ccw): self._controller.digital_write(self.__output_ccw, 1) def __reed_fb(self): """Read feedback.""" fb_value = 0 if not self._controller.is_valid_gpio(self.__input_fb): return fb_value if self.__feedback_type == FeedbackType.Digital: fb_value = self._controller.digital_read(self.__input_fb) if self.__feedback_type == FeedbackType.Analog: ai = self._controller.analog_read(self.__input_fb) value = ai["value"] if value < 0: value = 0 fb_value = value >= self.__feedback_treshold # self.__logger.debug(f"Voltage: {value:02.4f}") return fb_value #endregion #regio Private Methods def __to_time(self, degrees): return degrees / self.__deg_per_sec #endregion #region Public Methods def init(self): self.__stop() def update(self): if self.__blinds_state.is_state(BlindsState.Prepare): delta_pos = self.__new_position - self.__current_position if delta_pos == 0: self.__stop() self.__blinds_state.set_state(BlindsState.Wait) return time_to_move = self.__to_time(abs(delta_pos)) self.__logger.debug("Time: {}".format(time_to_move)) self.__move_timer.expiration_time = time_to_move self.__move_timer.update_last_time() if delta_pos > 0: self.__turn_cw() elif delta_pos < 0: self.__turn_ccw() self.__blinds_state.set_state(BlindsState.Execute) elif self.__blinds_state.is_state(BlindsState.Execute): self.__move_timer.update() if self.__move_timer.expired: self.__move_timer.clear() self.__stop() self.__current_position = self.__new_position self.__blinds_state.set_state(BlindsState.Wait) input_fb = self.__reed_fb() if input_fb: self.__stop() GlobalErrorHandler.log_hardware_limit(self.__logger, "{} has raised end position.".format(self.name)) self.__current_position = self.__new_position self.__blinds_state.set_state(BlindsState.Wait) elif self.__blinds_state.is_state(BlindsState.Calibrate): if self.__calibration_state.is_state(CalibrationState.Stop): self.__stop() self.__current_position = 0 self.__new_position = 0 self.__calibration_state.set_state(CalibrationState.TurnCW) elif self.__calibration_state.is_state(CalibrationState.TurnCW): self.__turn_cw() self.__calibration_state.set_state(CalibrationState.WaitCurStabCW) elif self.__calibration_state.is_state(CalibrationState.WaitCurStabCW): if self.__timout_counter >= 5: self.__timout_counter = 0 self.__calibration_state.set_state(CalibrationState.WaitLimitCW) else: self.__timout_counter += 1 elif self.__calibration_state.is_state(CalibrationState.WaitLimitCW): fb_value = self.__reed_fb() if fb_value: self.__stop() self.__t1 = time.time() self.__calibration_state.set_state(CalibrationState.TurnCCW) elif self.__calibration_state.is_state(CalibrationState.TurnCCW): self.__turn_ccw() self.__calibration_state.set_state(CalibrationState.WaitCurStabCCW) elif self.__calibration_state.is_state(CalibrationState.WaitCurStabCCW): if self.__timout_counter >= 5: self.__timout_counter = 0 self.__calibration_state.set_state(CalibrationState.WaitLimitCCW) else: self.__timout_counter += 1 elif self.__calibration_state.is_state(CalibrationState.WaitLimitCCW): fb_value = self.__reed_fb() if fb_value: self.__stop() self.__t2 = time.time() time_delta = self.__t2 - self.__t1 time_delta -= 4 self.__deg_per_sec = 180 / time_delta self.__logger.info("DPS: {}".format(self.__deg_per_sec)) self.__turn_cw() self.__calibration_state.set_state(CalibrationState.ExitTightness) elif self.__calibration_state.is_state(CalibrationState.ExitTightness): if self.__timout_counter >= 12: self.__timout_counter = 0 self.__stop() self.__calibration_state.set_state(CalibrationState.WaitCurrentStab) else: self.__timout_counter += 1 elif self.__calibration_state.is_state(CalibrationState.WaitCurrentStab): if self.__timout_counter >= 4: self.__timout_counter = 0 self.__calibration_state.set_state(CalibrationState.GoTo90) else: self.__timout_counter += 1 elif self.__calibration_state.is_state(CalibrationState.GoTo90): self.__calibration_state.set_state(CalibrationState.NONE) self.__set_position(90) return def shutdown(self): self.set_position(0) while not self.__blinds_state.is_state(BlindsState.Wait): self.update() pass def set_position(self, position): """Set position of the blinds. Args: position (float): Position of the blinds. """ if self.__new_position == position: return if position > 180: position = 180 elif position < 0: position = 0 self.__new_position = position self.__blinds_state.set_state(BlindsState.Prepare)
class Zone(): """Main zone class""" #region Attributes __logger = None """Logger""" __app_settings = None """Application settings.""" __controller = None """Neuron controller.""" __erp = None """Communication with the ERP.""" __registers = None """Registers""" __plugin_manager = None """Plugin manager.""" __update_rate = 0.5 """Controlling update rate in seconds.""" __update_timer = None """Update timer.""" __erp_service_update_rate = 5 """ERP service update rate in seconds.""" __erp_service_update_timer = None """ERP update timer.""" __erp_state_machine = None """Zone state.""" __stop_flag = False """Time to Stop flag.""" __busy_flag = False """Busy flag.""" __performance_profiler = PerformanceProfiler() """Performance profiler.""" __performance_profiler_timer = None """Performance profiler timer.""" # (Request to stop the queue from MG @ 15.01.2021) # __registers_snapshot = None # """Registers snapshot.""" #endregion #region Private Methods (Registers Interface) def __target_version_cb(self, register: Register): current_version = None target_version = None target_version_reg = register # Check data type of the current version. if not target_version_reg.data_type == "json": GlobalErrorHandler.log_bad_register_data_type(self.__logger, target_version_reg) return # Get target version value. if target_version_reg.value is not None: target_version = target_version_reg.value # Check the target version value. if target_version == None: GlobalErrorHandler.log_bad_register_value(self.__logger, target_version) return # Get current version register. current_version_reg = self.__registers.by_name("sys.software.current_version") # Check data type. if not ((current_version_reg.data_type == "json")): GlobalErrorHandler.log_bad_register_data_type(self.__logger, current_version_reg) return if current_version_reg is not None: current_version = current_version_reg.value # Check the current version value. if current_version == None: GlobalErrorHandler.log_bad_register_value(self.__logger, current_version) return # Update the software. software_update(current_version_reg.value, target_version_reg.value) # Save the current version. self.__app_settings.current_version = target_version_reg.value self.__app_settings.save() def __init_registers(self): """Setup registers source. """ registers_file = "" # Current file path. & Go to file. cwf = os.path.dirname(os.path.abspath(__file__)) registers_file = os.path.join(cwf, "..", "registers.csv") # Load depending of file format. if registers_file.endswith('json'): self.__registers = Registers.from_json(registers_file) elif registers_file.endswith('csv'): self.__registers = Registers.from_csv(registers_file) else: sys.exit(0) target_version = self.__registers.by_name("sys.software.current_version") if target_version is not None: target_version.value = self.__app_settings.current_version target_version = self.__registers.by_name("sys.software.target_version") if target_version is not None: target_version.update_handlers = self.__target_version_cb # TODO: Will we ask for update on every start? #endregion #region Private Methods (PLC) def __evok_cb(self, device): """EVOK callback service handler. Args: device (JSON): GPIOs that was changed. """ # Target inputs, by registers names. names = ["ac.door_closed_1.input", "ac.door_closed_2.input",\ "ac.pir_1.input", "ac.pir_2.input",\ "ac.window_closed_1.input", "ac.window_closed_2.input",\ "monitoring.cw.input", "monitoring.hw.input", "sys.at.input"] inputs = self.__registers.by_names(names) gpio = self.__controller.device_to_uniname(device) for input_reg in inputs: if input_reg is not None: if input_reg.value == gpio: # If register exists, apply value. required_name = input_reg.name.replace("input", "state") if self.__registers is not None: self.__registers.write(required_name, device["value"]) def __init_controller(self): """Initialize the controller. """ # Create PLC. self.__controller = ControllerFactory.create(self.__app_settings.controller) if self.__controller is None: raise ValueError("Controller is not created.") # Print out the vendor and model of the controller. self.__logger.info(self.__controller) if self.__controller.vendor == "unipi": self.__controller.set_webhook(self.__evok_cb) self.__controller.start_web_service() # Create entrance if os.name == "posix": # Load settings evok_settings = EvokSettings("/etc/evok.conf") # Modifie evok_settings.webhook_address = "http://127.0.0.1:8889/api/v1/evok/webhooks ; Put your server endpoint address here (e.g. http://123.123.123.123:/wh )" evok_settings.webhook_enabled = True evok_settings.webhook_device_mask = ["input", "wd"] evok_settings.webhook_complex_events = True # Save evok_settings.save() # Restart the service to accept the settings. EvokSettings.restart() #endregion #region Private Methods (ERP) def __on_erp_state_change_cb(self, machine): """React on ERP state changes. Args: machine (StateMachine): State machine state. """ self.__logger.info("ERP state: {}".format(machine.get_state())) def __erp_set_registers(self, data): """ERP set registers values. Args: data (dict): Registers names and values. """ # (Request to have WEB API for work with registers. MG @ 15.01.2021) result = {} registers = {} if data is not None: registers = data["registers"] for register_name in registers: register = self.__registers.by_name(register_name) if registers is not None: # Apply the changes. register.value = registers[register_name] # In th response add what is going on. result[register.name] = register.value return result def __erp_get_registers(self, data): """ERP get registers values. Args: registers (dict): Names of registers. Returns: dict: Dictionary of registers values and their names. """ # (Request to have WEB API for work with registers. MG @ 15.01.2021) result = {} registers = {} if data is not None: registers = data["registers"] for register_name in registers: register = self.__registers.by_name(register_name) if register is not None: result[register.name] = register.value return result def __init_erp(self): """Setup the ERP. """ # Take ERP info from settings. erp_host = self.__app_settings.get_erp_service["host"] erp_timeout = self.__app_settings.get_erp_service["timeout"] # Create ERP. self.__erp = bgERP(host=erp_host, timeout=erp_timeout) # Set callbacks. self.__erp.set_registers_cb( get_cb=self.__erp_get_registers, set_cb=self.__erp_set_registers) # Set the ERP update timer. self.__erp_service_update_timer = Timer(self.__erp_service_update_rate) # Set zone state machine. self.__erp_state_machine = StateMachine() self.__erp_state_machine.on_change(self.__on_erp_state_change_cb) self.__erp_state_machine.set_state(ERPState.NONE) def __erp_login(self): """Login to bgERP. """ login_state = self.__erp.login( serial_number=self.__controller.serial_number, model=self.__controller.model, version=self.__controller.version, config_time=self.__app_settings.get_erp_service["config_time"], bgerp_id=self.__app_settings.get_erp_service["erp_id"]) if login_state: # Rewrite the ERP service ID. if self.__app_settings.get_erp_service["erp_id"] != self.__erp.erp_id: self.__app_settings.get_erp_service["erp_id"] = self.__erp.erp_id self.__app_settings.save() self.__erp_state_machine.set_state(ERPState.Update) else: self.__erp_state_machine.set_state(ERPState.Login) def __erp_update(self): """Sync registers. """ # Update periodically bgERP. self.__erp_service_update_timer.update() if self.__erp_service_update_timer.expired: self.__erp_service_update_timer.clear() ztm_regs = self.__registers.by_scope(Scope.Device) ztm_regs = ztm_regs.new_then(60) ztm_regs_dict = ztm_regs.to_dict() update_state = self.__erp.sync(ztm_regs_dict) if update_state is not None: # is not None if self.__registers is not None: self.__registers.update(update_state) # Clear the last atendies. (Eml6287) self.__registers.write("ac.last_update_attendees", str([])) # (Eml6287) # (Request to stop the queue from MG @ 15.01.2021) # not_send_len = self.__registers_snapshot.qsize() # if not_send_len > 0: # Get from the queue. # snapshot = self.__registers_snapshot.get() # Send the firs from the queue. # self.__erp.sync(snapshot) else: GlobalErrorHandler.log_no_connection_erp(self.__logger) self.__erp_state_machine.set_state(ERPState.Login) # # (Request to stop the queue from MG @ 15.01.2021) # # Create absolute copy of the object. # reg_copy = self.__registers.by_scope(Scope.Device).to_dict().copy() # # Put the copy to the queue. # self.__registers_snapshot.put(reg_copy) def __update_erp(self): """Update ERP. """ if self.__erp_state_machine.is_state(ERPState.Login): # Login room. self.__erp_login() elif self.__erp_state_machine.is_state(ERPState.Update): # Run the process of the room. self.__erp_update() elif self.__erp_state_machine.is_state(ERPState.NONE): self.__erp_state_machine.set_state(ERPState.Login) #endregion #region Private Methods (Runtime) def __init_runtime(self): # Update timer. self.__update_timer = Timer(self.__update_rate) # Update with offset based on the serial number of the device. time_offset = 0 if self.__controller.serial_number is not None and self.__controller.serial_number.isdigit(): time_offset = int(self.__controller.serial_number) # Preset the time. self.__update_timer.expiration_time += (time_offset / 1000) #endregion #region Private Methods (Performance Profiler) def __init_performance_profiler(self): """Set the performance profiler. """ self.__performance_profiler.enable_mem_profile = True self.__performance_profiler.enable_time_profile = True self.__performance_profiler.on_time_change(self.__on_time_change) self.__performance_profiler.on_memory_change(self.__on_memory_change) # Setup the performance profiler timer. (60) 10 is for tests. self.__performance_profiler_timer = Timer(10) def __on_time_change(self, passed_time): """On consumed time change. Args: passed_time (int): Consumed runtime. """ if self.__registers is not None: self.__registers.write("sys.time.usage", float("{:10.3f}".format(passed_time))) def __on_memory_change(self, current, peak): """On RAM memory change. Args: current (int): Current RAM. peak (int): Peak RAM. """ if self.__registers is not None: self.__registers.write("sys.ram.current", float("{:10.4f}".format(current))) self.__registers.write("sys.ram.peak", float("{:10.4f}".format(peak))) # print(f"Current memory usage is {current / 10**3}kB; Peak was {peak / 10**3}kB") @__performance_profiler.profile def __update(self): """Update the zone. """ # Create log if if it does not. crate_log_file() # Update the neuron. state = self.__controller.update() if not state: self.__logger.error("PLC service should be restarted.") GlobalErrorHandler.log_no_connection_plc(self.__logger) return # Give plugins runtime. self.__plugin_manager.update() self.__update_erp() #endregion #region Public Methods def init(self): """Initialize the process. """ try: # Application settings. self.__app_settings = ApplicationSettings.get_instance() # Create logger. self.__logger = get_logger(__name__) # Create registers. self.__init_registers() # Create PLC. self.__init_controller() # Create the plugin manager. self.__plugin_manager = PluginsManager(self.__registers, self.__controller) # Setup the ERP. self.__init_erp() # Initialize the performance profiler. self.__init_performance_profiler() # Initialize the runtime. self.__init_runtime() # # (Request to stop the queue from MG @ 15.01.2021) # self.__registers_snapshot = queue.Queue() except Exception: self.__logger.error(traceback.format_exc()) sys.exit(0) def run(self): """Run the zone. """ self.__logger.info("Starting up the Zone") while not self.__stop_flag: # Update process timers. self.__update_timer.update() self.__performance_profiler_timer.update() # If time has come for execution then run it once and clear the timer. if self.__update_timer.expired: self.__update_timer.clear() # If the busy flag is raise pass the update cycle. if self.__busy_flag: pass self.__busy_flag = True try: # If the time has come for profiling. # Enable the flag and tke profile. if self.__performance_profiler_timer.expired: self.__performance_profiler_timer.clear() self.__performance_profiler.enable = True # Update the application. self.__update() # If the performance profile is runing stop it after the cycle. if self.__performance_profiler.enable: self.__performance_profiler.enable = False # Log the exception without to close the application. except Exception: self.__logger.error(traceback.format_exc()) self.__busy_flag = False # Wait until the main teared ends. while self.__busy_flag == True: pass self.__logger.info("Shutting down the Zone") # Shutdown the plugins. self.__plugin_manager.shutdown() def shutdown(self): """Shutdown the process. """ self.__stop_flag = True
def __init__(self, flowmeter, test_time): self.__flowmeter_dev = flowmeter self.__test_state = StateMachine(TestState.TakeFirstMeasurement) self.__check_timer = Timer(test_time)
class LeakTest: """Leak test class.""" #region Attributes __test_state = None """Test state machine.""" __check_timer = None """Leak test timer.""" __flowmeter_dev = None """Flow meter device.""" __result_cb = None """Callback result device.""" __first_measurement = 0 """First measurement of loop flow meter.""" __second_measurement = 0 """Second measurement of loop flow meter.""" #endregion #region Constructor / Destructor def __init__(self, flowmeter, test_time): self.__flowmeter_dev = flowmeter self.__test_state = StateMachine(TestState.TakeFirstMeasurement) self.__check_timer = Timer(test_time) def __del__(self): if self.__check_timer is not None: del self.__check_timer if self.__test_state is not None: del self.__test_state #endregion #region Public Methods def on_result(self, callback): """On result registration.""" if callback is None: return self.__result_cb = callback def run(self): """Run the test.""" self.__check_timer.update() if self.__check_timer.expired: self.__check_timer.clear() if self.__test_state.is_state(TestState.WaitForLeak): if self.__test_state.was(TestState.TakeFirstMeasurement): self.__test_state.set_state( TestState.TakeSecondMeasurement) elif self.__test_state.was(TestState.TakeSecondMeasurement): self.__test_state.set_state(TestState.TakeFirstMeasurement) elif self.__test_state.is_state(TestState.TakeFirstMeasurement): self.__first_measurement = self.__flowmeter_dev.get_liters() self.__test_state.set_state(TestState.WaitForLeak) elif self.__test_state.is_state(TestState.TakeSecondMeasurement): self.__second_measurement = self.__flowmeter_dev.get_liters() self.__test_state.set_state(TestState.WaitForLeak) leak_liters = abs(self.__second_measurement - self.__first_measurement) if self.__result_cb is not None: self.__result_cb(leak_liters)