def setUpClass(self):
        point_temperature_1 = PointAnalog(
            description="Temperature reading 1",
            u_of_m="ºC",
            hmi_writeable=False,
            update_period=1.0,
        )
        point_temperature_1.value = 5.0

        PointManager().add_to_database(name="temp_1", obj=point_temperature_1)

        point_temperature_2 = PointAnalog(
            description="Temperature reading 2",
            u_of_m="ºC",
            hmi_writeable=False,
            update_period=1.0,
        )
        PointManager().add_to_database(name="temp_2", obj=point_temperature_1)
        point_temperature_2.value = 5.1

        self.point = PointAnalogDual(
            description="Temperature reading",
            point_1=point_temperature_1,
            point_2=point_temperature_2,
        )
        PointManager().add_to_database(name="temp", obj=self.point)
    def test_yaml_pickle(self):
        s = PointManager().dump_database_to_yaml()
        # print (f"YAML:\n {s}")
        PointManager().clear_database
        PointManager().load_points_from_yaml_string(s)

        PointManager().find_point(test_process_point_name)
    def test_unscaling(self):
        point_analog = \
            PointManager().find_point("setpoint").readonly_object
        point_scaled = \
            PointManager().find_point("setpoint_scaled").readwrite_object

        point_scaled.value = 44.0
        self.assertEqual(point_analog.value, 87.0)
    def test_scaling(self):
        point_analog = \
          PointManager().find_point("temperature_source").readwrite_object
        point_scaled = \
          PointManager().find_point("test_scaled_point").readonly_object

        point_analog.value = 44.0
        print(point_scaled.value)
        self.assertEqual(point_scaled.value, 22.5)
    def test_yaml_pickle(self):
        point = PointManager().find_point("test_scaled_point")
        s = PointManager().dump_database_to_yaml()
        print(f"YAML:\n {s}")
        PointManager().clear_database
        PointManager().load_points_from_yaml_string(s)
        unpickled_point = PointManager().find_point("test_scaled_point")

        self.assertEqual(point.scaling, unpickled_point.scaling)
        self.assertEqual(point.offset, unpickled_point.offset)
예제 #6
0
def test_cmd_05_fail(modbus_server):
    ''' Test force COIL command.
    Force COIL 4 and verify that the server returns that the
    command is invalid.

    '''
    pd1 = PointManager().get_point_test('point_discrete_1')
예제 #7
0
def modbus_server():

    yml = ruamel.yaml.YAML(typ='safe', pure=True)
    yml.default_flow_style = False
    yml.indent(sequence=4, offset=2)
    cfg = None

    filename = os.path.join(
      os.path.dirname(__file__),
      './server_test_logic.yaml',
    )
    with open(filename, 'r') as ymlfile:
        cfg = yml.load(ymlfile)

    logger = 'testbench'

    server = ModbusServer(
        name="Testbench Modbus Server",
        logger=logger,
    )

    filename = os.path.join(
      os.path.dirname(__file__),
      "./test_points.yaml",
    )
    PointManager().load_points_from_yaml_file(filename)

    parameters = {
      'endpoint_address': 'localhost',
      'port': 10587,
      'socket_timeout': 100,
    }

    PointManager().assign_parameters(
        data = parameters,
        target = server,
    )

    PointManager().assign_points(
        data = cfg,
        point_handler = server,
        target_name = 'Modbus Server',
        interruptable = server,
    )

    return server
    def config(self, config: dict):
        self.logger.debug("Entering function")

        for device in config:
            self.logger.info("I2C: attempting to import " + device)
            imported_module = import_module(config[device]["module"],
                                            config[device]["package"])

            for i in dir(imported_module):
                attribute = getattr(imported_module, i)
                if inspect.isclass(attribute) \
                  and issubclass(attribute, i2cPrototype) \
                  and attribute != i2cPrototype:

                    device_instance = attribute(
                        name=device,
                        logger=config[device]["logger"],
                        bus=int(self.bus))

                    # Assign points to the module.
                    PointManager().assign_points(
                        data=config[device],
                        point_handler=device_instance,
                        target_name=device,
                        interruptable=self,
                    )

                    # Populate module parameters.
                    PointManager().assign_parameters(
                        target=device_instance,
                        data=config[device],
                    )

                    device_instance.config()
                    self.devices.append(device_instance)

        for d in self.devices:
            for p in d.interrupt_points:
                p.add_observer(self.name, self)

        self.current_read_device = self.devices[0]
    def test_a_yaml_pickle(self):
        s = PointManager().dump_database_to_yaml()
        PointManager().clear_database()
        print(f"YAML:\n {s}")
        PointManager().load_points_from_yaml_string(s)
        unpickled_point = PointManager().find_point("pump_run")

        self.assertEqual(
            self.point.description,
            unpickled_point.description,
        )
        self.assertEqual(
            self.point.requestable,
            unpickled_point.requestable,
        )

        self.assertEqual(
            self.point.retentive,
            unpickled_point.retentive,
        )

        self.assertEqual(
            self.point.hmi_writeable,
            unpickled_point.hmi_writeable,
        )

        self.assertEqual(
            self.point.update_period,
            unpickled_point.update_period,
        )

        self.assertEqual(self.point.on_state_description,
                         unpickled_point.on_state_description)

        self.assertEqual(self.point.off_state_description,
                         unpickled_point.off_state_description)

        self.assertEqual(
            self.point.value,
            unpickled_point.value,
        )
    def test_yaml_pickle(self):
        s = PointManager().dump_database_to_yaml()
        PointManager().clear_database
        PointManager().load_points_from_yaml_string(s)

        unpickled_point = PointManager().find_point("temp")

        self.assertEqual(
            self.point,
            unpickled_point,
        )

        self.assertEqual(
            self.point._point_1,
            unpickled_point._point_1,
        )

        self.assertEqual(
            self.point._point_2,
            unpickled_point._point_2,
        )
    def setUpClass(self):
        # Build the PointAnalog
        self.point_analog = PointAnalog(
            description="Temperature reading",
            u_of_m="ºC",
            hmi_writeable=False,
            retentive=True,
            update_period=1.0,
        )

        PointManager().add_to_database(
            name="temp_1",
            obj=self.point_analog,
        )

        # Build the PointDiscrete
        self.point_discrete = PointDiscrete(
            description="run cooling",
            hmi_writeable=False,
        )

        PointManager().add_to_database(
            name="run_cooling",
            obj=self.point_discrete,
        )

        # Build the PointEnumeration
        self.point_enumeration = PointEnumeration(
            description="System mode",
            states=["Hand", "Off", "Auto"],
            hmi_writeable=False,
        )

        PointManager().add_to_database(
            name="system_mode",
            obj=self.point_enumeration,
        )
 def setUpClass(self):
     self.point = PointDiscrete(
         description="Pump run",
         on_state_description="Running",
         off_state_description="Stopped",
         hmi_writeable=True,
         requestable=True,
         retentive=True,
         update_period=20.0,
     )
     self.point.value = True
     PointManager().add_to_database(
         name="pump_run",
         obj=self.point,
     )
    def setUpClass(self):
        point = PointAnalog(
            description="Temperature reading 1 (pressure sensor)",
            u_of_m="ºC",
            update_period=1.2,
        )
        PointManager.add_to_database(
            name="temperature_source",
            obj=point,
        )

        point = PointAnalogScaled(
            scaling=2.0,
            offset=-1.0,
            point=point,
            readonly=True,
        )

        PointManager.add_to_database(
            name="test_scaled_point",
            obj=point,
        )

        point = PointAnalog(
            description="Temperature Setpoint",
            u_of_m="ºC",
            update_period=None,
        )

        PointManager.add_to_database(
            name="setpoint",
            obj=point,
        )

        point = PointAnalogScaled(
            scaling=2.0,
            offset=-1.0,
            point=point,
            readonly=False,
        )

        PointManager.add_to_database(
            name="setpoint_scaled",
            obj=point,
        )
예제 #14
0
def test_cmd_01(modbus_server):
    ''' Test read COIL status command.
    Read 3 coils, starting at address 3. Note that only coils 3 and 5 are
    populated. The routine should return a 0 bit for the unpopulated coil.
    '''

    pd1 = PointManager().get_point_test('point_discrete_1')
    pd2 = PointManager().get_point_test('point_discrete_2')
    pd1.value = True
    pd2.value = True

    data_array = []

    # MBAP header
    # transaction ID = 0x12 0x34
    # protocol identifier = 0x00 0x00
    # length = 0x00 0x06
    # Unit identifier = 01
    data_array = [0x12, 0x34, 0x00, 0x00, 0x00, 0x06, 0x01]  # MBAP header

    # read 3 coils starting at address 03
    # command = 0x01
    # starting address = 0x00 0x03
    # coils to read = 0x00 0x03
    data_array += [0x01, 0x00, 0x03, 0x00, 0x03]

    data = bytearray(data_array)
    del data_array

    return_data = modbus_server.process_request(data)
    del data

    # return command 1, one byte, both discretes on
    expected_responce = [0x12, 0x34, 0x00, 0x00, 0x00, 0x04, 0x01]  # MBAP
    expected_responce += [0x01, 0x01, 0x05]
    expected_responce = bytearray(expected_responce)
    assert return_data == expected_responce
 def test_json_pickle(self):
     point = PointManager().find_point("setpoint_scaled").readwrite_object
     pickle_text = jsonpickle.encode(point)
     unpickled_point = jsonpickle.decode(pickle_text)
     self.assertEqual(point.scaling, unpickled_point.scaling)
     self.assertEqual(point.offset, unpickled_point.offset)
예제 #16
0
def test_cmd_05(modbus_server):
    ''' Test force COIL command.
    Force COIL 3 and verify that 3 changes and 5 doesn't.

    '''
    pd1 = PointManager().get_point_test('point_discrete_1')
    def __init__(self, logic_yaml_files: 'List[str]',
                 point_database_yaml_files: 'List[str]') -> 'None':

        yml = ruamel.yaml.YAML(typ='safe', pure=True)
        yml.default_flow_style = False
        yml.indent(sequence=4, offset=2)

        # open the supplied logic yaml file.
        for file in logic_yaml_files:
            with open(file, 'r') as ymlfile:
                cfg = yml.load(ymlfile)

        # Import all the loggers
        section = "loggers"
        for logger in cfg[section]:
            # self.logger.info("attempting to create logger: " + logger)
            # setup the device logger
            logger_obj = logging.getLogger(logger)
            logger_obj.setLevel(cfg[section][logger]['level'])
            fh = RotatingFileHandler(
                filename=cfg[section][logger]['file'],
                maxBytes=cfg[section][logger]['maxBytes'],
                backupCount=cfg[section][logger]['backupCount'],
                encoding=None,
                delay=False,
            )
            fh.setFormatter(formatter)
            logger_obj.addHandler(fh)

        self.logger = logging.getLogger('supervisory')
        assert self.logger is not None, \
            "The logger didn't assign properly"
        PointManager.logger = self.logger

        # load the point database(s).
        for file in point_database_yaml_files:
            self.logger.info(f"loading file: {file}")
            PointManager().load_points_from_yaml_file(file)

        # Setup the alarm notifiers.
        section = "AlarmNotifiers"
        for notifier in cfg[section]:
            self.logger.info(f"attempting import AlarmNotifier {notifier}")

            imported_module = import_module(cfg[section][notifier]["module"],
                                            cfg[section][notifier]["package"])

            assert 'logger' in cfg[section][notifier], \
                f"No logger entry defined for {notifier}"
            logger = cfg[section][notifier]["logger"]

            for i in dir(imported_module):
                attribute = getattr(imported_module, i)

                # Search the file for an AlarmNotifier object.
                if inspect.isclass(attribute) \
                  and issubclass(attribute, AlarmNotifier) \
                  and attribute != AlarmNotifier:

                    concrete_notifier = attribute(name=notifier, logger=logger)
                    self.logger.info(
                        f"adding {imported_module.__name__} {attribute} to "
                        "alarm notifiers list")

                    Alarm.alarm_notifiers.append(concrete_notifier)

                    # Populate module assign_parameters
                    PointManager().assign_parameters(
                        data=cfg[section][notifier],
                        target=concrete_notifier,
                    )

        self.logger.info("Starting Supervisor")

        # Create the signleton alarm handler thread.
        # The alarm handler is not an option.
        self.alarm_handler = AlarmHandler(
            name="alarm processer",
            logger="supervisory",
        )

        Alarm.alarm_handler = self.alarm_handler
        self.threads.append(self.alarm_handler)

        # Create all of the custom threads.
        section = 'SupervisedThreads'
        for thread_name in cfg[section]:
            self.logger.info(
                f"attempting to import {cfg[section][thread_name]['module']}")

            imported_module = import_module(
                cfg[section][thread_name]["module"],
                cfg[section][thread_name]["package"])

            for i in dir(imported_module):
                attribute = getattr(imported_module, i)

                # Search the file for the SupervisedThread object.
                if    inspect.isclass(attribute) \
                  and issubclass(attribute, SupervisedThread) \
                  and attribute != SupervisedThread:
                    # Everything looks valid,
                    # import the instansiate the module.

                    supervised_thread = attribute(
                        name=thread_name,
                        logger=cfg[section][thread_name]["logger"])

                    self.logger.info(f"added {thread_name} {attribute}")

                    # Populate module points
                    PointManager().assign_points(
                        data=cfg[section][thread_name],
                        point_handler=supervised_thread,
                        target_name=thread_name,
                        interruptable=supervised_thread,
                    )

                    # Populate module assign_parameters
                    PointManager().assign_parameters(cfg[section][thread_name],
                                                     supervised_thread)

                    # Now that the points and parameters are assigned. Run any
                    # remaining configuration
                    # config is free form specific to the device where required.
                    if "config" in cfg[section][thread_name]:
                        config = cfg[section][thread_name]['config']
                    else:
                        config = None

                    supervised_thread.config(config=config)

                    # Append the fully built module to the threads dict.
                    self.threads.append(supervised_thread)

        # Fire up all the threads.
        for thread in self.threads:
            self.logger.info(f"starting: {thread.name}")
            thread.start()

        # Start the point database server
        rpc_object = RpcServer()
        rpc_object.active_alarm_list = self.alarm_handler.active_alarm_list
        rpc_object.global_alarm_list = PointManager().global_alarms()
        rpc_object.point_dict = PointManager().global_points()
        rpc_object.thread_list = self.threads
        rpc_object.get_hmi_point = PointManager().get_hmi_point

        self.rpc_server = ThreadedServer(
            rpc_object,
            port=18861,
            logger=self.logger,
            protocol_config={"allow_public_attrs": True})

        self.rpc_server_thread = threading.Thread(target=self.rpc_server.start)
        self.rpc_server_thread.start()

        self.logger.info("Completed Supervisor setup")
    def setUpClass(self):

        point_pump_water_pressure = PointAnalog(
            description="Pump water pressure",
            u_of_m="psi",
            hmi_writeable=False,
            update_period=0.333333,
            value=123.45,
        )

        # PointManager.add_to_database(
        #   name = "pump_water_pressure",
        #   obj = point_pump_water_pressure,
        # )

        # point_pump_water_pressure.value = 123.45

        point_pump_on_water_pressure = PointAnalog(
            description="Pump water pressure on setpoint",
            u_of_m="psi",
            hmi_writeable=True,
            value=115.0,
        )

        # PointManager.add_to_database(
        #   name = "pump_on_water_pressure",
        #   obj = point_pump_on_water_pressure,
        # )

        point_pump_off_water_pressure = PointAnalog(
            description="Pump water pressure off setpoint",
            u_of_m="psi",
            hmi_writeable=True,
            value=125.0,
        )

        # PointManager.add_to_database(
        #   name = "pump_off_water_pressure",
        #   obj = point_pump_off_water_pressure,
        # )

        alarm_h1_pump_water_pressure = AlarmAnalog(
            description="Pump pressure H1",
            alarm_value=128.0,
            on_delay=1.0,
            off_delay=1.0,
            hysteresis=1.0,
            high_low_limit="HIGH",
            consequences="The pump has been temporarily shut off",
            more_info=
            "The pressure setpoint of the system has probably been set beyond "
            "the H1 level. The H1 level provides a safety mechanism and "
            "prevents the pump from overpressuring the system.",
        )

        # PointManager.add_to_database(
        #   name = "h1_pump_water_pressure",
        #   obj = alarm_h1_pump_water_pressure,
        # )

        alarm_h2_pump_water_pressure = AlarmAnalog(
            description="Pump pressure H2",
            alarm_value=132.0,
            on_delay=0.0,
            off_delay=1.0,
            hysteresis=1.0,
            high_low_limit="HIGH",
            consequences=
            "The system will attempt to bleed all pressure. The charge and "
            "relief valves will be opened until this  alarm is acknowledged.",
            more_info=
            "The pump relay is likely stuck closed. The system pressure will "
            "be bled to prevent overpressure and prevent thermal failure of the"
            "pump by allowing water to circulate.",
        )

        # PointManager.add_to_database(
        #   name = "h2_pump_water_pressure",
        #   obj = alarm_h2_pump_water_pressure,
        # )

        alarm_l1_pump_water_pressure = AlarmAnalog(
            description="Pump pressure L1",
            alarm_value=113.0,
            on_delay=2.0,
            off_delay=1.0,
            hysteresis=1.0,
            high_low_limit="LOW",
            consequences="Feed system is will not satisfy demand",
            more_info="The pump may have failed or a leak may have developed on "
            "the high pressure side",
        )

        # PointManager.add_to_database(
        #   name = "l1_pump_water_pressure",
        #   obj = alarm_l1_pump_water_pressure,
        # )

        alarm_l2_pump_water_pressure = AlarmAnalog(
            description="Pump pressure L2",
            alarm_value=40.0,
            on_delay=5.0,
            off_delay=5.0,
            hysteresis=1.0,
            high_low_limit="LOW",
            consequences="System will be shut down.",
            more_info="The pump may have failed or a leak may have developed on "
            "the high pressure side",
        )

        # PointManager.add_to_database(
        #   name = "l2_pump_water_pressure",
        #   obj = alarm_l2_pump_water_pressure,
        # )

        # Logic driven pump related alarms.
        alarm_pump_runtime_fault = Alarm(
            description="Pump runtime exceeded",
            on_delay=60.0,
            off_delay=1.0,
            consequences="The pump has been locked out until this alarm is "
            "acknowledged",
            more_info="The following may be happened: pump may be run dry, "
            "failing, or a leak may have developed.",
        )
        PointManager.add_to_database(
            name="pump_runtime_fault",
            obj=alarm_pump_runtime_fault,
        )

        # Active pressure decay constant. Measures how fast the pressure in the
        # accumlator tank drops when the valves are open. Too much indicates a
        # leak, not enough and you've got a clog.
        point_active_pressure_decay = PointAnalog(
            description="Active water pressure decay constant",
            u_of_m="%",
            hmi_writeable=False,
        )
        # PointManager.add_to_database(
        #   name = "active_pressure_decay",
        #   obj = point_active_pressure_decay,
        # )

        alarm_h1_active_pressure_decay = AlarmAnalog(
            description="Pressure decay high - misting clog detected",
            alarm_value=0.98,
            hysteresis=0.01,
            on_delay=180,
            off_delay=180,
            high_low_limit="HIGH",
            consequences="None",
            more_info=
            "The system has detected that the pressure accumulator is " +
            "not draing at the proper rate.",
        )
        # PointManager.add_to_database(
        #   name = "active_pressure_decay",
        #   obj = alarm_h1_active_pressure_decay,
        # )

        alarm_l1_active_pressure_decay = AlarmAnalog(
            description="Pressure decay low - misting leak",
            alarm_value=0.95,
            hysteresis=0.01,
            on_delay=180,
            off_delay=180,
            high_low_limit="LOW",
            consequences="None",
            more_info="The system has detected that the pressure accumulator is "
            "not draining at the proper rate.")
        # PointManager.add_to_database(
        #   name = "l1_active_pressure_decay",
        #   obj = alarm_l1_active_pressure_decay,
        # )

        process_active_pressure_decay = \
          ProcessValue(point_active_pressure_decay)
        process_active_pressure_decay.high_display_limit = 1.0
        process_active_pressure_decay.low_display_limit = 0.0

        process_active_pressure_decay.add_alarm(
            "H1", alarm_h1_active_pressure_decay)

        process_active_pressure_decay.add_alarm(
            "L1", alarm_l1_active_pressure_decay)

        # Static pressure decay constant, Measure how much the system leaks when
        # the pump is off and valves are closed. Too much decay and there's a
        # leak.
        point_static_pressure_decay = PointAnalog(
            description="Static water pressure decay constant",
            u_of_m="%",
            hmi_writeable=False,
        )

        # PointManager.add_to_database(
        #   name = "static_pressure_decay",
        #   obj = point_static_pressure_decay,
        # )

        alarm_l1_static_pressure_decay = AlarmAnalog(
            description="Pressure decay low - accumulator leak",
            alarm_value=0.99,
            hysteresis=0.01,
            on_delay=180,
            off_delay=180,
            high_low_limit="LOW",
        )

        # PointManager.add_to_database(
        #   name = "l1_static_pressure_decay",
        #   obj = alarm_l1_static_pressure_decay,
        # )

        process_static_pressure_decay = ProcessValue(
            point_static_pressure_decay)
        process_static_pressure_decay.high_display_limit = 1.0
        process_static_pressure_decay.low_display_limit = 0.0
        process_static_pressure_decay.add_alarm(
            "L1", alarm_l1_static_pressure_decay)

        # PointManager.add_to_database(
        #   name = "static_pressure_decay",
        #   obj = process_static_pressure_decay,
        # )

        point_run_pump = PointDiscrete(
            description="Pump",
            on_state_description="On",
            off_state_description="Off",
            hmi_writeable=False,
        )
        # PointManager.add_to_database(
        #   name = "run_pump",
        #   obj = point_run_pump,
        # )

        # Pump ProcessValue assembly.
        self.point = ProcessValue(point_pump_water_pressure)
        self.point.high_display_limit = 135.0
        self.point.low_display_limit = 110.0
        self.point.add_control_point("cut_in", point_pump_on_water_pressure)
        self.point.add_control_point("cut_out", point_pump_off_water_pressure)
        self.point.add_related_point("run", point_run_pump)

        self.point.add_related_point(
            "decay_static",
            process_static_pressure_decay,
        )

        self.point.add_related_point(
            "decay_active",
            process_active_pressure_decay,
        )
        self.point.add_alarm("H1", alarm_h1_pump_water_pressure)
        self.point.add_alarm("H2", alarm_h2_pump_water_pressure)
        self.point.add_alarm("L1", alarm_l1_pump_water_pressure)
        self.point.add_alarm("L2", alarm_l2_pump_water_pressure)

        PointManager.add_to_database(
            name=test_process_point_name,
            obj=self.point,
        )