Exemple #1
0
    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)
Exemple #2
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"]
Exemple #3
0
    def __init__(self, config):
        """Convector

        Args:
            config (dict): Configuration
        """

        super().__init__(config)

        self._state = StateMachine(ValveState.NONE)
Exemple #4
0
    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"]
Exemple #5
0
    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))
Exemple #6
0
    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)
Exemple #7
0
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()
Exemple #8
0
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()
Exemple #9
0
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()
Exemple #10
0
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
Exemple #11
0
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)
Exemple #12
0
    def __init__(self):
        """Constructor"""

        self.__test_state = StateMachine(TestState.NONE)
        self.__check_timer = Timer(0)
        self.__registers = Registers.from_csv()
Exemple #13
0
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)
Exemple #14
0
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
Exemple #15
0
 def __init__(self, flowmeter, test_time):
     self.__flowmeter_dev = flowmeter
     self.__test_state = StateMachine(TestState.TakeFirstMeasurement)
     self.__check_timer = Timer(test_time)
Exemple #16
0
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)