Esempio n. 1
0
class TemplatePlugin(BasePlugin):
    """Template plugin controller."""

    #region Attributes

    __logger = None
    """Logger"""

    __update_timer = None
    """Update timer."""

    #endregion

    #region Private Methods

    def __do_job(self):
        """Do the JOB method."""

        self.__logger.info("Do some job.")

#endregion

#region Public Methods

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

        self.__logger = get_logger(__name__)
        self.__logger.info("Starting up the {}".format(self.name))

        self.__update_timer = Timer(1)

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

        # Update the timer.
        self.__update_timer.update()

        if self.__update_timer.expired:

            self.__update_timer.clear()

            self.__do_job()

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

        self.__logger.info("Shutting down the {}".format(self.name))
Esempio n. 2
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()
Esempio n. 3
0
File: alarm.py Progetto: bgerp/ztm
class Alarm(BasePlugin):
    """Blinds controller device."""

    #region Attributes

    __logger = None
    """Logger"""

    __update_timer = None
    """Update timer."""

    #endregion

    #region Private Methods (Registers Interface)

    def __sound_device_settings_cb(self, register: Register):

        # TODO:  - Call the visual device factory.
        # TODO:  - Build the visual device interface.
        # TODO:  - Check is it possible to build.

        self.__logger.debug("Init the sound device.")

    def __visual_device_settings_cb(self, register: Register):

        # TODO:  - Call the visual device factory.
        # TODO:  - Build the visual device interface.
        # TODO:  - Check is it possible to build.

        self.__logger.debug("Init the visual device.")

    def __init_registers(self):

        visual_device_settings = self._registers.by_name(
            "{}.device.visual.settings".format(self.key))
        if visual_device_settings is not None:
            visual_device_settings.update_handlers = self.__visual_device_settings_cb
            visual_device_settings.update()

        sound_device_settings = self._registers.by_name(
            "{}.device.sound.settings".format(self.key))
        if sound_device_settings is not None:
            sound_device_settings.update_handlers = self.__sound_device_settings_cb
            sound_device_settings.update()

#endregion

#region Private Methods

    def __do_job(self):
        """Do the JOB method."""

        self.__logger.info("Do some job.")

#endregion

#region Public Methods

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

        self.__logger = get_logger(__name__)
        self.__logger.info("Starting up the {}".format(self.name))

        self.__update_timer = Timer(1)

        self.__init_registers()

    def _update(self):
        """Update the plugin.
        """
        # Update the timer.
        self.__update_timer.update()

        if self.__update_timer.expired:

            self.__update_timer.clear()

            self.__do_job()

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

        self.__logger.info("Shutting down the {}".format(self.name))
Esempio n. 4
0
File: zone.py Progetto: 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()
Esempio n. 5
0
File: stat.py Progetto: bgerp/ztm
class Statistics(BasePlugin):
    """Template plugin controller."""

    #region Attributes

    __logger = None
    """Logger"""

    __update_timer = None
    """Update timer."""

    __light_sensor = None
    """Light sensor target.
    """

    #endregion

    #region Private Methods

    def __get_todays_path(self, name=""):

        # Current file path.
        cwf = os.path.dirname(os.path.abspath(__file__))

        # Statistics path.
        full_dir_path = os.path.join(cwf, "..", "..", "..", "statistics")

        # Crete log directory.
        if not os.path.exists(full_dir_path):
            os.makedirs(full_dir_path)

        # Todays filename.
        todays_file_name = strftime("%Y%m%d_%H%M%S", gmtime())
        if not name != "":
            todays_file_name += "_"
            todays_file_name += name
        todays_file_name += ".log"

        # File name.
        file_name = os.path.join(full_dir_path, todays_file_name)

        return file_name

    def __create_logger(self, name, level=logging.INFO):
        """To setup as many loggers as you want"""

        log_file = self.__get_todays_path(name)

        # Formater
        # log_format = "%(asctime)s\t%(levelname)s\t%(name)s\t%(lineno)s\t%(message)s"
        log_format = "%(asctime)s\t%(name)s\t%(message)s"
        formatter = logging.Formatter(log_format)

        # Handler
        handler = logging.FileHandler(log_file)
        handler.setFormatter(formatter)

        # Logger
        logger = logging.getLogger(name)
        logger.setLevel(level)
        logger.addHandler(handler)

        return logger

    def __periodic_job(self):
        """Do the JOB method."""

        if self.__light_sensor is not None:
            self.__light_sensor.update()
            measured_value = self.__light_sensor.get_value()
            if self.__light_sensor_logger is not None:
                self.__light_sensor_logger.info("{}".format(measured_value))

#endregion

#region Private Methods (Registers Interface)

    def __light_sensor_settings_cb(self, register: Register):

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

        if register.value != None and self.__light_sensor is None:

            dev_name = "{} {}".format("Test sensor", self.name)
            self.__light_sensor = LuxmeterFactory.create(
                name=dev_name,
                controller=self._controller,
                vendor=register.value['vendor'],
                model=register.value['model'],
                options=register.value['options'])

            if self.__light_sensor is not None:
                self.__light_sensor.init()

    def __init_registers(self):

        sensor_enabled = self._registers.by_name("light.sensor.settings")
        if sensor_enabled is not None:
            sensor_enabled.update_handlers = self.__light_sensor_settings_cb
            sensor_enabled.update()

#endregion

#region Public Methods

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

        self.__logger = get_logger(__name__)
        self.__logger.info("Starting up the {}".format(self.name))

        self.__update_timer = Timer(1)

        self.__light_sensor_logger = self.__create_logger("LightSensor")

        self.__init_registers()

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

        # Update the timer.
        self.__update_timer.update()

        if self.__update_timer.expired:

            self.__update_timer.clear()

            self.__periodic_job()

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

        self.__logger.info("Shutting down the {}".format(self.name))
Esempio n. 6
0
class Sys(BasePlugin):
    """Blinds controller device."""

#region Attributes

    __logger = None
    """Logger"""

    __led_state = 0
    """LED State."""

    __blink_timer = None
    """Update timestamp."""

    __led_out = verbal_const.OFF
    """LED Output"""

    __collission_timer = None
    """Update timer."""

    __rules = None
    """Rules"""

    __enable_info_msg = True
    """Enable info messages."""

    __enable_wrn_msg = True
    """Enable warning messages."""

    __enable_err_msg = True
    """Enable error messages."""

    __disc_status_timer = None
    """Disc check timer."""

#endregion

#region Destructor

    def __del__(self):

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

        super().__del__()

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

#endregion

#region Private Methods (Status LED)

    def __set_led(self, state):

        if self._controller.is_valid_gpio(self.__led_out):
            self._controller.set_led(self.__led_out, state)

#endregion

#region Private Methods (Colision Detection)

    def __setup_rules(self):

        self.__rules = Rules()

        # GPIOs
        for index in range(12):
            self.__rules.add(Rule("DO{}".format(index), MonitoringLevel.Error))
            self.__rules.add(Rule("DI{}".format(index), MonitoringLevel.Warning))
            self.__rules.add(Rule("AO{}".format(index), MonitoringLevel.Error))
            self.__rules.add(Rule("AI{}".format(index), MonitoringLevel.Warning))
            self.__rules.add(Rule("RO{}".format(index), MonitoringLevel.Error))

        # 1 Wire devices
        ow_devices = self._controller.get_1w_devices()
        for ow_device in ow_devices:
            self.__rules.add(Rule(ow_device["circuit"], MonitoringLevel.Info))

        # Serial Ports
        if os.name == "nt":
            for index in range(1, 11):
                self.__rules.add(Rule("COM{}".format(index), MonitoringLevel.Error))

        elif os.name == "posix":
            for index in range(0, 11):
                self.__rules.add(Rule("/dev/ttyS{}".format(index), MonitoringLevel.Error))
                self.__rules.add(Rule("/dev/ttyUSB{}".format(index), MonitoringLevel.Error))
                self.__rules.add(Rule("/dev/ttyACM{}".format(index), MonitoringLevel.Error))

        # Add event.
        self.__rules.on_event(self.__on_event)

    def __on_event(self, intersections, rule: Rule):
        """On event callback for collisions."""

        level = MonitoringLevel(rule.level)

        if level == MonitoringLevel.Debug:
            self.__logger.debug("Debug")
            self.__logger.debug(intersections)

        if level == MonitoringLevel.Info and self.__enable_info_msg:
            self._registers.write("{}.col.info_message".format(self.key), intersections)

        if level == MonitoringLevel.Warning and self.__enable_wrn_msg:
            self._registers.write("{}.col.warning_message".format(self.key), intersections)

        if level == MonitoringLevel.Error and self.__enable_err_msg:
            self._registers.write("{}.col.error_message".format(self.key), intersections)

#endregion

#region Private Methods (Registers Interface)

    def __blink_time_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

        self.__blink_timer.expiration_time = register.value

    def __led_out_cb(self, register):

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

        self.__led_out = register.value

    def __clear_errors_cb(self, register):
        """Clear errors callback."""

        if register.value == 1:
            # Clear the flag.
            register.value = 0

            # Clear info messages.
            self._registers.write("{}.col.info_message".format(self.key), {})

            # Clear warning messages.
            self._registers.write("{}.col.warning_message".format(self.key), {})

            # Clear error messages.
            self._registers.write("{}.col.error_message".format(self.key), {})

    def __enable_info_msg_cb(self, register):

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

        self.__enable_info_msg = register.value

    def __enable_wrn_msg_cb(self, register):

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

        self.__enable_wrn_msg = register.value

    def __enable_err_msg_cb(self, register):

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

        self.__enable_err_msg = register.value

    def __init_registers(self):

        # Status LED blink time.
        blink_time = self._registers.by_name(self.key + ".sl.blink_time")
        if blink_time is not None:
            blink_time.update_handlers = self.__blink_time_cb
            blink_time.update()

        # Status LED output.
        output = self._registers.by_name(self.key + ".sl.output")
        if output is not None:
            output.update_handlers = self.__led_out_cb
            output.update()

        clear_errors = self._registers.by_name(self.key + ".col.clear_errors")
        if clear_errors is not None:
            clear_errors.update_handlers = self.__clear_errors_cb
            clear_errors.value = 1

        # Enable info messages.
        enable_info_msg = self._registers.by_name(self.key + ".col.info_message.enable")
        if enable_info_msg is not None:
            enable_info_msg.update_handlers = self.__enable_info_msg_cb
            enable_info_msg.update()

        # Enable warning messages.
        enable_wrn_msg = self._registers.by_name(self.key + ".col.warning_message.enable")
        if enable_wrn_msg is not None:
            enable_wrn_msg.update_handlers = self.__enable_wrn_msg_cb
            enable_wrn_msg.update()

        # Enable error messages.
        enable_err_msg = self._registers.by_name(self.key + ".col.error_message.enable")
        if enable_err_msg is not None:
            enable_err_msg.update_handlers = self.__enable_err_msg_cb
            enable_err_msg.update()

    def __update_disc_space(self):

        total, used, free = disk_size()

        self._registers.write("sys.disc.total", total)
        self._registers.write("sys.disc.used", used)
        self._registers.write("sys.disc.free", free)

#endregion

#region Public Methods

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

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

        # Status LED blink timer.
        self.__blink_timer = Timer(1)

        # Colission detection.
        self.__collission_timer = Timer(1)

        # Create disc check timer.
        self.__disc_status_timer = Timer(10)

        self.__init_registers()

        self.__setup_rules()

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

        # Update the Blink LED timer.
        self.__blink_timer.update()
        if self.__blink_timer.expired:
            self.__blink_timer.clear()

            if self.__led_state:
                self.__led_state = 0
            else:
                self.__led_state = 1

            # update the LED state.
            self.__set_led(self.__led_state)

        # Update the collision timer.
        self.__collission_timer.update()
        if self.__collission_timer.expired:
            self.__collission_timer.clear()

             # Check for collision.
            self.__rules.check(self._registers)

        # Update disc space.
        self.__disc_status_timer.update()
        if self.__disc_status_timer.expired:
            self.__disc_status_timer.clear()

            # Update the disck space registers.
            self.__update_disc_space()

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

        self.__logger.info("Shutting down the {}".format(self.name))
        self.__set_led(0)
Esempio n. 7
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)
Esempio n. 8
0
class Blind(BasePlugin):
    """Blind controller device."""

    #region Attributes

    __logger = None
    """Logger"""

    __identifier = 0
    """Identifier
    """

    __blind_mechanism = None
    """Blkind mechanism
    """

    __sun_spot_update_timer = None
    """Sun spot update timer.
    """

    __sun_azm = 0
    """Sun azimuth.
    """

    __sun_elev = 0
    """Sun elevation.
    """

    __sun_spot_limit = 0
    """Sun spot limit.
    """

    __object_height = 0
    """Object height [m]
    """

    __zone_occupation = False
    """Zone occupation flag.
    """

    #endregion

    #region Constructor / Destructor

    def __init__(self, **config):
        """Constructor"""

        super().__init__(config)

        if "identifier" in config:
            self.__identifier = config["identifier"]

    def __del__(self):
        """Destructor"""

        super().__del__()

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

#endregion

#region Private Methods (Registers)

    def __blind_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.__blind_mechanism is None:

            self.__blind_mechanism = BlindsFactory.create(
                controller=self._controller,
                name="Blind",
                vendor=register.value['vendor'],
                model=register.value['model'],
                options=register.value['options'])

            if self.__blind_mechanism is not None:
                self.__blind_mechanism.init()

        # Delete
        elif register.value == {} and self.__blind_mechanism is not None:
            del self.__blind_mechanism

    def __position_cb(self, register):

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

        if self.__blind_mechanism is not None:
            self.__blind_mechanism.set_position(register.value)

    def __object_height_cb(self, register):

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

        if self.__object_height != register.value:
            self.__object_height = register.value

    def __sunspot_limit_cb(self, register):

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

        if self.__sun_spot_limit != register.value:
            self.__sun_spot_limit = register.value

    def __zone_occupation_cb(self, register):

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

        if self.__zone_occupation != register.value:
            self.__zone_occupation = register.value

    def __init_registers(self):

        blind = self._registers.by_name("{}.blind_{}.mechanism".format(
            self.key, self.__identifier))
        if blind is not None:
            blind.update_handlers = self.__blind_cb
            blind.update()

        position = self._registers.by_name("{}.blind_{}.position".format(
            self.key, self.__identifier))
        if position is not None:
            position.update_handlers = self.__position_cb
            position.update()

        object_height = self._registers.by_name(
            "{}.blind_{}.object_height".format(self.key, self.__identifier))
        if object_height is not None:
            object_height.update_handlers = self.__object_height_cb
            object_height.update()

        sunspot_limit = self._registers.by_name(
            "{}.blind_{}.sunspot_limit".format(self.key, self.__identifier))
        if sunspot_limit is not None:
            sunspot_limit.update_handlers = self.__sunspot_limit_cb
            sunspot_limit.update()

        ac_zone_occupied = self._registers.by_name(
            "ac.zone_{}_occupied".format(self.__identifier))
        if ac_zone_occupied is not None:
            ac_zone_occupied.update_handlers = self.__zone_occupation_cb
            ac_zone_occupied.update()

    def __get_sun_pos(self):

        sun_elev_reg = self._registers.by_name("envm.sun.elevation")
        if sun_elev_reg is not None:
            if not ((sun_elev_reg.data_type == "float") or
                    (sun_elev_reg.data_type == "int")):
                GlobalErrorHandler.log_bad_register_value(
                    self.__logger, sun_elev_reg)
                return

            if self.__sun_elev != sun_elev_reg.value:
                self.__sun_elev = sun_elev_reg.value

        sun_azm_reg = self._registers.by_name("envm.sun.azimuth")
        if sun_azm_reg is not None:
            if not ((sun_azm_reg.data_type == "float") or
                    (sun_azm_reg.data_type == "int")):
                GlobalErrorHandler.log_bad_register_value(
                    self.__logger, sun_azm_reg)
                return

            if self.__sun_azm != sun_azm_reg.value:
                self.__sun_azm = sun_azm_reg.value

#endregion

#region Private Methods

    def __calc_sun_spot(self):

        if (self.__sun_azm > 0) and (self.__sun_elev > 0):
            # print(f"Blinds -> Azm: {self.__sun_azm:03.2f}; Elev: {self.__sun_elev:03.2f}")

            # Calculate the shadow length.
            shadow_l = shadow_length(self.__object_height,
                                     to_rad(self.__sun_elev))
            # print(f"Blinds -> Shadow: {shadow_l:03.2f}")

            theta = 360 - (self.__sun_azm + 180)
            # print(f"Blinds -> Theta: {theta:03.2f}")

            # Calculate cartesian
            x = shadow_l * math.cos(to_rad(abs(theta)))
            y = shadow_l * math.sin(to_rad(abs(theta)))
            # print(f"Blinds -> X: {x:03.2f}; Y: {y:03.2f}")

            is_cloudy = False
            if (x > self.__sun_spot_limit
                    or y > self.__sun_spot_limit) and not is_cloudy:

                # If is not cloudy and the sun spot is too big, just close the blinds.
                if not self.__blind_mechanism is None:
                    self.__blind_mechanism.set_position(0)

#endregion

#region Public Methods

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

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

        self.__sun_spot_update_timer = Timer(2)

        self.__init_registers()

        if not self.__blind_mechanism is None:
            self.__blind_mechanism.set_position(90)

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

        # Update occupation flags.
        if not self.__zone_occupation:
            if not self.__blind_mechanism is None:
                # self.__blind_mechanism.set_position(0)
                pass

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

            self.__get_sun_pos()
            self.__calc_sun_spot()

        if not self.__blind_mechanism is None:
            self.__blind_mechanism.update()

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

        self.__logger.info("Shutting down the {} {}".format(
            self.name, self.__identifier))

        if not self.__blind_mechanism is None:
            self.__blind_mechanism.shutdown()
Esempio n. 9
0
class Monitoring(BasePlugin):
    """Blinds controller device."""

    #region Attributes

    __logger = None
    """Logger"""

    __cw_flowmeter_dev = None
    """Cold water flow meter."""

    __cw_leak_test = None
    """Cold water leak test."""

    __hw_flowmeter_dev = None
    """Hot water flow meter."""

    __hw_leak_test = None
    """Hot water leak test."""

    __power_analyser = None
    """Power analyser."""

    __evok_setting = None
    """EVOK settings."""

    __measurements = []
    """Power analyser measurements.
    """

    __demand_timer = None
    """Demand measuring timer.
    """

    #endregion

    #region Private Methods

    def __filter_measurements_by_time(self, measurements, time_sec: int):

        # Create filter list.
        filtered_measurements = measurements.copy()
        filtered_measurements.clear()

        # Reset delete flag.
        delete_flag = False

        # Now!
        time_now = time.time()

        # Filter all records.
        for measurement in measurements:

            # Calculate delta time.
            delta_t = time_now - measurement["ts"]

            # Filter
            if delta_t < time_sec:
                filtered_measurements.append(measurement)

            # Else mark for deletion.
            else:
                delete_flag = True

        # Execute the flag.
        if delete_flag:
            self.__measurements.clear()
            for measurement in filtered_measurements:
                self.__measurements.append(measurement)

#endregion

#region Private Methods (Cold Water Flowmeter)

    def __cw_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.__cw_flowmeter_dev is None:
            self.__cw_flowmeter_dev = FlowmetersFactory.create(
                name="Cold water flowmeter",
                controller=self._controller,
                vendor=register.value['vendor'],
                model=register.value['model'],
                options=register.value['options'])

            if self.__cw_flowmeter_dev is not None:
                self.__cw_flowmeter_dev.init()

                # 20 seconds is time for leak testing.
                self.__cw_leak_test = LeakTest(self.__cw_flowmeter_dev, 20)
                self.__cw_leak_test.on_result(self.__cw_leaktest_result)

        elif register.value == {} and self.__cw_flowmeter_dev is not None:
            self.__cw_flowmeter_dev.shutdown()
            del self.__cw_flowmeter_dev
            del self.__cw_leak_test

    def __cw_leaktest_result(self, leak_liters):

        if leak_liters > 0:
            self._registers.write("{}.cw.leak".format(self.key), leak_liters)

    def __init_cw(self):

        cw_flowmeter = self._registers.by_name(
            "{}.cw.flowmeter_settings".format(self.key))
        if cw_flowmeter is not None:
            cw_flowmeter.update_handlers = self.__cw_flowmeter_settings_cb
            cw_flowmeter.update()

    def __update_cw(self):

        fm_state = self._registers.by_name(self.key + ".cw.value")
        if self.__cw_flowmeter_dev is not None and\
            fm_state is not None:
            fm_state.value = self.__cw_flowmeter_dev.get_liters()

        # If the zone is empty check for leaks.
        is_empty = self._registers.by_name("envm.is_empty")
        if self.__cw_flowmeter_dev is not None and\
            is_empty is not None and\
            is_empty.value:

            self.__cw_leak_test.run()

#endregion

#region Private Methods (Hot Water Flowmeter)

    def __hw_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.__hw_flowmeter_dev is None:
            self.__hw_flowmeter_dev = FlowmetersFactory.create(
                name="Hot water flowmeter",
                controller=self._controller,
                vendor=register.value['vendor'],
                model=register.value['model'],
                options=register.value['options'])

            if self.__hw_flowmeter_dev is not None:
                self.__hw_flowmeter_dev.init()

                # 20 seconds is time for leak testing.
                self.__hw_leak_test = LeakTest(self.__hw_flowmeter_dev, 20)
                self.__hw_leak_test.on_result(self.__hw_leaktest_result)

        elif register.value == {} and self.__hw_flowmeter_dev is not None:
            self.__hw_flowmeter_dev.shutdown()
            del self.__hw_flowmeter_dev
            del self.__hw_leak_test

    def __hw_leaktest_result(self, leak_liters):

        if leak_liters > 0:
            self._registers.write("{}.hw.leak".format(self.key), leak_liters)

    def __init_hw(self):

        hw_flowmeter = self._registers.by_name(
            "{}.hw.flowmeter_settings".format(self.key))
        if hw_flowmeter is not None:
            hw_flowmeter.update_handlers = self.__hw_flowmeter_settings_cb
            hw_flowmeter.update()

    def __update_hw(self):

        fm_state = self._registers.by_name("{}.hw.value".foamrat(self.key))
        if self.__hw_flowmeter_dev is not None and\
            fm_state is not None:
            fm_state.value = self.__hw_flowmeter_dev.get_liters()

        # If the zone is empty check for leaks.
        is_empty = self._registers.by_name("envm.is_empty")
        if self.__hw_flowmeter_dev is not None and\
            is_empty is not None and\
            is_empty.value:

            self.__hw_leak_test.run()

#endregion

#region Private Methods (Power Analyser)

    def __read_all_parameters(self):

        black_list = [
            "GetRelays",
            "GetDigitalInputs",
            "GetAnalogOutputs",
            "GetAnalogInputs",
            "SetRelays",
            "SetAnalogOutputs",
        ]

        for parameter in self.__power_analyser.parameters:

            name = parameter.parameter_name

            if name in black_list:
                continue

            value = self.__read_local_parameter(name)

            print("{}: {}".format(name, value))

    def __read_local_parameter(self, name):

        value = 0.0

        request = self.__power_analyser.generate_request(name)
        if request is not None:
            response = self._controller.execute_mb_request(
                request, self.__power_analyser.uart)
            if not response.isError():
                registers = {}
                for index in range(request.address,
                                   request.address + request.count):
                    registers[index] = response.registers[index -
                                                          request.address]
                value = self.__power_analyser.get_parameter_value(
                    name, registers)

        return value

    def __read_unipi_mb_master(self):

        # Get structure data.
        registers_ids = self.__power_analyser.get_registers_ids()

        # Get values by the structure.
        registers_values = self._controller.read_mb_registers(\
            self.__uart, \
            self.__dev_id, \
            registers_ids, \
            RegisterType.ReadInputRegisters)

        # Convert values to human readable.
        parameters_values = self.__power_analyser.get_parameters_values(
            registers_values)

        # Format the floating points.
        for parameter_value in parameters_values:
            try:
                parameters_values[parameter_value] = \
                    float("{:06.3f}".format(float(parameters_values[parameter_value])))

            except ValueError:
                parameters_values[parameter_value] = float(
                    "{:06.3f}".format(000.0))

        return parameters_values

    def __pa_enabled_cb(self, register):

        if not register.data_type == "json":
            return

        if register.value != {} and self.__power_analyser is None:

            self.__power_analyser = PowerAnalyserFactory.create(
                controller=self._controller,
                name="Zone Power analyser",
                vendor=register.value['vendor'],
                model=register.value['model'],
                options=register.value['options'])

            if self._controller.vendor == "UniPi":

                # Load EVOK settings.
                if os.name == "posix":
                    self.__evok_setting = EvokSettings("/etc/evok.conf")
                if os.name == "nt":
                    self.__evok_setting = EvokSettings("evok.conf")

                # Save settings to the EVOK software.
                if not self.__evok_setting.device_exists("EXTENTION_1"):

                    # Vendor
                    # vendor = register.value['vendor']

                    # Model
                    model = register.value['model']

                    # UART
                    uart = register.value['model']

                    # Unit
                    unit = register.value['options']['unit']

                    # Add extention 1.
                    extention_1 = {
                        "global_id": unit,
                        "device_name": model,
                        "modbus_uart_port": "/dev/extcomm/0/0",
                        "allow_register_access": True,
                        "address": uart,
                        "scan_frequency": 10,
                        "scan_enabled": True,
                        "baud_rate": 9600,
                        "parity": "N",
                        "stop_bits": 1
                    }

                    # Add the configuration.
                    self.__evok_setting.add_named_device(
                        extention_1, "EXTENTION_1")
                    self.__evok_setting.save()
                    self.__logger.debug("Enable the Power Analyser.")

                    # Restart the service.
                    if os.name == "posix":
                        EvokSettings.restart()
                        self.__logger.debug("Restart the EVOK service.")

        elif register.value == {} and self.__power_analyser is not None:
            self.__power_analyser = None

            if self._controller.vendor == "UniPi":

                if self.__evok_setting.device_exists("EXTENTION_1"):

                    # Remove the settings.
                    self.__evok_setting.remove_device("EXTENTION_1")
                    self.__evok_setting.save()
                    self.__logger.debug("Disable the Power Analyser.")

                    # Restart the service.
                    if os.name == "posix":
                        EvokSettings.restart()
                        self.__logger.debug("Restart the EVOK service.")

    def __pa_demand_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

        if register.value < 0.0:
            GlobalErrorHandler.log_bad_register_value(self.__logger, register)
            return

        if self.__demand_timer is not None:
            self.__demand_timer.expiration_time = register.value

    def __init_pa(self):

        pa_enabled = self._registers.by_name(self.key + ".pa.settings")
        if pa_enabled is not None:
            pa_enabled.update_handlers = self.__pa_enabled_cb
            pa_enabled.update()

        demand_time = self._registers.by_name(self.key + ".pa.demand_time")
        if demand_time is not None:
            demand_time.update_handlers = self.__pa_demand_time_cb
            demand_time.update()

    def __update_pa(self):

        if self.__power_analyser is None:
            return

        # self.__read_all_parameters()

        measurement = {
            "ImportActiveEnergy": 0.0,
            "ExportActiveEnergy": 0.0,
            "ImportReactiveEnergy": 0.0,
            "ExportReactiveEnergy": 0.0,
            "Phase1Current": 0.0,
            "Phase2Current": 0.0,
            "Phase3Current": 0.0,
            "ts": 0,
        }

        if self.__power_analyser.model == "SDM120":

            if self._controller.vendor == "UniPi":

                values = self.__read_unipi_mb_master()

                measurement["ImportActiveEnergy"] = values[
                    "ImportActiveEnergy"]
                measurement["ExportActiveEnergy"] = values[
                    "ExportActiveEnergy"]
                measurement["ImportReactiveEnergy"] = values[
                    "ImportReactiveEnergy"]
                measurement["ExportReactiveEnergy"] = values[
                    "ExportReactiveEnergy"]
                measurement["Phase1Current"] = values["Current"]

            else:
                measurement[
                    "ImportActiveEnergy"] = self.__read_local_parameter(
                        "ImportActiveEnergy")
                measurement[
                    "ExportActiveEnergy"] = self.__read_local_parameter(
                        "ExportActiveEnergy")
                measurement[
                    "ImportReactiveEnergy"] = self.__read_local_parameter(
                        "ImportReactiveEnergy")
                measurement[
                    "ExportReactiveEnergy"] = self.__read_local_parameter(
                        "ExportReactiveEnergy")
                measurement["Phase1Current"] = self.__read_local_parameter(
                    "Current")

        elif self.__power_analyser.model == "SDM630":

            if self._controller.vendor == "UniPi":
                values = self.__read_unipi_mb_master()
                measurement["ImportActiveEnergy"] = values["TotalImportkWh"]
                measurement["ExportActiveEnergy"] = values["TotalExportkWh"]
                measurement["ImportReactiveEnergy"] = values[
                    "TotalImportkVArh"]
                measurement["ExportReactiveEnergy"] = values[
                    "TotalExportkVArh"]
                measurement["Phase1Current"] = values["Phase1Current"]
                measurement["Phase2Current"] = values["Phase2Current"]
                measurement["Phase3Current"] = values["Phase3Current"]

            else:
                measurement[
                    "ImportActiveEnergy"] = self.__read_local_parameter(
                        "TotalImportkWh")
                measurement[
                    "ExportActiveEnergy"] = self.__read_local_parameter(
                        "TotalExportkWh")
                measurement[
                    "ImportReactiveEnergy"] = self.__read_local_parameter(
                        "TotalImportkVArh")
                measurement[
                    "ExportReactiveEnergy"] = self.__read_local_parameter(
                        "TotalExportkVArh")
                measurement["Phase1Current"] = self.__read_local_parameter(
                    "Phase1Current")
                measurement["Phase2Current"] = self.__read_local_parameter(
                    "Phase2Current")
                measurement["Phase3Current"] = self.__read_local_parameter(
                    "Phase3Current")

        else:
            self.__logger.error("Unknown power analyser")

        # Set the time of the measurement.
        measurement["ts"] = time.time()

        # Add measurement to the tail.
        self.__measurements.append(measurement)

        # This magical number represents seconds for 24 hours.
        self.__filter_measurements_by_time(self.__measurements, 86400)

        # Update parameters in the registers.
        self._registers.write("{}.pa.measurements".format(self.key),
                              json.dumps(self.__measurements))

#endregion

#region Public Methods

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

        self.__logger = get_logger(__name__)
        self.__logger.info("Starting up the {}".format(self.name))

        self.__demand_timer = Timer(3600)

        # Init cold water flow meter.
        self.__init_cw()

        # Init hot water flow meter.
        self.__init_hw()

        # Init power analyser.
        self.__init_pa()

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

        # Update cold water flow meter.
        self.__update_cw()

        # Update hot water flow meter.
        self.__update_hw()

        # Check is it time to measure.
        self.__demand_timer.update()
        if self.__demand_timer.expired:

            # Clear the timer.
            self.__demand_timer.clear()

            # Update power analyser.
            self.__update_pa()

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

        self.__logger.info("Shutting down the {}".format(self.name))
Esempio n. 10
0
File: envm.py Progetto: bgerp/ztm
class Environment(BasePlugin):
    """Environment control and provisioning."""

    #region Attributes

    __logger = None
    """Logger"""

    __update_timer = None
    """Update timer."""

    __sunpos_enabled_value = False
    """Sun position local calculation enabled."""

    __location_lat = 0.0
    """Location latitude.
    """

    __location_lon = 0.0
    """Location longitude.
    """

    __location_elv = 0.0
    """Location elevation.
    """

    __time_zone = 0
    """Location time zone.
    """

    __temperature = 0
    """Temperature
    """

    #endregion

    #region Private Methods (Registers)

    def __sunpos_enabled_cb(self, register):
        """Sun position calculation enable flag handler.

        Args:
            register (Register): The register.
        """

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

        if self.__sunpos_enabled_value != register.value:
            self.__sunpos_enabled_value = register.value

    def __location_lat_cb(self, register):
        """Get latitude of the building.

        Args:
            register (Register): The register.
        """

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

        if self.__location_lat != register.value:
            self.__location_lat = register.value

    def __location_lon_cb(self, register):
        """Get longitude of the building.

        Args:
            register (Register): The register.
        """

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

        if self.__location_lon != register.value:
            self.__location_lon = register.value

    def __location_elv_cb(self, register):
        """Get elevation of the building.

        Args:
            register (Register): The register.
        """

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

        if self.__location_elv != register.value:
            self.__location_elv = register.value

    def __time_zone_cb(self, register):
        """Get time zone.

        Args:
            register (Register): The register.
        """

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

        if self.__time_zone != register.value:
            self.__time_zone = register.value

    def __temperature_cb(self, register):
        """Get environment temperature.

        Args:
            register (Register): The register.
        """

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

        if self.__temperature != register.value:
            self.__temperature = register.value

    def __init_registers(self):

        # Software sun position enabled.
        # sunpos_enabled = self._registers.by_name(self.key + ".sunpos.enabled")
        sunpos_enabled = self._registers.by_name("envm.sunpos.enabled")
        if sunpos_enabled is not None:
            sunpos_enabled.update_handlers = self.__sunpos_enabled_cb
            sunpos_enabled.update()

        location_lat = self._registers.by_name("envm.building.location.lat")
        if location_lat is not None:
            location_lat.update_handlers = self.__location_lat_cb
            location_lat.update()

        location_lon = self._registers.by_name("envm.building.location.lon")
        if location_lon is not None:
            location_lon.update_handlers = self.__location_lon_cb
            location_lon.update()

        location_elv = self._registers.by_name("envm.building.location.elv")
        if location_elv is not None:
            location_elv.update_handlers = self.__location_elv_cb
            location_elv.update()

        time_zone = self._registers.by_name("envm.building.location.time_zone")
        if time_zone is not None:
            time_zone.update_handlers = self.__time_zone_cb
            time_zone.update()

        temperature = self._registers.by_name("envm.temp.actual")
        if temperature is not None:
            temperature.update_handlers = self.__temperature_cb
            temperature.update()

    def __set_sunpos(self):
        """Set sun position.
        """

        self._registers.write("envm.sun.elevation", self.__elevation)
        self._registers.write("envm.sun.azimuth", self.__azimuth)

#endregion

#region Private Methods

    def __calculate_position(self):
        """Calculate sun position.
        """

        # https://www.suncalc.org/#/43.0781,25.5955,17/2021.05.07/11:09/1/1

        # Latitude of the target.
        lat = self.__location_lat

        # Longitude of the target.
        lon = self.__location_lon

        # Elevation, in meters.
        # It is formed by the sum of altitude in [m] + height of the object (building) in [m]
        elv = self.__location_elv

        # Temperature, in degrees celsius.
        temp = self.__temperature

        # Atmospheric pressure, in millibar.
        presure = 1013.0

        # Difference between earth\'s rotation time (TT) and universal time (UT1).
        diff_time = 3600 * self.__time_zone

        # Output in radians instead of degrees.
        mou = False

        # Get sun position.
        time_now = datetime.now()

        azm, zen, ra, dec, h = sunpos(time_now, lat, lon, elv, temp, presure,
                                      diff_time, mou)
        elv_out = 90 - zen - 2
        azm_out = azm

        # self.__logger.info(f"Azimuth: {azm:.2f}; Elevation: {elv:.2f}")
        # print(f"SunPos -> Azm: {azm_out:.2f}; Elev: {elv_out:.2f}")

        # Update sun location.
        self.__azimuth = azm_out
        self.__elevation = elv_out

#endregion

#region Public Methods

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

        self.__logger = get_logger(__name__)
        self.__logger.info("Starting up the {}".format(self.name))

        self.__update_timer = Timer(2)

        self.__init_registers()

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

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

            if self.__sunpos_enabled_value:
                self.__calculate_position()
                self.__set_sunpos()

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

        self.__logger.info("Shutting down the {}".format(self.name))
Esempio n. 11
0
File: modv1.py Progetto: 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)
Esempio n. 12
0
class Light(BasePlugin):
    """Light controller plugin."""

    #region Attributes

    __logger = None
    """Logger
    """

    __update_timer = None
    """Update timer.
    """

    __light_sensor = None
    """Light sensor.
    """

    __v1_output = verbal_const.OFF
    """Analog voltage output 1.
    """

    __v2_output = verbal_const.OFF
    """Analog voltage output 2.
    """

    __target_illumination = 50.0
    """Target illumination. [Lux]
    """

    __error_gain = 0.001
    """Gain of the error. This parameter is the smoothness of the curve.
    """

    __output_limit = 10000
    """Illumination force limit. [V]
    """

    __tmp_output = 0
    """Temporary output. [V]
    """

    __output = 0
    """Main output. [V]
    """

    #endregion

    #region Private Methods (Registers Interface)

    def __error_gain_cb(self, register):

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

        if self.__error_gain != register.value:
            self.__error_gain = register.value

    def __sensor_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.__light_sensor is None:

            self.__light_sensor = LightSensorFactory.create(
                controller=self._controller,
                name="Room light sensor.",
                vendor=register.value['vendor'],
                model=register.value['model'],
                options=register.value['options'])

            if self.__light_sensor is not None:
                self.__light_sensor.init()

        elif register.value == {} and self.__light_sensor is not None:
            self.__light_sensor.shutdown()
            del self.__light_sensor

    def __v1_output_cb(self, register):

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

        self.__v1_output = register.value

    def __v2_output_cb(self, register):

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

        self.__v2_output = register.value

    def __target_illum_cb(self, register):

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

        if self.__target_illumination != register.value:
            self.__target_illumination = register.value

    def __init_registers(self):

        sensor_enabled = self._registers.by_name(self.key + ".sensor.settings")
        if sensor_enabled is not None:
            sensor_enabled.update_handlers = self.__sensor_settings_cb
            sensor_enabled.update()

        v1_output = self._registers.by_name(self.key + ".v1.output")
        if v1_output is not None:
            v1_output.update_handlers = self.__v1_output_cb
            v1_output.update()

        v2_output = self._registers.by_name(self.key + ".v2.output")
        if v2_output is not None:
            v2_output.update_handlers = self.__v2_output_cb
            v2_output.update()

        error_gain = self._registers.by_name(self.key + ".error_gain")
        if error_gain is not None:
            error_gain.update_handlers = self.__error_gain_cb
            error_gain.update()

        target_illum = self._registers.by_name(self.key + ".target_illum")
        if target_illum is not None:
            target_illum.update_handlers = self.__target_illum_cb
            target_illum.update()

    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

#endregion

#region Private Methods (Controller Interface)

    def __set_voltages(self, voltage_1, voltage_2):
        """Set the voltage outputs.

        Parameters
        ----------
        voltage_1 : float
            Voltage 1.
        voltage_2 : float
            Voltage 2.
        """

        value_v1 = voltage_1

        if value_v1 > 10:
            value_v1 = 10

        if value_v1 < 0:
            value_v1 = 0

        value_v2 = voltage_2

        if value_v2 > 10:
            value_v2 = 10

        if value_v2 < 0:
            value_v2 = 0

        if self._controller.is_valid_gpio(self.__v1_output):
            self._controller.analog_write(self.__v1_output, value_v1)

        if self._controller.is_valid_gpio(self.__v2_output):
            self._controller.analog_write(self.__v2_output, value_v2)

#endregion

#region Private Methods

    def __flood_fade(self, setpoint):
        """Flood fade generator.

        Args:
            setpoint (float): Setpoint for the hardware.

        Returns:
            list: Output voltages for the two analog outputs.
        """

        # Negative limit.
        if setpoint < 0:
            setpoint = 0

        # Positive limit.
        if setpoint > 100:
            setpoint = 100

        voltage_1 = 0
        voltage_2 = 0

        # The model.
        if setpoint <= 50:
            voltage_1 = l_scale(setpoint, [0, 50], [0, 100])
            voltage_2 = 0
        else:
            voltage_1 = 100
            voltage_2 = l_scale(setpoint, [50, 100], [0, 100])

        # Return the voltages.
        return (voltage_1, voltage_2)

    def __calculate(self):
        """ Apply thermal force to the devices. """

        current_illumination = 0
        target_illumination = 0
        delta = 0
        error = 0

        # If there is no one at the zone, just turn off the lights.
        is_empty = self.__is_empty()

        # Scale t
        target_illumination = l_scale(self.__target_illumination,
                                      [0.0, self.__output_limit], [0.0, 100.0])

        # Read sensor.
        if self.__light_sensor is not None:
            current_illumination = self.__light_sensor.get_value()
            current_illumination = l_scale(current_illumination,
                                           [0.0, self.__output_limit],
                                           [0.0, 100.0])

        # Limits
        lower_limit = 0.0
        mid_1 = 20.0
        mid_2 = 80.0
        upper_limit = 100.0

        # Check the limits.
        if lower_limit <= target_illumination <= mid_1:
            self.__tmp_output = (target_illumination / mid_1) * 20.0

        elif mid_1 < target_illumination < mid_2:

            # Apply the formula.
            first = current_illumination
            second = (1 + ((50.0 - target_illumination) / 100.0))
            calculated_value = first * second

            # Calculate the target error.
            error = calculated_value - current_illumination

            # Integrate the delta to temporal output.
            delta = error * self.__error_gain
            self.__tmp_output += delta

        elif mid_2 <= target_illumination <= upper_limit:
            self.__tmp_output = (target_illumination / upper_limit) * 100.

        # Limitate the output by target value.
        if self.__tmp_output > abs(self.__output_limit):
            self.__tmp_output = self.__output_limit

        # Limitate by absolute maximum and minimum.
        if self.__tmp_output < 0:
            self.__tmp_output = 0
        if self.__tmp_output > 100:
            self.__tmp_output = 100

        # Apply the output if it is different.
        self.__output = self.__tmp_output

        # Convert to volgate.
        to_voltage_scale = 0.1  # Magic number!!!
        voltage_1, voltage_2 = self.__flood_fade(self.__output)
        out_to_v1 = voltage_1 * to_voltage_scale
        out_to_v2 = voltage_2 * to_voltage_scale

        # If the zone is empty, turn the lights off.
        if is_empty:
            pass

        # set the voltage.
        self.__set_voltages(out_to_v1, out_to_v2)

        self.__logger.debug("TRG {:3.3f}\tINP {:3.3f}\tERR: {:3.3f}\tOUT1: {:3.3f}\tOUT2: {:3.3f}"\
            .format(target_illumination, current_illumination, delta, out_to_v1, out_to_v2))

#endregion

#region Public Methods

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

        self.__logger = get_logger(__name__)
        self.__logger.info("Starting up the {}".format(self.name))

        self.__update_timer = Timer(1)

        self.__init_registers()

        self.__set_voltages(0, 0)

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

        # Update sensor data.
        self.__light_sensor.update()

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

            self.__calculate()

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

        self.__logger.info("Shutting down the {}".format(self.name))
        self.__set_voltages(0, 0)
Esempio n. 13
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
Esempio n. 14
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)
Esempio n. 15
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)
Esempio n. 16
0
class AirChambers(BasePlugin):
    """Air chambers controller."""

    #region Attributes

    __logger = None
    """Logger"""

    __update_timer = None
    """Update timer."""

    #endregion

    #region Attributes

    __logger = None
    """Logger"""

    __update_timer = None
    """Update timer."""

    #endregion

    #region Private Methods (Registers Interface)

    # def __visual_device_settings_cb(self, register: Register):

    #     self.__logger.debug("Init the visual device.")

    def __init_registers(self):

        # visual_device_settings = self._registers.by_name("{}.device.visual.settings".format(self.key))
        # if visual_device_settings is not None:
        #     visual_device_settings.update_handlers = self.__visual_device_settings_cb
        #     visual_device_settings.update()

        pass

#endregion

#region Private Methods

    def __do_job(self):
        """Do the JOB method."""

        self.__logger.info("Do some job.")

#endregion

#region Protected Methods

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

        self.__logger = get_logger(__name__)
        self.__logger.info("Starting up the {}".format(self.name))

        self.__update_timer = Timer(1)

        self.__init_registers()

        # TODO: Check is it necessary to create a air chamber abstraction. Yes it is necessary.
        # TODO: Create tcp modbus master.

    def _update(self):
        """Update the plugin.
        """
        # Update the timer.
        self.__update_timer.update()

        if self.__update_timer.expired:

            self.__update_timer.clear()

            self.__do_job()

            # TODO: Pull the data from the chambers.

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

        self.__logger.info("Shutting down the {}".format(self.name))