예제 #1
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()
예제 #2
0
파일: zone.py 프로젝트: bgerp/ztm
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()
예제 #3
0
파일: modv1.py 프로젝트: bgerp/ztm
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)
예제 #4
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)
예제 #5
0
class Zone(BasePlugin):
    """Security zone definition class."""

    #region Attribute

    __logger = None
    """Logger"""

    __identifier = 0
    """Security zone identifier."""

    __allowed_attendant = []
    """Allowed attendant."""

    __free_to_lock = 0
    """Free to lock flag."""

    __open_door_flag = 0
    """Open door flag."""

    __open_timer = None
    """Open timer."""

    __presence_timer = None
    """Presence timer"""

    __on_card_cb = None
    """Reader read callback"""

    __entry_reader = None
    """Entry card reader."""

    __exit_reader = None
    """Exit card reader."""

    __exit_btn_input = verbal_const.OFF  # "DI0"
    """Exit button input."""

    __window_closed_input = verbal_const.OFF
    """Window closed sensor input."""

    __door_closed_input = verbal_const.OFF
    """Door closed sensor input."""

    __pir_input = verbal_const.OFF
    """PIR closed sensor input."""

    __lock_mechanism_output = verbal_const.OFF  # "DO0"
    """Locking mechanism output."""

    __door_window_blind_output = verbal_const.OFF
    """Door window blind output."""

    #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.__entry_reader is not None:
            del self.__entry_reader

        if self.__exit_reader is not None:
            del self.__exit_reader

        if self.__open_timer is not None:
            del self.__open_timer

        super().__del__()

        if self.__logger is not None:
            del self.__logger

#endregion

#region Private Methods (Registers)

    def __entry_reader_cb(self, register):

        # Check data type.
        if not register.data_type == "json":
            GlobalErrorHandler.log_bad_register_value(self.__logger, register)
            return

        # Create
        if register.value != {} and self.__entry_reader is None:

            self.__entry_reader = CardReaderFactory.create(
                controller=self._controller,
                name="Entry Reader",
                vendor=register.value['vendor'],
                model=register.value['model'],
                options=register.value['options'])

            if self.__entry_reader is not None:
                if self.__entry_reader.reader_state == CardReaderState.NONE:
                    self.__entry_reader.cb_read_card(self.__cb_read_card)
                    self.__entry_reader.init()

        # Delete
        elif register.value == {} and self.__entry_reader is not None:
            self.__delete_reader(self.__entry_reader)

    def __exit_reader_cb(self, register):

        # Check data type.
        if not register.data_type == "json":
            GlobalErrorHandler.log_bad_register_value(self.__logger, register)
            return

        # Create
        if register.value != {} and self.__exit_reader is None:

            self.__exit_reader = CardReaderFactory.create(
                controller=self._controller,
                name="Exit Reader",
                vendor=register.value['vendor'],
                model=register.value['model'],
                options=register.value['options'])

            if self.__exit_reader is not None:
                if self.__exit_reader.reader_state == CardReaderState.NONE:
                    self.__exit_reader.cb_read_card(self.__cb_read_card)
                    self.__exit_reader.init()

        # Delete
        elif register.value == {} and self.__exit_reader is not None:
            self.__delete_reader(self.__exit_reader)

    def __exit_btn_input_cb(self, register):

        # Check data type.
        if not register.data_type == "str":
            GlobalErrorHandler.log_bad_register_value(self.__logger, register)
            return

        self.__exit_btn_input = register.value

    def __window_closed_input_cb(self, register):

        # Check data type.
        if not register.data_type == "str":
            GlobalErrorHandler.log_bad_register_value(self.__logger, register)
            return

        self.__window_closed_input = register.value

    def __door_closed_input_cb(self, register):

        # Check data type.
        if not register.data_type == "str":
            GlobalErrorHandler.log_bad_register_value(self.__logger, register)
            return

        self.__door_closed_input = register.value

    def __pir_input_cb(self, register):

        # Check data type.
        if not register.data_type == "str":
            GlobalErrorHandler.log_bad_register_value(self.__logger, register)
            return

        self.__pir_input = register.value

    def __lock_mechanism_output_cb(self, register):

        # Check data type.
        if not register.data_type == "str":
            GlobalErrorHandler.log_bad_register_value(self.__logger, register)
            return

        self.__lock_mechanism_output = register.value

    def __door_window_blind_output_cb(self, register):

        # Check data type.
        if not register.data_type == "str":
            GlobalErrorHandler.log_bad_register_value(self.__logger, register)
            return

        self.__door_window_blind_output = register.value

    def __time_to_open_cb(self, register):

        if not register.data_type == "int":
            GlobalErrorHandler.log_bad_register_value(self.__logger, register)
            return

        self.__open_timer.expiration_time = register.value

    def __is_empty_timeout_cb(self, register):

        # Check data type.
        if not register.data_type == "int":
            GlobalErrorHandler.log_bad_register_data_type(
                self.__logger, register)
            return

        self.__presence_timer.expiration_time = register.value

    def __door_window_blind_value_cb(self, register):

        # Check data type.
        if not register.data_type == "bool":
            GlobalErrorHandler.log_bad_register_value(self.__logger, register)
            return

        self.__set_door_window_blind(register.value)

    def __init_registers(self):

        # Get time to open the latch.
        time_to_open = self._registers.by_name("{}.time_to_open_{}".format(
            self.key, self.__identifier))
        if time_to_open is not None:
            time_to_open.update_handlers = self.__time_to_open_cb
            time_to_open.update()

        # Is empty timeout.
        is_empty_timeout = self._registers.by_name("envm.is_empty_timeout")
        if is_empty_timeout is not None:
            is_empty_timeout.update_handlers = self.__is_empty_timeout_cb
            is_empty_timeout.update()

        # Entry reader.
        entry_reader_enabled = self._registers.by_name(
            "{}.entry_reader_{}.enabled".format(self.key, self.__identifier))
        if entry_reader_enabled is not None:
            entry_reader_enabled.update_handlers = self.__entry_reader_cb

        # Exit reader.
        exit_reader_enabled = self._registers.by_name(
            "{}.exit_reader_{}.enabled".format(self.key, self.__identifier))
        if exit_reader_enabled is not None:
            exit_reader_enabled.update_handlers = self.__exit_reader_cb

        # Create exit button.
        exit_button_input = self._registers.by_name(
            "{}.exit_button_{}.input".format(self.key, self.__identifier))
        if exit_button_input is not None:
            exit_button_input.update_handlers = self.__exit_btn_input_cb
            exit_button_input.update()

        # Create window closed sensor.
        window_closed_input = self._registers.by_name(
            "{}.window_closed_{}.input".format(self.key, self.__identifier))
        if window_closed_input is not None:
            window_closed_input.update_handlers = self.__window_closed_input_cb
            window_closed_input.update()

        # Create door closed sensor.
        door_closed_input = self._registers.by_name(
            "{}.door_closed_{}.input".format(self.key, self.__identifier))
        if door_closed_input is not None:
            door_closed_input.update_handlers = self.__door_closed_input_cb
            door_closed_input.update()

        # Create PIR sensor.
        pir_input = self._registers.by_name("{}.pir_{}.input".format(
            self.key, self.__identifier))
        if pir_input is not None:
            pir_input.update_handlers = self.__pir_input_cb
            pir_input.update()

        # Create locking mechanism.
        lock_mechanism_output = self._registers.by_name(
            "{}.lock_mechanism_{}.output".format(self.key, self.__identifier))
        if lock_mechanism_output is not None:
            lock_mechanism_output.update_handlers = self.__lock_mechanism_output_cb
            lock_mechanism_output.update()

        # Door window blind.
        door_window_blind_output = self._registers.by_name(
            "{}.door_window_blind_{}.output".format(self.key,
                                                    self.__identifier))
        if door_window_blind_output is not None:
            door_window_blind_output.update_handlers = self.__door_window_blind_output_cb
            door_window_blind_output.update()

        # Door window blind.
        self._registers.add_callback("{}.door_window_blind_{}.value".format(
            self.key, self.__identifier),
                                     self.__door_window_blind_value_cb,
                                     update=True)

    def __set_zone_occupied(self, value):

        reg_occupation = self._registers.by_name("{}.zone_{}_occupied".format(
            self.key, self.__identifier))
        if reg_occupation is not None:
            if reg_occupation.value != value:
                reg_occupation.value = value

#endregion

#region Private Methods (Card Reader)

    def __check_card_state(self, card_id):

        # Request - Eml6287
        card_state = CardState.NONE

        allowed_len = len(self.__allowed_attendant)
        checked_index = 0
        for card in self.__allowed_attendant:
            if card["card_id"] == card_id:
                now = time.time()

                if now < card["valid_until"]:
                    card_state = CardState.Allowed
                    break

                else:
                    card_state = CardState.Expired
                    break

            checked_index += 1
            if checked_index == allowed_len and card_state == CardState.NONE:
                card_state = CardState.NotAllowed

        return card_state

    def __cb_read_card(self, card_id, serial_number):

        # Set flag to open the door.
        card_state = self.__check_card_state(card_id)
        if card_state == CardState.Allowed:
            if self.__open_door_flag == 0:
                self.__open_door_flag = 1

        if self.__on_card_cb is not None:
            self.__on_card_cb(card_id, serial_number, card_state.value)

    def __delete_reader(self, reader):

        if reader is not None:

            reader.shutdown()

            while reader.reader_state == CardReaderState.RUN:
                pass

            del reader

    def __update_entry_reader(self):
        """Update entry reader state."""

        if self.__entry_reader is not None:

            # Update card reader.
            self.__entry_reader.update()

            if self.__entry_reader.reader_state == CardReaderState.STOP:

                message = "Card reader {}; State {}; Port {}."\
                    .format(self.__entry_reader.serial_number, \
                            self.__entry_reader.reader_state, \
                            self.__entry_reader.port_name)

                GlobalErrorHandler.log_hardware_malfunction(
                    self.__logger, message)

                self.__entry_reader.init()

            if self.__entry_reader.reader_state == CardReaderState.NONE:

                message = "Card reader {}; State {}."\
                    .format(self.__entry_reader.serial_number, self.__entry_reader.reader_state)

                GlobalErrorHandler.log_hardware_malfunction(
                    self.__logger, message)

                self.__entry_reader.init()

    def __update_exit_reader(self):
        """Update exit reader state."""

        if self.__exit_reader is not None:

            # Update card reader.
            self.__exit_reader.update()

            if self.__exit_reader.reader_state == CardReaderState.STOP:

                message = "Card reader {}; State {}; Port {}."\
                    .format(self.__exit_reader.serial_number, \
                            self.__exit_reader.reader_state, \
                            self.__exit_reader.port_name)

                GlobalErrorHandler.log_hardware_malfunction(
                    self.__logger, message)

                self.__exit_reader.init()

            if self.__exit_reader.reader_state == CardReaderState.NONE:

                message = "Card reader {}; State {}."\
                    .format(self.__exit_reader.serial_number, self.__entry_reader.reader_state)

                GlobalErrorHandler.log_hardware_malfunction(
                    self.__logger, message)

                self.__exit_reader.init()

#endregion

#region (PLC)

    def __read_exit_button(self):

        state = False

        if self._controller.is_valid_gpio(self.__exit_btn_input):
            state = self._controller.digital_read(self.__exit_btn_input)

        return state

    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)

        return state

    def __read_door_tamper(self):

        state = False

        if self._controller.is_valid_gpio(self.__door_closed_input):
            state = self._controller.digital_read(self.__door_closed_input)

        return state

    def __read_pir_sensor(self):

        state = False

        if self._controller.is_valid_gpio(self.__pir_input):
            state = self._controller.digital_read(self.__pir_input)

        return state

    def __set_lock_mechanism(self, value=0):

        if self._controller.is_valid_gpio(self.__lock_mechanism_output):
            self._controller.digital_write(self.__lock_mechanism_output, value)

    def __set_door_window_blind(self, value=0):

        if self._controller.is_valid_gpio(self.__door_window_blind_output):
            self._controller.digital_write(self.__door_window_blind_output,
                                           value)

#endregion

#region Protected Methods

    def _init(self):
        """Initialize the plugin.
        """

        # Create logger.
        self.__logger = get_logger(__name__)
        self.__logger.info("Starting up the {} {}".format(
            self.name, self.__identifier))

        # Setup open timer to 10 seconds.
        self.__open_timer = Timer(10)

        # Setup presence timer to 60 seconds.
        self.__presence_timer = Timer(60)

        self.__init_registers()

    def _update(self):
        """Update the plugin.
        """

        # Update inputs.
        btn_state = self.__read_exit_button()
        door_tamper_state = self.__read_door_tamper()
        pir_sensor_state = self.__read_pir_sensor()
        window_tamper_state = self.__read_window_tamper()

        self.__update_entry_reader()
        self.__update_exit_reader()

        # Check if the button is pressed.
        if btn_state:
            if self.__open_door_flag == 0:
                self.__open_door_flag = 1

        # Check if the flag is raise.
        if self.__open_door_flag == 1:
            self.__set_lock_mechanism(1)
            self.__set_door_window_blind(1)
            self.__open_timer.update_last_time()
            self.__open_door_flag = 0
            self.__free_to_lock = 1

        # Lock the door after when it is closed.
        if door_tamper_state:
            if self.__free_to_lock == 1:
                self.__set_lock_mechanism(0)
                self.__set_door_window_blind(0)
                self.__free_to_lock = 0

        # Check is it time to close the latch.
        if self.__open_door_flag == 0:

            self.__open_timer.update()
            if self.__open_timer.expired:
                self.__open_timer.clear()

                if self.__free_to_lock == 1:
                    self.__set_lock_mechanism(0)
                    self.__set_door_window_blind(0)
                    self.__free_to_lock = 0

        # If one of the sensor are activated, set occupation flag to true.
        if door_tamper_state or pir_sensor_state or window_tamper_state:
            self.__presence_timer.update_last_time()
            self.__set_zone_occupied(True)

        # Else, run the expiration timer.
        else:
            self.__presence_timer.update()
            if self.__presence_timer.expired:
                self.__presence_timer.clear()
                self.__set_zone_occupied(False)

    def _shutdown(self):
        """Shutting down the plugin.
        """

        self.__logger.info("Shutting down the {} {}".format(
            self.name, self.__identifier))
        self.__set_lock_mechanism(0)
        self.__set_door_window_blind(0)
        self.__delete_reader(self.__entry_reader)
        self.__delete_reader(self.__exit_reader)

#endregion

#region Public Methods

    def on_card(self, callback):
        """Set reader read calback."""

        if callback is None:
            return

        self.__on_card_cb = callback

    def add_allowed_attendant(self, card_id):
        """Add allowed attendant ID.

        Parameters
        ----------
        ids : str
            Cards id.

        """

        if card_id is None:
            return

        if card_id == "":
            return

        if card_id in self.__allowed_attendant:
            pass

        else:
            self.__allowed_attendant.append(card_id)

    def add_allowed_attendees(self, ids):
        """Add allowed attendees IDs.

        Parameters
        ----------
        ids : array
            Cards id.

        """

        if ids is None:
            return

        self.__allowed_attendant.clear()

        for key in ids:
            record = ids[key]
            self.add_allowed_attendant(record)