From 64b4225cb2e50a7becfe99d1a8638f2e0307b205 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Sun, 20 Jul 2025 17:56:14 +0200 Subject: [PATCH 01/61] changed imports to local version --- .gitignore | 2 + examples/virtual_pea_minimal.py | 34 ++++++---- examples/virtual_pea_with_recipe.py | 65 ++++++++++++------- readme.md | 14 ++-- src/mtppy/active_elements.py | 54 +++++++++------ src/mtppy/indicator_elements.py | 4 +- src/mtppy/mtp_generator.py | 23 ++++--- src/mtppy/opcua_server_pea.py | 24 ++++--- src/mtppy/operation_elements.py | 6 +- src/mtppy/operation_source_mode.py | 2 +- src/mtppy/procedure.py | 2 +- src/mtppy/procedure_control.py | 4 +- src/mtppy/service.py | 16 ++--- src/mtppy/state_machine.py | 15 +++-- src/mtppy/suc_data_assembly.py | 2 +- tests/test_ana_drv.py | 59 +++++++++++------ tests/test_ana_vlv.py | 29 ++++++--- tests/test_bin_drv.py | 56 ++++++++++------ tests/test_bin_vlv.py | 2 +- tests/test_mon_ana_drv.py | 59 +++++++++++------ tests/test_mon_ana_vlv.py | 11 ++-- tests/test_mon_bin_drv.py | 26 +++++--- tests/test_mon_bin_vlv.py | 8 ++- ...p_generator_instance_hierarchy_services.py | 36 ++++++---- tests/test_mtp_generator_instance_list.py | 39 ++++++----- tests/test_mtp_generator_structure.py | 11 ++-- tests/test_pid_ctrl.py | 8 ++- tests/test_procedure_control.py | 38 +++++++---- tests/test_state_machine.py | 15 +++-- 29 files changed, 420 insertions(+), 244 deletions(-) diff --git a/.gitignore b/.gitignore index 178135c..9683059 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /dist/ +__pycache__/ +~$* \ No newline at end of file diff --git a/examples/virtual_pea_minimal.py b/examples/virtual_pea_minimal.py index 8e9f797..3bfc9a9 100644 --- a/examples/virtual_pea_minimal.py +++ b/examples/virtual_pea_minimal.py @@ -1,10 +1,10 @@ -from mtppy.opcua_server_pea import OPCUAServerPEA -from mtppy.mtp_generator import MTPGenerator -from mtppy.service import Service -from mtppy.procedure import Procedure -from mtppy.operation_elements import * -from mtppy.indicator_elements import * -from mtppy.active_elements import * +from MTPPy_Async.src.mtppy.opcua_server_pea import OPCUAServerPEA +from MTPPy_Async.src.mtppy.mtp_generator import MTPGenerator +from MTPPy_Async.src.mtppy.service import Service +from MTPPy_Async.src.mtppy.procedure import Procedure +from MTPPy_Async.src.mtppy.operation_elements import * +from MTPPy_Async.src.mtppy.indicator_elements import * +from MTPPy_Async.src.mtppy.active_elements import * import time import random @@ -137,32 +137,44 @@ def resetting(self): opcua_server = module.get_opcua_server() opcua_ns = module.get_opcua_ns() time.sleep(1) + input('Press Enter to continue...') print('--- Set procedure parameters to Operator mode ---') - opcua_server.get_node('ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateOpOp').set_value(True) - opcua_server.get_node('ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateOpOp').set_value(True) + opcua_server.get_node( + 'ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateOpOp').set_value(True) + opcua_server.get_node( + 'ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateOpOp').set_value(True) time.sleep(1) + input('Press Enter to continue...') print('--- Set procedure parameter values ---') - opcua_server.get_node('ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VOp').set_value(40) - opcua_server.get_node('ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VOp').set_value(60) + opcua_server.get_node( + 'ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VOp').set_value(40) + opcua_server.get_node( + 'ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VOp').set_value(60) time.sleep(1) + input('Press Enter to continue...') print('--- Set service to Operator mode ---') opcua_server.get_node('ns=3;s=services.rand_num_gen.op_src_mode.StateOpOp').set_value(True) time.sleep(1) + input('Press Enter to continue...') print('--- Start service ---') opcua_server.get_node('ns=3;s=services.rand_num_gen.state_machine.CommandOp').set_value(4) time.sleep(10) + input('Press Enter to continue...') print('--- Complete service ---') opcua_server.get_node('ns=3;s=services.rand_num_gen.state_machine.CommandOp').set_value(1024) time.sleep(1) + input('Press Enter to continue...') print('--- Reset service ---') opcua_server.get_node('ns=3;s=services.rand_num_gen.state_machine.CommandOp').set_value(2) + input('Press Enter to continue...') print('--- Set service dummy to Offline mode ---') opcua_server.get_node('ns=3;s=services.rand_num_gen.op_src_mode.StateOffOp').set_value(True) time.sleep(1) + input('Press Enter to continue...') diff --git a/examples/virtual_pea_with_recipe.py b/examples/virtual_pea_with_recipe.py index 903fcd0..820ed61 100644 --- a/examples/virtual_pea_with_recipe.py +++ b/examples/virtual_pea_with_recipe.py @@ -1,13 +1,16 @@ -from mtppy.opcua_server_pea import OPCUAServerPEA -from mtppy.mtp_generator import MTPGenerator -from mtppy.service import Service -from mtppy.procedure import Procedure -from mtppy.operation_elements import * -from mtppy.indicator_elements import * +from MTPPy_Async.src.mtppy.opcua_server_pea import OPCUAServerPEA +from MTPPy_Async.src.mtppy.mtp_generator import MTPGenerator +from MTPPy_Async.src.mtppy.service import Service +from MTPPy_Async.src.mtppy.procedure import Procedure +from MTPPy_Async.src.mtppy.operation_elements import * +from MTPPy_Async.src.mtppy.indicator_elements import * import time import random from datetime import datetime +import logging + +logging.getLogger("opcua").setLevel(logging.ERROR) class ServiceDummy(Service): @@ -18,8 +21,10 @@ def __init__(self, tag_name, tag_description): def add_service_parameters(self): serv_parameters = [AnaServParam('serv_param_ana', v_min=0, v_max=50, v_scl_min=0, v_scl_max=10, v_unit=23), - DIntServParam('serv_param_dint', v_min=-10, v_max=10, v_scl_min=0, v_scl_max=-10, v_unit=23), - BinServParam('serv_param_bin', v_state_0='state_0', v_state_1='state_1'), + DIntServParam('serv_param_dint', v_min=-10, v_max=10, + v_scl_min=0, v_scl_max=-10, v_unit=23), + BinServParam('serv_param_bin', v_state_0='state_0', + v_state_1='state_1'), StringServParam('serv_param_str') ] [self.add_configuration_parameter(serv_param) for serv_param in serv_parameters] @@ -34,8 +39,10 @@ def add_procedures(self): # Procedure 3 proc_3 = Procedure(2, 'proc_3', is_self_completing=True, is_default=False) proc_parameters = [AnaServParam('proc_param_ana', v_min=0, v_max=50, v_scl_min=0, v_scl_max=10, v_unit=23), - DIntServParam('proc_param_dint', v_min=-10, v_max=10, v_scl_min=0, v_scl_max=-10, v_unit=23), - BinServParam('proc_param_bin', v_state_0='state_0', v_state_1='state_1'), + DIntServParam('proc_param_dint', v_min=-10, v_max=10, + v_scl_min=0, v_scl_max=-10, v_unit=23), + BinServParam('proc_param_bin', v_state_0='state_0', + v_state_1='state_1'), StringServParam('proc_param_str'), ] [proc_3.add_procedure_parameter(proc_param) for proc_param in proc_parameters] @@ -135,14 +142,14 @@ def resetting(self): if __name__ == '__main__': - writer_info_dict = {'WriterName': 'tud/plt', 'WriterID': 'tud/plt', 'WriterVendor': 'tud', - 'WriterVendorURL': 'www.tud.de', - 'WriterVersion': '1.0.0', 'WriterRelease': '', 'LastWritingDateTime': str(datetime.now()), - 'WriterProjectTitle': 'tu/plt/mtp', 'WriterProjectID': ''} - export_manifest_path = '../manifest_files/example_recipe_manifest.aml' - mtp_generator = MTPGenerator(writer_info_dict, export_manifest_path) + # writer_info_dict = {'WriterName': 'tud/plt', 'WriterID': 'tud/plt', 'WriterVendor': 'tud', + # 'WriterVendorURL': 'www.tud.de', + # 'WriterVersion': '1.0.0', 'WriterRelease': '', 'LastWritingDateTime': str(datetime.now()), + # 'WriterProjectTitle': 'tu/plt/mtp', 'WriterProjectID': ''} + # export_manifest_path = '../manifest_files/example_recipe_manifest.aml' + # mtp_generator = MTPGenerator(writer_info_dict, export_manifest_path) - module = OPCUAServerPEA(mtp_generator) + module = OPCUAServerPEA() # Service definition service_1 = ServiceDummy('dummy', 'description') @@ -153,10 +160,12 @@ def resetting(self): module.run_opcua_server() # Test + input('Press Enter to start test...') opcua_server = module.get_opcua_server() opcua_ns = module.get_opcua_ns() time.sleep(1) print('--- Set parameters of service dummy to Operator mode ---') + input('Press Enter to continue...') opcua_server.get_node( 'ns=3;s=services.dummy.configuration_parameters.serv_param_ana.op_src_mode.StateOpOp').set_value(True) opcua_server.get_node( @@ -169,36 +178,48 @@ def resetting(self): time.sleep(1) print('--- Set parameters VOp of service dummy to different values ---') - opcua_server.get_node('ns=3;s=services.dummy.configuration_parameters.serv_param_ana.VOp').set_value(10.54) - opcua_server.get_node('ns=3;s=services.dummy.configuration_parameters.serv_param_dint.VOp').set_value(-5.11) - opcua_server.get_node('ns=3;s=services.dummy.configuration_parameters.serv_param_bin.VOp').set_value(True) - opcua_server.get_node('ns=3;s=services.dummy.configuration_parameters.serv_param_str.VOp').set_value('hello there') + input('Press Enter to continue...') + opcua_server.get_node( + 'ns=3;s=services.dummy.configuration_parameters.serv_param_ana.VOp').set_value(10.54) + opcua_server.get_node( + 'ns=3;s=services.dummy.configuration_parameters.serv_param_dint.VOp').set_value(-5.11) + opcua_server.get_node( + 'ns=3;s=services.dummy.configuration_parameters.serv_param_bin.VOp').set_value(True) + opcua_server.get_node( + 'ns=3;s=services.dummy.configuration_parameters.serv_param_str.VOp').set_value('hello there') time.sleep(1) print('--- Change procedure to 2 ---') + input('Press Enter to continue...') opcua_server.get_node('ns=3;s=services.dummy.procedure_control.ProcedureOp').set_value(2) time.sleep(1) print('--- Set service dummy to Operator mode ---') + input('Press Enter to continue...') opcua_server.get_node('ns=3;s=services.dummy.op_src_mode.StateOpOp').set_value(True) time.sleep(2) print('--- Start service dummy ---') + input('Press Enter to continue...') opcua_server.get_node('ns=3;s=services.dummy.state_machine.CommandOp').set_value(4) - time.sleep(500) + time.sleep(5) print('--- Try to unhold service dummy ---') + input('Press Enter to continue...') opcua_server.get_node('ns=3;s=services.dummy.state_machine.CommandOp').set_value(32) time.sleep(2) print('--- Complete service dummy ---') + input('Press Enter to continue...') opcua_server.get_node('ns=3;s=services.dummy.state_machine.CommandOp').set_value(1024) time.sleep(1) print('--- Reset service dummy ---') + input('Press Enter to continue...') opcua_server.get_node('ns=3;s=services.dummy.state_machine.CommandOp').set_value(2) print('--- Set service dummy to Offline mode ---') + input('Press Enter to continue...') opcua_server.get_node('ns=3;s=services.dummy.op_src_mode.StateOffOp').set_value(True) time.sleep(1) diff --git a/readme.md b/readme.md index 458e3df..7eecb23 100644 --- a/readme.md +++ b/readme.md @@ -75,10 +75,10 @@ After definition of the service set, the MTP file can be generated and the insta ### Service definition ```python -from mtppy.service import Service -from mtppy.procedure import Procedure -from mtppy.operation_elements import * -from mtppy.indicator_elements import * +from MTPPy_Async.src.mtppy.service import Service +from MTPPy_Async.src.mtppy.procedure import Procedure +from MTPPy_Async.src.mtppy.operation_elements import * +from MTPPy_Async.src.mtppy.indicator_elements import * class RandomNumberGenerator(Service): def __init__(self, tag_name, tag_description): @@ -181,8 +181,8 @@ In additional, active elemements that are service-independent should be defined ### PEA definition Now, the service with its procedure can be added to the PEA instance. ```python -from mtppy.opcua_server_pea import OPCUAServerPEA -from mtppy.active_elements import PIDCtrl +from MTPPy_Async.src.mtppy.opcua_server_pea import OPCUAServerPEA +from MTPPy_Async.src.mtppy.active_elements import PIDCtrl module = OPCUAServerPEA() @@ -203,7 +203,7 @@ The last line will start the OPC UA server. ### MTP generation To generate an MTP manifest, instantiate an MTP generator. ```python -from mtppy.mtp_generator import MTPGenerator +from MTPPy_Async.src.mtppy.mtp_generator import MTPGenerator writer_info_dict = {'WriterName': 'tud/plt', 'WriterID': 'tud/plt', 'WriterVendor': 'tud', 'WriterVendorURL': 'www.tud.de', diff --git a/src/mtppy/active_elements.py b/src/mtppy/active_elements.py index 0f6b95f..c2c7c96 100644 --- a/src/mtppy/active_elements.py +++ b/src/mtppy/active_elements.py @@ -1,10 +1,10 @@ import logging import threading -from mtppy.attribute import Attribute +from MTPPy_Async.src.mtppy.attribute import Attribute -from mtppy.operation_source_mode import OperationSourceModeActiveElements -from mtppy.suc_data_assembly import SUCActiveElement +from MTPPy_Async.src.mtppy.operation_source_mode import OperationSourceModeActiveElements +from MTPPy_Async.src.mtppy.suc_data_assembly import SUCActiveElement from time import sleep from simple_pid import PID @@ -39,7 +39,8 @@ def __init__(self, tag_name, tag_description='', self._add_attribute(Attribute('SafePos', bool, init_value=safe_pos)) self._add_attribute(Attribute('SafePosEn', bool, init_value=self.safe_pos_en)) - self._add_attribute(Attribute('SafePosAct', bool, init_value=False)) # default value should be true? + self._add_attribute(Attribute('SafePosAct', bool, init_value=False) + ) # default value should be true? self._add_attribute(Attribute('OpenAut', bool, init_value=0, sub_cb=self.set_open_aut)) self._add_attribute(Attribute('CloseAut', bool, init_value=0, sub_cb=self.set_close_aut)) self._add_attribute(Attribute('OpenOp', bool, init_value=0, sub_cb=self.set_open_op)) @@ -246,7 +247,8 @@ def set_permit(self, value: bool): value = True self.attributes['Permit'].set_value(value) logging.debug('Permit set to %s' % value) - self.attributes['SafePosAct'].set_value(False) # safety position should not be activated for permit mode + # safety position should not be activated for permit mode + self.attributes['SafePosAct'].set_value(False) def set_interlock(self, value: bool): if not self.attributes['IntlEn'].value: @@ -394,7 +396,8 @@ def monitor_static_error(self): if self.monitored_values.stop_event_lock.is_set(): logging.debug('static monitoring stopped') break - states, control_signals = self.compare_states_control_signals(self.attributes['MonStatTi'].value) + states, control_signals = self.compare_states_control_signals( + self.attributes['MonStatTi'].value) if not all(states): # if the states of valve changed if all(control_signals): # but the control signals did not change @@ -410,7 +413,8 @@ def monitor_dynamic_error(self): if self.monitored_values.stop_event_lock.is_set(): logging.debug('dynamic monitoring stopped') break - states, control_signals = self.compare_states_control_signals(self.attributes['MonDynTi'].value) + states, control_signals = self.compare_states_control_signals( + self.attributes['MonDynTi'].value) if all(states): # if the states of valve did not changed if not all(control_signals): # but a control command is executed @@ -572,7 +576,8 @@ def __init__(self, tag_name: str, tag_description: str = '', open_fbk_calc: bool self._add_attribute(Attribute('SafePos', bool, init_value=safe_pos)) self._add_attribute(Attribute('SafePosEn', bool, init_value=self.safe_pos_en)) - self._add_attribute(Attribute('SafePosAct', bool, init_value=False)) # default value should be true? + self._add_attribute(Attribute('SafePosAct', bool, init_value=False) + ) # default value should be true? self._add_attribute(Attribute('OpenOp', bool, init_value=0, sub_cb=self.set_open_op)) self._add_attribute(Attribute('CloseOp', bool, init_value=0, sub_cb=self.set_close_op)) self._add_attribute(Attribute('OpenAut', bool, init_value=0, sub_cb=self.set_open_aut)) @@ -805,7 +810,8 @@ def monitor_static_error(self): if self.monitored_values.stop_event_lock.is_set(): logging.debug('static monitoring stopped') break - states, control_signals = self.compare_states_control_signals(self.attributes['MonStatTi'].value) + states, control_signals = self.compare_states_control_signals( + self.attributes['MonStatTi'].value) if not all(states): # if the states of valve changed if all(control_signals): # but the control signals did not change @@ -821,7 +827,8 @@ def monitor_dynamic_error(self): if self.monitored_values.stop_event_lock.is_set(): logging.debug('dynamic monitoring stopped') break - states, control_signals = self.compare_states_control_signals(self.attributes['MonDynTi'].value) + states, control_signals = self.compare_states_control_signals( + self.attributes['MonDynTi'].value) if all(states): # if the states of valve did not changed if not all(control_signals): # but a control command is executed @@ -933,7 +940,8 @@ def __init__(self, tag_name, tag_description='', rev_fbk_calc=True, fwd_fbk_calc self.prot_en = prot_en self._add_attribute(Attribute('SafePos', bool, init_value=safe_pos)) - self._add_attribute(Attribute('SafePosAct', bool, init_value=False)) # default value should be true? + self._add_attribute(Attribute('SafePosAct', bool, init_value=False) + ) # default value should be true? self._add_attribute(Attribute('FwdEn', bool, init_value=fwd_en)) self._add_attribute(Attribute('RevEn', bool, init_value=rev_en)) self._add_attribute(Attribute('StopOp', bool, init_value=0, sub_cb=self.set_stop_op)) @@ -1189,7 +1197,8 @@ def monitor_static_error(self): if self.monitored_values.stop_event_lock.is_set(): logging.debug('static monitoring stopped') break - states, control_signals = self.compare_states_control_signals(self.attributes['MonStatTi'].value) + states, control_signals = self.compare_states_control_signals( + self.attributes['MonStatTi'].value) if not all(states): if all(control_signals): @@ -1202,7 +1211,8 @@ def monitor_dynamic_error(self): if self.monitored_values.stop_event_lock.is_set(): logging.debug('dynamic monitoring stopped') break - states, control_signals = self.compare_states_control_signals(self.attributes['MonDynTi'].value) + states, control_signals = self.compare_states_control_signals( + self.attributes['MonDynTi'].value) if all(states): if not all(control_signals): @@ -1677,7 +1687,8 @@ def monitor_static_error(self): if self.monitored_values.stop_event_lock.is_set(): logging.debug('static monitoring stopped') break - states, control_signals = self.compare_states_control_signals(self.attributes['MonStatTi'].value) + states, control_signals = self.compare_states_control_signals( + self.attributes['MonStatTi'].value) if not all(states): if all(control_signals): @@ -1690,7 +1701,8 @@ def monitor_dynamic_error(self): if self.monitored_values.stop_event_lock.is_set(): logging.debug('dynamic monitoring stopped') break - states, control_signals = self.compare_states_control_signals(self.attributes['MonDynTi'].value) + states, control_signals = self.compare_states_control_signals( + self.attributes['MonDynTi'].value) if all(states): if not all(control_signals): @@ -1757,7 +1769,8 @@ def start_monitor(self): logging.debug('rpm error monitoring start') if self.attributes['RpmAHEn'].value: - self.monitor_rpm_limit_high_thread = threading.Thread(target=self.monitor_rpm_high_limit) + self.monitor_rpm_limit_high_thread = threading.Thread( + target=self.monitor_rpm_high_limit) self.monitor_rpm_limit_high_thread.start() logging.debug('rpm high limit monitoring start') @@ -1883,7 +1896,8 @@ def __init__(self, mv_init_value=0, kp=100, ki=10, kd=1, mv_min=0, mv_max=100, s self.mv = mv_init_value self.sample_time = sample_time - self.ctrl = PID(Kp=self.kp, Ki=self.ki, Kd=self.kd, output_limits=(mv_min, mv_max), sample_time=sample_time) + self.ctrl = PID(Kp=self.kp, Ki=self.ki, Kd=self.kd, + output_limits=(mv_min, mv_max), sample_time=sample_time) self.thread = threading.Thread(target=self.loop) self.stop_flag = True @@ -1972,8 +1986,10 @@ def __init__(self, tag_name, tag_description='', self._add_attribute(Attribute('PVSclMin', float, init_value=pv_scl_min)) self._add_attribute(Attribute('PVSclMax', float, init_value=pv_scl_max)) self._add_attribute(Attribute('PVUnit', int, init_value=pv_unit)) - self._add_attribute(Attribute('SPMan', float, init_value=sp_man_min, sub_cb=self.set_sp_man)) - self._add_attribute(Attribute('SPInt', float, init_value=sp_int_min, sub_cb=self.set_sp_int)) + self._add_attribute( + Attribute('SPMan', float, init_value=sp_man_min, sub_cb=self.set_sp_man)) + self._add_attribute( + Attribute('SPInt', float, init_value=sp_int_min, sub_cb=self.set_sp_int)) self._add_attribute(Attribute('SPSclMin', float, init_value=sp_scl_min)) self._add_attribute(Attribute('SPSclMax', float, init_value=sp_scl_max)) self._add_attribute(Attribute('SPUnit', int, init_value=sp_unit)) diff --git a/src/mtppy/indicator_elements.py b/src/mtppy/indicator_elements.py index dd03292..8dab9b2 100644 --- a/src/mtppy/indicator_elements.py +++ b/src/mtppy/indicator_elements.py @@ -1,7 +1,7 @@ import logging -from mtppy.attribute import Attribute -from mtppy.suc_data_assembly import SUCIndicatorElement +from MTPPy_Async.src.mtppy.attribute import Attribute +from MTPPy_Async.src.mtppy.suc_data_assembly import SUCIndicatorElement class AnaView(SUCIndicatorElement): diff --git a/src/mtppy/mtp_generator.py b/src/mtppy/mtp_generator.py index 5ec26ec..5bf659d 100644 --- a/src/mtppy/mtp_generator.py +++ b/src/mtppy/mtp_generator.py @@ -1,4 +1,4 @@ -from mtppy.suc_data_assembly import * +from MTPPy_Async.src.mtppy.suc_data_assembly import * import xml.etree.ElementTree as ET import random import string @@ -182,7 +182,8 @@ def add_components_to_services(self, data_assembly: SUCDataAssembly, sc_id: str, elif section == 'configuration_parameters': s_component = ET.SubElement(self.service, 'InternalElement') - s_component.set('RefBaseSystemUnitPath', 'MTPServiceSUCLib/ServiceParameter/ConfigurationParameter') + s_component.set('RefBaseSystemUnitPath', + 'MTPServiceSUCLib/ServiceParameter/ConfigurationParameter') elif section == 'procedures': s_component = ET.SubElement(self.service, 'InternalElement') @@ -208,19 +209,23 @@ def add_components_to_services(self, data_assembly: SUCDataAssembly, sc_id: str, elif section == 'procedure_parameters': s_component = ET.SubElement(self.service_procedure, 'InternalElement') - s_component.set('RefBaseSystemUnitPath', 'MTPServiceSUCLib/ServiceParameter/ProcedureParameter') + s_component.set('RefBaseSystemUnitPath', + 'MTPServiceSUCLib/ServiceParameter/ProcedureParameter') elif section == 'process_value_ins': s_component = ET.SubElement(self.service_procedure, 'InternalElement') - s_component.set('RefBaseSystemUnitPath', 'MTPServiceSUCLib/ServiceParameter/ProcessValueIn') + s_component.set('RefBaseSystemUnitPath', + 'MTPServiceSUCLib/ServiceParameter/ProcessValueIn') elif section == 'report_values': s_component = ET.SubElement(self.service_procedure, 'InternalElement') - s_component.set('RefBaseSystemUnitPath', 'MTPServiceSUCLib/ServiceParameter/ReportValue') + s_component.set('RefBaseSystemUnitPath', + 'MTPServiceSUCLib/ServiceParameter/ReportValue') elif section == 'process_value_outs': s_component = ET.SubElement(self.service_procedure, 'InternalElement') - s_component.set('RefBaseSystemUnitPath', 'MTPServiceSUCLib/ServiceParameter/ProcessValueOut') + s_component.set('RefBaseSystemUnitPath', + 'MTPServiceSUCLib/ServiceParameter/ProcessValueOut') else: raise TypeError('service components type error') @@ -318,7 +323,8 @@ def add_external_interface(self, opc_node_id: str, opc_ns: int, linked_attr_id: node_name = opc_node_id.split('=')[-1] elem = ET.SubElement(self.opcua_server, 'ExternalInterface') - self.generate_attributes(elem, node_name, linked_attr_id, 'MTPCommunicationICLib/DataItem/OPCUAItem') + self.generate_attributes(elem, node_name, linked_attr_id, + 'MTPCommunicationICLib/DataItem/OPCUAItem') # opc ua node attribute: identifier attr_identifier = ET.SubElement(elem, 'Attribute') @@ -361,7 +367,8 @@ def add_supported_role_class(self, parent: ET.Element): """ for internal_element in parent.findall('InternalElement'): SupportedRoleClass = ET.SubElement(internal_element, 'SupportedRoleClass') - SupportedRoleClass.set('RefRoleClassPath', 'AutomationMLBaseRoleClassLib/AutomationMLBaseRole') + SupportedRoleClass.set( + 'RefRoleClassPath', 'AutomationMLBaseRoleClassLib/AutomationMLBaseRole') if internal_element.findall('InternalElement'): self.add_supported_role_class(internal_element) diff --git a/src/mtppy/opcua_server_pea.py b/src/mtppy/opcua_server_pea.py index 883d409..33d69b7 100644 --- a/src/mtppy/opcua_server_pea.py +++ b/src/mtppy/opcua_server_pea.py @@ -1,13 +1,13 @@ import logging from opcua import Server, ua -from mtppy.communication_object import OPCUACommunicationObject -from mtppy.service import Service -from mtppy.suc_data_assembly import SUCDataAssembly, SUCActiveElement -from mtppy.mtp_generator import MTPGenerator +from MTPPy_Async.src.mtppy.communication_object import OPCUACommunicationObject +from MTPPy_Async.src.mtppy.service import Service +from MTPPy_Async.src.mtppy.suc_data_assembly import SUCDataAssembly, SUCActiveElement +from MTPPy_Async.src.mtppy.mtp_generator import MTPGenerator class OPCUAServerPEA: - def __init__(self, mtp_generator: MTPGenerator = None, endpoint: str ='opc.tcp://127.0.0.1:4840/'): + def __init__(self, mtp_generator: MTPGenerator = None, endpoint: str = 'opc.tcp://127.0.0.1:4840/'): """ Defines an OPC UA server for PEA. :param mtp_generator: Instance of an MTP generator. If specified, an MTP file is generated each time @@ -129,7 +129,7 @@ def _create_opcua_objects_for_folders(self, data_assembly: SUCDataAssembly, pare # type of data assembly (e.g. services, active_elements, procedures etc.) da_type = parent_opcua_prefix.split('=')[-1].split('.')[-1] - folders = ['configuration_parameters', 'procedures','procedure_parameters', + folders = ['configuration_parameters', 'procedures', 'procedure_parameters', 'process_value_ins', 'report_values', 'process_value_outs'] leaves = ['op_src_mode', 'state_machine', 'procedure_control'] @@ -157,9 +157,11 @@ def _create_opcua_objects_for_folders(self, data_assembly: SUCDataAssembly, pare section_node = da_node.add_folder(section_node_id, section_name) if section_name in folders: for parameter in eval(f'data_assembly.{section_name}.values()'): - self._create_opcua_objects_for_folders(parameter, section_node_id, section_node) + self._create_opcua_objects_for_folders( + parameter, section_node_id, section_node) if section_name in leaves: - self._create_opcua_objects_for_leaves(section, section_node_id, section_node, instance) + self._create_opcua_objects_for_leaves( + section, section_node_id, section_node, instance) # create linked obj between instance and service component if self.mtp: @@ -181,7 +183,8 @@ def _create_opcua_objects_for_leaves(self, opcua_object, parent_opcua_prefix: st opcua_type = self._infer_data_type(attr.type) opcua_node_obj = parent_opcua_object.add_variable(attribute_node_id, attr.name, attr.init_value, varianttype=opcua_type) - logging.debug(f'OPCUA Node: {attribute_node_id}, Name: {attr.name}, Value: {attr.init_value}') + logging.debug( + f'OPCUA Node: {attribute_node_id}, Name: {attr.name}, Value: {attr.init_value}') opcua_node_obj.set_writable(False) opcua_comm_obj = OPCUACommunicationObject(opcua_node_obj, node_id=opcua_node_obj) attr.attach_communication_object(opcua_comm_obj) @@ -205,7 +208,8 @@ def _create_opcua_objects_for_leaves(self, opcua_object, parent_opcua_prefix: st pass else: if self.mtp: - self.mtp.add_attr_to_instance(par_instance, attr.name, attr.init_value, linked_id) + self.mtp.add_attr_to_instance( + par_instance, attr.name, attr.init_value, linked_id) # We subscribe to nodes that are writable attributes if attr.sub_cb is not None: diff --git a/src/mtppy/operation_elements.py b/src/mtppy/operation_elements.py index 364846d..41ea8ee 100644 --- a/src/mtppy/operation_elements.py +++ b/src/mtppy/operation_elements.py @@ -1,8 +1,8 @@ import logging -from mtppy.attribute import Attribute -from mtppy.operation_source_mode import OperationSourceMode -from mtppy.suc_data_assembly import SUCOperationElement +from MTPPy_Async.src.mtppy.attribute import Attribute +from MTPPy_Async.src.mtppy.operation_source_mode import OperationSourceMode +from MTPPy_Async.src.mtppy.suc_data_assembly import SUCOperationElement class AnaServParam(SUCOperationElement): diff --git a/src/mtppy/operation_source_mode.py b/src/mtppy/operation_source_mode.py index d5c0b69..ea99daa 100644 --- a/src/mtppy/operation_source_mode.py +++ b/src/mtppy/operation_source_mode.py @@ -1,6 +1,6 @@ import logging -from mtppy.attribute import Attribute +from MTPPy_Async.src.mtppy.attribute import Attribute class OperationSourceMode: diff --git a/src/mtppy/procedure.py b/src/mtppy/procedure.py index 20fc8ad..7790212 100644 --- a/src/mtppy/procedure.py +++ b/src/mtppy/procedure.py @@ -1,6 +1,6 @@ import logging -from mtppy.suc_data_assembly import * +from MTPPy_Async.src.mtppy.suc_data_assembly import * class Procedure(SUCServiceProcedure): diff --git a/src/mtppy/procedure_control.py b/src/mtppy/procedure_control.py index 566ed6f..68eae2b 100644 --- a/src/mtppy/procedure_control.py +++ b/src/mtppy/procedure_control.py @@ -1,7 +1,7 @@ import logging -from mtppy.attribute import Attribute -from mtppy.operation_source_mode import OperationSourceMode +from MTPPy_Async.src.mtppy.attribute import Attribute +from MTPPy_Async.src.mtppy.operation_source_mode import OperationSourceMode class ProcedureControl: diff --git a/src/mtppy/service.py b/src/mtppy/service.py index 16f79fb..8ca0433 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -2,14 +2,14 @@ from abc import abstractmethod -from mtppy.suc_data_assembly import SUCServiceControl -from mtppy.thread_control import ThreadControl -from mtppy.operation_source_mode import OperationSourceMode -from mtppy.state_machine import StateMachine -from mtppy.procedure_control import ProcedureControl -from mtppy.state_codes import StateCodes -from mtppy.procedure import Procedure -from mtppy.suc_data_assembly import SUCOperationElement +from MTPPy_Async.src.mtppy.suc_data_assembly import SUCServiceControl +from MTPPy_Async.src.mtppy.thread_control import ThreadControl +from MTPPy_Async.src.mtppy.operation_source_mode import OperationSourceMode +from MTPPy_Async.src.mtppy.state_machine import StateMachine +from MTPPy_Async.src.mtppy.procedure_control import ProcedureControl +from MTPPy_Async.src.mtppy.state_codes import StateCodes +from MTPPy_Async.src.mtppy.procedure import Procedure +from MTPPy_Async.src.mtppy.suc_data_assembly import SUCOperationElement StateCodes = StateCodes() diff --git a/src/mtppy/state_machine.py b/src/mtppy/state_machine.py index 7762c6a..95baf20 100644 --- a/src/mtppy/state_machine.py +++ b/src/mtppy/state_machine.py @@ -1,11 +1,11 @@ import logging -from mtppy.attribute import Attribute -from mtppy.state_codes import StateCodes -from mtppy.command_codes import CommandCodes -from mtppy.command_en_control import CommandEnControl -from mtppy.operation_source_mode import OperationSourceMode -from mtppy.procedure_control import ProcedureControl +from MTPPy_Async.src.mtppy.attribute import Attribute +from MTPPy_Async.src.mtppy.state_codes import StateCodes +from MTPPy_Async.src.mtppy.command_codes import CommandCodes +from MTPPy_Async.src.mtppy.command_en_control import CommandEnControl +from MTPPy_Async.src.mtppy.operation_source_mode import OperationSourceMode +from MTPPy_Async.src.mtppy.procedure_control import ProcedureControl StateCodes = StateCodes() CommandCodes = CommandCodes() @@ -57,7 +57,8 @@ def command_execution(self, com_var: int): cmd_str = CommandCodes.int_code[com_var] if not self.command_en_ctrl.is_enabled(cmd_str): - logging.debug(f'CommandEn does not permit to execute {cmd_str} from state {self.get_current_state_str()}') + logging.debug( + f'CommandEn does not permit to execute {cmd_str} from state {self.get_current_state_str()}') return else: logging.debug(f'CommandEn permits to execute {cmd_str}') diff --git a/src/mtppy/suc_data_assembly.py b/src/mtppy/suc_data_assembly.py index a077ed3..203889c 100644 --- a/src/mtppy/suc_data_assembly.py +++ b/src/mtppy/suc_data_assembly.py @@ -1,4 +1,4 @@ -from mtppy.attribute import Attribute +from MTPPy_Async.src.mtppy.attribute import Attribute class SUCDataAssembly: diff --git a/tests/test_ana_drv.py b/tests/test_ana_drv.py index e68e83e..b30ac31 100644 --- a/tests/test_ana_drv.py +++ b/tests/test_ana_drv.py @@ -1,4 +1,4 @@ -from mtppy.active_elements import AnaDrv +from MTPPy_Async.src.mtppy.active_elements import AnaDrv def init_ana_drv(op_mode='off', src_mode='int', rev_fbk_calc=True, fwd_fbk_calc=True, rpm_fbk_calc=True, @@ -60,7 +60,8 @@ def test_fwd_rev(): for command in [True, False]: ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True) eval(f'ana_drv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') if set_command in ['set_fwd_op', 'set_fwd_aut']: if command: assert ana_drv.attributes['FwdCtrl'].value == expected_result @@ -111,7 +112,8 @@ def test_fwd_rev_trip(): def test_fwd_rev_permit_en_true(): for op_mode, src_mode, set_command, _ in test_scenario: for command in [True, False]: - ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, perm_en=True) + ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, + fwd_en=True, rev_en=True, perm_en=True) ana_drv.set_permit(False) eval(f'ana_drv.{set_command}({command})') print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: False') @@ -122,10 +124,12 @@ def test_fwd_rev_permit_en_true(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, perm_en=True) + ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, + fwd_en=True, rev_en=True, perm_en=True) ana_drv.set_permit(True) eval(f'ana_drv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert ana_drv.attributes['Permit'].value == True assert ana_drv.attributes['SafePosAct'].value == False @@ -148,10 +152,12 @@ def test_fwd_rev_permit_en_true(): def test_fwd_rev_permit_en_false(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, perm_en=False) + ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, + fwd_en=True, rev_en=True, perm_en=False) ana_drv.set_permit(True) eval(f'ana_drv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert ana_drv.attributes['Permit'].value == True assert ana_drv.attributes['SafePosAct'].value == False @@ -174,7 +180,8 @@ def test_fwd_rev_permit_en_false(): def test_fwd_rev_interlock_en_true(): for op_mode, src_mode, set_command, _ in test_scenario: for command in [True, False]: - ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, intl_en=True) + ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, + fwd_en=True, rev_en=True, intl_en=True) ana_drv.set_interlock(False) eval(f'ana_drv.{set_command}({command})') print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: False') @@ -186,10 +193,12 @@ def test_fwd_rev_interlock_en_true(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, intl_en=True) + ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, + fwd_en=True, rev_en=True, intl_en=True) ana_drv.set_interlock(True) eval(f'ana_drv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert ana_drv.attributes['Interlock'].value == True assert ana_drv.attributes['SafePosAct'].value == False @@ -212,10 +221,12 @@ def test_fwd_rev_interlock_en_true(): def test_fwd_rev_interlock_en_false(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, intl_en=False) + ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, + fwd_en=True, rev_en=True, intl_en=False) ana_drv.set_interlock(True) eval(f'ana_drv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert ana_drv.attributes['Interlock'].value == True assert ana_drv.attributes['SafePosAct'].value == False @@ -238,7 +249,8 @@ def test_fwd_rev_interlock_en_false(): def test_fwd_rev_protect_en_true(): for op_mode, src_mode, set_command, _ in test_scenario: for command in [True, False]: - ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, prot_en=True) + ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, + fwd_en=True, rev_en=True, prot_en=True) ana_drv.set_protect(False) eval(f'ana_drv.{set_command}({command})') print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: False') @@ -250,10 +262,12 @@ def test_fwd_rev_protect_en_true(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, prot_en=True) + ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, + fwd_en=True, rev_en=True, prot_en=True) ana_drv.set_protect(True) eval(f'ana_drv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert ana_drv.attributes['Protect'].value == True assert ana_drv.attributes['SafePosAct'].value == False @@ -276,10 +290,12 @@ def test_fwd_rev_protect_en_true(): def test_fwd_rev_protect_en_false(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, prot_en=False) + ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, + fwd_en=True, rev_en=True, prot_en=False) ana_drv.set_protect(True) eval(f'ana_drv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert ana_drv.attributes['Protect'].value == True assert ana_drv.attributes['SafePosAct'].value == False @@ -317,7 +333,8 @@ def test_fwd_rev_protect_en_false(): def test_reset(): for op_mode, src_mode, set_command, result in test_scenario_reset: for command in [True, False]: - ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, prot_en=True) + ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, + fwd_en=True, rev_en=True, prot_en=True) ana_drv.set_protect(False) assert ana_drv.attributes['Protect'].value == False @@ -352,7 +369,8 @@ def test_stop(): ana_drv.set_fwd_op(True) ana_drv.set_fwd_aut(True) - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {changes_expected}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {changes_expected}') if changes_expected: assert ana_drv.attributes['FwdCtrl'].value == True eval(f'ana_drv.{set_command}({command})') @@ -385,7 +403,8 @@ def test_rpm(): ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, rpm_fbk_calc=True) eval(f'ana_drv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') if changes_expected: if -10 <= command <= 1000: assert ana_drv.get_rpm() == command diff --git a/tests/test_ana_vlv.py b/tests/test_ana_vlv.py index 0b5c9f9..59e39f4 100644 --- a/tests/test_ana_vlv.py +++ b/tests/test_ana_vlv.py @@ -1,5 +1,5 @@ import pytest -from mtppy.active_elements import AnaVlv +from MTPPy_Async.src.mtppy.active_elements import AnaVlv def init_ana_vlv(op_mode='off', src_mode='int', open_fbk_calc=True, close_fbk_calc=True, pos_fbk_calc=True, @@ -61,7 +61,8 @@ def test_open_close(): for command in [True, False]: ana_vlv = init_ana_vlv(op_mode=op_mode, src_mode=src_mode) eval(f'ana_vlv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') if set_command in ['set_open_op', 'set_open_aut']: if command: assert ana_vlv.attributes['OpenAct'].value == expected_result @@ -105,7 +106,8 @@ def test_open_close_permit_en_true(): ana_vlv = init_ana_vlv(op_mode=op_mode, src_mode=src_mode, perm_en=True) ana_vlv.set_permit(True) eval(f'ana_vlv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert ana_vlv.attributes['Permit'].value == True assert ana_vlv.attributes['SafePosAct'].value == False @@ -139,7 +141,8 @@ def test_open_close_permit_en_false(): ana_vlv = init_ana_vlv(op_mode=op_mode, src_mode=src_mode, perm_en=False) ana_vlv.set_permit(True) eval(f'ana_vlv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert ana_vlv.attributes['Permit'].value == True assert ana_vlv.attributes['SafePosAct'].value == False @@ -187,7 +190,8 @@ def test_open_close_interlock_en_true(): ana_vlv = init_ana_vlv(op_mode=op_mode, src_mode=src_mode, intl_en=True) ana_vlv.set_interlock(True) eval(f'ana_vlv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert ana_vlv.attributes['Interlock'].value == True assert ana_vlv.attributes['SafePosAct'].value == False @@ -221,7 +225,8 @@ def test_open_close_interlock_en_false(): ana_vlv = init_ana_vlv(op_mode=op_mode, src_mode=src_mode, intl_en=False) ana_vlv.set_interlock(True) eval(f'ana_vlv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert ana_vlv.attributes['Interlock'].value == True assert ana_vlv.attributes['SafePosAct'].value == False @@ -272,7 +277,8 @@ def test_open_close_protect_en_true(): assert ana_vlv.attributes['CloseAct'].value == False # state after calling reset eval(f'ana_vlv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') if set_command in ['set_open_op', 'set_open_aut']: if command: @@ -310,7 +316,8 @@ def test_open_close_protect_en_false(): assert ana_vlv.attributes['CloseAct'].value == False # state after calling reset eval(f'ana_vlv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') if set_command in ['set_open_op', 'set_open_aut']: if command: assert ana_vlv.attributes['OpenAct'].value == expected_result @@ -394,7 +401,8 @@ def test_pos_open(): elif op_mode == 'aut': ana_vlv.set_open_aut(True) eval(f'ana_vlv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') if changes_expected: if 2 <= command <= 10: assert ana_vlv.get_pos() == command @@ -416,7 +424,8 @@ def test_pos_close(): elif op_mode == 'aut': ana_vlv.set_close_aut(True) eval(f'ana_vlv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') assert ana_vlv.get_pos() == 2 assert ana_vlv.get_pos_fbk() == 2 diff --git a/tests/test_bin_drv.py b/tests/test_bin_drv.py index 1060f3a..53a8f86 100644 --- a/tests/test_bin_drv.py +++ b/tests/test_bin_drv.py @@ -1,5 +1,5 @@ import pytest -from mtppy.active_elements import BinDrv +from MTPPy_Async.src.mtppy.active_elements import BinDrv def init_bin_drv(op_mode='off', src_mode='int', rev_fbk_calc=True, fwd_fbk_calc=True, @@ -59,7 +59,8 @@ def test_fwd_rev(): for command in [True, False]: bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True) eval(f'bin_drv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') if set_command in ['set_fwd_op', 'set_fwd_aut']: if command: assert bin_drv.attributes['FwdCtrl'].value == expected_result @@ -110,7 +111,8 @@ def test_fwd_rev_trip(): def test_fwd_rev_permit_en_true(): for op_mode, src_mode, set_command, _ in test_scenario: for command in [True, False]: - bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, perm_en=True) + bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, + fwd_en=True, rev_en=True, perm_en=True) bin_drv.set_permit(False) eval(f'bin_drv.{set_command}({command})') print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: False') @@ -123,10 +125,12 @@ def test_fwd_rev_permit_en_true(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, perm_en=True) + bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, + fwd_en=True, rev_en=True, perm_en=True) bin_drv.set_permit(True) eval(f'bin_drv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert bin_drv.attributes['Permit'].value == True assert bin_drv.attributes['SafePosAct'].value == False @@ -157,10 +161,12 @@ def test_fwd_rev_permit_en_true(): def test_fwd_rev_permit_en_false(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - ana_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, perm_en=False) + ana_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, + fwd_en=True, rev_en=True, perm_en=False) ana_drv.set_permit(True) eval(f'ana_drv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert ana_drv.attributes['Permit'].value == True assert ana_drv.attributes['SafePosAct'].value == False @@ -183,7 +189,8 @@ def test_fwd_rev_permit_en_false(): def test_fwd_rev_interlock_en_true(): for op_mode, src_mode, set_command, _ in test_scenario: for command in [True, False]: - bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, intl_en=True) + bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, + fwd_en=True, rev_en=True, intl_en=True) bin_drv.set_interlock(False) eval(f'bin_drv.{set_command}({command})') print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: False') @@ -195,10 +202,12 @@ def test_fwd_rev_interlock_en_true(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, intl_en=True) + bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, + fwd_en=True, rev_en=True, intl_en=True) bin_drv.set_interlock(True) eval(f'bin_drv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert bin_drv.attributes['Interlock'].value == True assert bin_drv.attributes['SafePosAct'].value == False @@ -221,10 +230,12 @@ def test_fwd_rev_interlock_en_true(): def test_fwd_rev_interlock_en_false(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, intl_en=False) + bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, + fwd_en=True, rev_en=True, intl_en=False) bin_drv.set_interlock(True) eval(f'bin_drv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert bin_drv.attributes['Interlock'].value == True assert bin_drv.attributes['SafePosAct'].value == False @@ -247,7 +258,8 @@ def test_fwd_rev_interlock_en_false(): def test_fwd_rev_protect_en_true(): for op_mode, src_mode, set_command, _ in test_scenario: for command in [True, False]: - bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, prot_en=True) + bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, + fwd_en=True, rev_en=True, prot_en=True) bin_drv.set_protect(False) eval(f'bin_drv.{set_command}({command})') print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: False') @@ -259,10 +271,12 @@ def test_fwd_rev_protect_en_true(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, prot_en=True) + bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, + fwd_en=True, rev_en=True, prot_en=True) bin_drv.set_protect(True) eval(f'bin_drv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert bin_drv.attributes['Protect'].value == True assert bin_drv.attributes['SafePosAct'].value == False @@ -285,10 +299,12 @@ def test_fwd_rev_protect_en_true(): def test_fwd_rev_protect_en_false(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, prot_en=False) + bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, + fwd_en=True, rev_en=True, prot_en=False) bin_drv.set_protect(True) eval(f'bin_drv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert bin_drv.attributes['Protect'].value == True assert bin_drv.attributes['SafePosAct'].value == False @@ -326,7 +342,8 @@ def test_fwd_rev_protect_en_false(): def test_reset(): for op_mode, src_mode, set_command, result in test_scenario_reset: for command in [True, False]: - bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, prot_en=True) + bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, + fwd_en=True, rev_en=True, prot_en=True) bin_drv.set_protect(False) assert bin_drv.attributes['Protect'].value == False @@ -363,7 +380,8 @@ def test_stop(): elif op_mode == 'aut': bin_drv.set_fwd_aut(True) - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {changes_expected}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {changes_expected}') if changes_expected: assert bin_drv.attributes['FwdCtrl'].value == True eval(f'bin_drv.{set_command}({command})') diff --git a/tests/test_bin_vlv.py b/tests/test_bin_vlv.py index 26c762a..3c6782e 100644 --- a/tests/test_bin_vlv.py +++ b/tests/test_bin_vlv.py @@ -1,5 +1,5 @@ import pytest -from mtppy.active_elements import BinVlv +from MTPPy_Async.src.mtppy.active_elements import BinVlv def init_bin_vlv(op_mode='off', src_mode='int', open_fbk_calc=True, close_fbk_calc=True, safe_pos=0, safe_pos_en=True, diff --git a/tests/test_mon_ana_drv.py b/tests/test_mon_ana_drv.py index d6724af..be61f6d 100644 --- a/tests/test_mon_ana_drv.py +++ b/tests/test_mon_ana_drv.py @@ -1,5 +1,5 @@ import pytest -from mtppy.active_elements import MonAnaDrv +from MTPPy_Async.src.mtppy.active_elements import MonAnaDrv import time @@ -56,25 +56,30 @@ def init_mon_ana_drv(op_mode='off', src_mode='int', rev_fbk_calc=True, fwd_fbk_c def test_static_error(): for op_mode, src_mode, set_command in test_scenario_no_control_signals: for command in [True, False]: - mon_ana_drv = init_mon_ana_drv(op_mode=op_mode, src_mode=src_mode, rmp_ah_en=False, rmp_al_en=False) + mon_ana_drv = init_mon_ana_drv( + op_mode=op_mode, src_mode=src_mode, rmp_ah_en=False, rmp_al_en=False) mon_ana_drv.start_monitor() eval(f'mon_ana_drv.{set_command}({command})') print(f'Scenario: mode {op_mode} {src_mode}, {set_command}') - mon_ana_drv.attributes['FwdFbk'].set_value(True) # FwdFbk becomes to True without any control signals + # FwdFbk becomes to True without any control signals + mon_ana_drv.attributes['FwdFbk'].set_value(True) time.sleep(0.6) assert mon_ana_drv.attributes['MonStatErr'].value == True assert mon_ana_drv.attributes['SafePosAct'].value == True - assert mon_ana_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation + # safe position is clockwise rotation + assert mon_ana_drv.attributes['FwdFbk'].value == True - mon_ana_drv.attributes['FwdFbk'].set_value(False) # FwdFbk becomes to True without any control signals + # FwdFbk becomes to True without any control signals + mon_ana_drv.attributes['FwdFbk'].set_value(False) time.sleep(0.6) assert mon_ana_drv.attributes['MonStatErr'].value == True assert mon_ana_drv.attributes['SafePosAct'].value == True - assert mon_ana_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation + # safe position is clockwise rotation + assert mon_ana_drv.attributes['FwdFbk'].value == True mon_ana_drv.set_stop_monitor() mon_ana_drv.monitor_static_thread.join() @@ -88,7 +93,8 @@ def test_static_error_within_monitor_time(): for op_mode, src_mode, set_command in test_scenario_no_control_signals: for command in [True, False]: - mon_ana_drv = init_mon_ana_drv(op_mode=op_mode, src_mode=src_mode, rmp_ah_en=False, rmp_al_en=False) + mon_ana_drv = init_mon_ana_drv( + op_mode=op_mode, src_mode=src_mode, rmp_ah_en=False, rmp_al_en=False) mon_ana_drv.start_monitor() eval(f'mon_ana_drv.{set_command}({command})') @@ -115,7 +121,8 @@ def test_static_error_within_monitor_time(): def test_dynamic_error_fwd_stop(): for op_mode, src_mode, fwd_command, stop_command in test_scenario_control_signals_fwd: - mon_ana_drv = init_mon_ana_drv(op_mode=op_mode, src_mode=src_mode, rmp_ah_en=False, rmp_al_en=False) + mon_ana_drv = init_mon_ana_drv( + op_mode=op_mode, src_mode=src_mode, rmp_ah_en=False, rmp_al_en=False) mon_ana_drv.start_monitor() eval(f'mon_ana_drv.{fwd_command}(True)') @@ -128,7 +135,8 @@ def test_dynamic_error_fwd_stop(): assert mon_ana_drv.attributes['MonDynErr'].value == True assert mon_ana_drv.attributes['MonStatErr'].value == False assert mon_ana_drv.attributes['SafePosAct'].value == True - assert mon_ana_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation + # safe position is clockwise rotation + assert mon_ana_drv.attributes['FwdFbk'].value == True eval(f'mon_ana_drv.{stop_command}(True)') print(f'Scenario: mode {op_mode} {src_mode}, {stop_command}') @@ -140,7 +148,8 @@ def test_dynamic_error_fwd_stop(): assert mon_ana_drv.attributes['MonDynErr'].value == True assert mon_ana_drv.attributes['MonStatErr'].value == False assert mon_ana_drv.attributes['SafePosAct'].value == True - assert mon_ana_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation + # safe position is clockwise rotation + assert mon_ana_drv.attributes['FwdFbk'].value == True mon_ana_drv.set_stop_monitor() mon_ana_drv.monitor_static_thread.join() @@ -157,7 +166,8 @@ def test_dynamic_error_fwd_stop(): def test_dynamic_error_rev_stop(): for op_mode, src_mode, rev_command, stop_command in test_scenario_control_signals_rev: - mon_ana_drv = init_mon_ana_drv(op_mode=op_mode, src_mode=src_mode, rmp_ah_en=False, rmp_al_en=False) + mon_ana_drv = init_mon_ana_drv( + op_mode=op_mode, src_mode=src_mode, rmp_ah_en=False, rmp_al_en=False) mon_ana_drv.start_monitor() eval(f'mon_ana_drv.{rev_command}(True)') @@ -169,7 +179,8 @@ def test_dynamic_error_rev_stop(): assert mon_ana_drv.attributes['MonDynErr'].value == True assert mon_ana_drv.attributes['MonStatErr'].value == False assert mon_ana_drv.attributes['SafePosAct'].value == True - assert mon_ana_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation + # safe position is clockwise rotation + assert mon_ana_drv.attributes['FwdFbk'].value == True eval(f'mon_ana_drv.{stop_command}(True)') print(f'Scenario: mode {op_mode} {src_mode}, {stop_command}') @@ -181,7 +192,8 @@ def test_dynamic_error_rev_stop(): assert mon_ana_drv.attributes['MonDynErr'].value == True assert mon_ana_drv.attributes['MonStatErr'].value == False assert mon_ana_drv.attributes['SafePosAct'].value == True - assert mon_ana_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation + # safe position is clockwise rotation + assert mon_ana_drv.attributes['FwdFbk'].value == True mon_ana_drv.set_stop_monitor() mon_ana_drv.monitor_static_thread.join() @@ -198,7 +210,8 @@ def test_dynamic_error_rev_stop(): def test_dynamic_error_reset(): for op_mode, src_mode, set_command in test_scenario_control_signals_reset: - mon_ana_drv = init_mon_ana_drv(op_mode=op_mode, src_mode=src_mode, rmp_ah_en=False, rmp_al_en=False) + mon_ana_drv = init_mon_ana_drv( + op_mode=op_mode, src_mode=src_mode, rmp_ah_en=False, rmp_al_en=False) mon_ana_drv.start_monitor() # set valve to open @@ -219,7 +232,8 @@ def test_dynamic_error_reset(): assert mon_ana_drv.attributes['MonDynErr'].value == True assert mon_ana_drv.attributes['MonStatErr'].value == False assert mon_ana_drv.attributes['SafePosAct'].value == True - assert mon_ana_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation + # safe position is clockwise rotation + assert mon_ana_drv.attributes['FwdFbk'].value == True mon_ana_drv.set_stop_monitor() mon_ana_drv.monitor_static_thread.join() @@ -242,8 +256,10 @@ def test_rpm_error(): time.sleep(0.5) eval(f'mon_ana_drv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') - mon_ana_drv.attributes['RpmFbk'].set_value(400) # RpmFbk is 400, it does not reach desired rpm (500 or 700) + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') + # RpmFbk is 400, it does not reach desired rpm (500 or 700) + mon_ana_drv.attributes['RpmFbk'].set_value(400) time.sleep(0.5) if -10 <= command <= 1000: if command == 500: @@ -254,7 +270,8 @@ def test_rpm_error(): assert mon_ana_drv.attributes['RpmErr'].value == -410 assert mon_ana_drv.attributes['SafePosAct'].value == True - assert mon_ana_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation + # safe position is clockwise rotation + assert mon_ana_drv.attributes['FwdFbk'].value == True mon_ana_drv.set_stop_monitor() mon_ana_drv.monitor_static_thread.join() @@ -270,7 +287,8 @@ def test_rpm_high_limit_alarm(): mon_ana_drv.start_monitor() time.sleep(0.5) eval(f'mon_ana_drv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') time.sleep(0.5) if -10 <= command <= 1000: @@ -293,7 +311,8 @@ def test_rpm_low_limit_alarm(): mon_ana_drv.start_monitor() time.sleep(0.5) eval(f'mon_ana_drv.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') time.sleep(0.5) assert mon_ana_drv.attributes['RpmALAct'].value == True diff --git a/tests/test_mon_ana_vlv.py b/tests/test_mon_ana_vlv.py index 25d591e..8beebcc 100644 --- a/tests/test_mon_ana_vlv.py +++ b/tests/test_mon_ana_vlv.py @@ -1,5 +1,5 @@ import pytest -from mtppy.active_elements import MonAnaVlv +from MTPPy_Async.src.mtppy.active_elements import MonAnaVlv import time @@ -67,7 +67,8 @@ def test_static_error(): assert mon_ana_vlv.attributes['MonStatErr'].value == True assert mon_ana_vlv.attributes['MonDynErr'].value == False assert mon_ana_vlv.attributes['SafePosAct'].value == True - assert mon_ana_vlv.attributes['OpenFbk'].value == True # safe position of valve is open + # safe position of valve is open + assert mon_ana_vlv.attributes['OpenFbk'].value == True time.sleep(0.5) @@ -78,7 +79,8 @@ def test_static_error(): assert mon_ana_vlv.attributes['MonStatErr'].value == True assert mon_ana_vlv.attributes['MonDynErr'].value == False assert mon_ana_vlv.attributes['SafePosAct'].value == True - assert mon_ana_vlv.attributes['OpenFbk'].value == True # safe position of valve is open + # safe position of valve is open + assert mon_ana_vlv.attributes['OpenFbk'].value == True mon_ana_vlv.set_stop_monitor() mon_ana_vlv.monitor_static_thread.join() @@ -167,7 +169,8 @@ def test_pos_open(): mon_ana_vlv.set_open_aut(True) eval(f'mon_ana_vlv.{set_command}({command})') print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command}') - mon_ana_vlv.attributes['PosFbk'].set_value(7.5) # position tolerance is 1, pos_fbk is 2.5 --> pos error + # position tolerance is 1, pos_fbk is 2.5 --> pos error + mon_ana_vlv.attributes['PosFbk'].set_value(7.5) time.sleep(1.5) # time for monitoring pos error is 1s if 2 <= command < 6 or 8 < command <= 10: # out of the pos tolerance range assert mon_ana_vlv.attributes['PosReachedFbk'].value == False diff --git a/tests/test_mon_bin_drv.py b/tests/test_mon_bin_drv.py index cf2120e..79f3682 100644 --- a/tests/test_mon_bin_drv.py +++ b/tests/test_mon_bin_drv.py @@ -1,5 +1,5 @@ import pytest -from mtppy.active_elements import MonBinDrv +from MTPPy_Async.src.mtppy.active_elements import MonBinDrv import time @@ -59,19 +59,23 @@ def test_static_error(): eval(f'mon_bin_drv.{set_command}({command})') print(f'Scenario: mode {op_mode} {src_mode}, {set_command}') - mon_bin_drv.attributes['FwdFbk'].set_value(True) # FwdFbk becomes to True without any control signals + # FwdFbk becomes to True without any control signals + mon_bin_drv.attributes['FwdFbk'].set_value(True) time.sleep(0.6) assert mon_bin_drv.attributes['MonStatErr'].value == True assert mon_bin_drv.attributes['SafePosAct'].value == True - assert mon_bin_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation + # safe position is clockwise rotation + assert mon_bin_drv.attributes['FwdFbk'].value == True - mon_bin_drv.attributes['FwdFbk'].set_value(False) # FwdFbk becomes to True without any control signals + # FwdFbk becomes to True without any control signals + mon_bin_drv.attributes['FwdFbk'].set_value(False) time.sleep(0.6) assert mon_bin_drv.attributes['MonStatErr'].value == True assert mon_bin_drv.attributes['SafePosAct'].value == True - assert mon_bin_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation + # safe position is clockwise rotation + assert mon_bin_drv.attributes['FwdFbk'].value == True mon_bin_drv.set_stop_monitor() mon_bin_drv.monitor_static_thread.join() @@ -125,7 +129,8 @@ def test_dynamic_error_fwd_stop(): assert mon_bin_drv.attributes['MonDynErr'].value == True assert mon_bin_drv.attributes['MonStatErr'].value == False assert mon_bin_drv.attributes['SafePosAct'].value == True - assert mon_bin_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation + # safe position is clockwise rotation + assert mon_bin_drv.attributes['FwdFbk'].value == True eval(f'mon_bin_drv.{stop_command}(True)') print(f'Scenario: mode {op_mode} {src_mode}, {stop_command}') @@ -137,7 +142,8 @@ def test_dynamic_error_fwd_stop(): assert mon_bin_drv.attributes['MonDynErr'].value == True assert mon_bin_drv.attributes['MonStatErr'].value == False assert mon_bin_drv.attributes['SafePosAct'].value == True - assert mon_bin_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation + # safe position is clockwise rotation + assert mon_bin_drv.attributes['FwdFbk'].value == True mon_bin_drv.set_stop_monitor() mon_bin_drv.monitor_static_thread.join() @@ -165,7 +171,8 @@ def test_dynamic_error_rev_stop(): assert mon_bin_drv.attributes['MonDynErr'].value == True assert mon_bin_drv.attributes['MonStatErr'].value == False assert mon_bin_drv.attributes['SafePosAct'].value == True - assert mon_bin_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation + # safe position is clockwise rotation + assert mon_bin_drv.attributes['FwdFbk'].value == True eval(f'mon_bin_drv.{stop_command}(True)') print(f'Scenario: mode {op_mode} {src_mode}, {stop_command}') @@ -177,7 +184,8 @@ def test_dynamic_error_rev_stop(): assert mon_bin_drv.attributes['MonDynErr'].value == True assert mon_bin_drv.attributes['MonStatErr'].value == False assert mon_bin_drv.attributes['SafePosAct'].value == True - assert mon_bin_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation + # safe position is clockwise rotation + assert mon_bin_drv.attributes['FwdFbk'].value == True mon_bin_drv.set_stop_monitor() mon_bin_drv.monitor_static_thread.join() diff --git a/tests/test_mon_bin_vlv.py b/tests/test_mon_bin_vlv.py index 225198a..7a8c1ea 100644 --- a/tests/test_mon_bin_vlv.py +++ b/tests/test_mon_bin_vlv.py @@ -1,5 +1,5 @@ import pytest -from mtppy.active_elements import MonBinVlv +from MTPPy_Async.src.mtppy.active_elements import MonBinVlv import time @@ -65,7 +65,8 @@ def test_static_error(): assert mon_bin_vlv.attributes['MonStatErr'].value == True assert mon_bin_vlv.attributes['MonDynErr'].value == False assert mon_bin_vlv.attributes['SafePosAct'].value == True - assert mon_bin_vlv.attributes['OpenFbk'].value == True # safe position of valve is open + # safe position of valve is open + assert mon_bin_vlv.attributes['OpenFbk'].value == True time.sleep(0.5) @@ -76,7 +77,8 @@ def test_static_error(): assert mon_bin_vlv.attributes['MonStatErr'].value == True assert mon_bin_vlv.attributes['MonDynErr'].value == False assert mon_bin_vlv.attributes['SafePosAct'].value == True - assert mon_bin_vlv.attributes['OpenFbk'].value == True # safe position of valve is open + # safe position of valve is open + assert mon_bin_vlv.attributes['OpenFbk'].value == True mon_bin_vlv.set_stop_monitor() mon_bin_vlv.monitor_static_thread.join() diff --git a/tests/test_mtp_generator_instance_hierarchy_services.py b/tests/test_mtp_generator_instance_hierarchy_services.py index f52b1db..1c00b47 100644 --- a/tests/test_mtp_generator_instance_hierarchy_services.py +++ b/tests/test_mtp_generator_instance_hierarchy_services.py @@ -4,12 +4,12 @@ added to InstanceHierarchy services """ import pytest -from mtppy.mtp_generator import MTPGenerator -from mtppy.service import Service -from mtppy.procedure import Procedure -from mtppy.operation_elements import * -from mtppy.indicator_elements import * -from mtppy.active_elements import * +from MTPPy_Async.src.mtppy.mtp_generator import MTPGenerator +from MTPPy_Async.src.mtppy.service import Service +from MTPPy_Async.src.mtppy.procedure import Procedure +from MTPPy_Async.src.mtppy.operation_elements import * +from MTPPy_Async.src.mtppy.indicator_elements import * +from MTPPy_Async.src.mtppy.active_elements import * # basic infos needed to initiate mtp_generator writer_info_dict = {'WriterName': 'tud/plt', 'WriterID': 'tud/plt', 'WriterVendor': 'tud', @@ -29,7 +29,8 @@ procedure_process_value_out = BinView('process_value_out') # test obj: procedure parameter -procedure_param = DIntServParam('proc_param_dint', v_min=-10, v_max=10, v_scl_min=0, v_scl_max=-10, v_unit=23) +procedure_param = DIntServParam('proc_param_dint', v_min=-10, v_max=10, + v_scl_min=0, v_scl_max=-10, v_unit=23) # test obj: procedure procedure = Procedure(0, 'cont', is_self_completing=False, is_default=True) @@ -38,7 +39,8 @@ procedure.add_procedure_parameter(procedure_param) # test obj: service config parameter -serv_parameters = AnaServParam('serv_param_ana', v_min=0, v_max=50, v_scl_min=0, v_scl_max=10, v_unit=23) +serv_parameters = AnaServParam('serv_param_ana', v_min=0, v_max=50, + v_scl_min=0, v_scl_max=10, v_unit=23) # test obj: service @@ -102,7 +104,8 @@ def resetting(self): class TestInstanceHierarchyServices(object): # test some functions of mtp generator def setup_class(self): - self.mtp_generator = MTPGenerator(writer_info_dict, export_manifest_path, manifest_template_path) + self.mtp_generator = MTPGenerator( + writer_info_dict, export_manifest_path, manifest_template_path) def test_add_service_to_InstanceHierarchy(self): """ @@ -111,24 +114,29 @@ def test_add_service_to_InstanceHierarchy(self): self.mtp_generator.create_components_for_services(service1, 'services') # test, if two InternalElements are added to InstanceHierarchy Services # the name of these two InternalElements should correspond the service name - service_name = self.mtp_generator.instance_hierarchy_service.find('InternalElement').get('Name') + service_name = self.mtp_generator.instance_hierarchy_service.find( + 'InternalElement').get('Name') assert service_name == 'service_test1' def test_add_components_to_service(self): - self.mtp_generator.create_components_for_services(serv_parameters, 'configuration_parameters') + self.mtp_generator.create_components_for_services( + serv_parameters, 'configuration_parameters') self.mtp_generator.create_components_for_services(procedure, 'procedures') # InternalElement service should only have two sub-InternalElements config param and procedure - service_sub_elements = self.mtp_generator.instance_hierarchy_service.find('InternalElement') + service_sub_elements = self.mtp_generator.instance_hierarchy_service.find( + 'InternalElement') assert len(service_sub_elements.findall('InternalElement')) == 2 def test_add_components_to_procedure(self): self.mtp_generator.create_components_for_services(procedure_param, 'procedure_parameters') self.mtp_generator.create_components_for_services(procedure_report_value, 'report_values') - self.mtp_generator.create_components_for_services(procedure_process_value_out, 'process_value_outs') + self.mtp_generator.create_components_for_services( + procedure_process_value_out, 'process_value_outs') # in this test case, a procedure should have one report value, one value out and one procedure parameter - procedure_ie = self.mtp_generator.instance_hierarchy_service.findall('InternalElement/InternalElement')[1] + procedure_ie = self.mtp_generator.instance_hierarchy_service.findall( + 'InternalElement/InternalElement')[1] assert len(procedure_ie.findall('InternalElement')) == 3 pro_parameters = procedure_ie.findall('InternalElement')[0] diff --git a/tests/test_mtp_generator_instance_list.py b/tests/test_mtp_generator_instance_list.py index b208ec0..5406cee 100644 --- a/tests/test_mtp_generator_instance_list.py +++ b/tests/test_mtp_generator_instance_list.py @@ -3,12 +3,12 @@ With these functions, data assembly and it´s attributes can be added under InstanceList and SourceList/OPCServer """ import pytest -from mtppy.mtp_generator import MTPGenerator -from mtppy.service import Service -from mtppy.procedure import Procedure -from mtppy.operation_elements import * -from mtppy.indicator_elements import * -from mtppy.active_elements import * +from MTPPy_Async.src.mtppy.mtp_generator import MTPGenerator +from MTPPy_Async.src.mtppy.service import Service +from MTPPy_Async.src.mtppy.procedure import Procedure +from MTPPy_Async.src.mtppy.operation_elements import * +from MTPPy_Async.src.mtppy.indicator_elements import * +from MTPPy_Async.src.mtppy.active_elements import * import xml.etree.ElementTree as ET @@ -102,7 +102,8 @@ def resetting(self): class TestMTPInstanceList(object): # test some functions of mtp generator def setup_class(self): - self.mtp_generator = MTPGenerator(writer_info_dict, export_manifest_path, manifest_template_path) + self.mtp_generator = MTPGenerator( + writer_info_dict, export_manifest_path, manifest_template_path) self.mtp_generator.add_module_type_package('1.0', 'mtp_test', '') self.module_type_package = self.mtp_generator.module_type_package endpoint = '127.0.0.0' @@ -116,7 +117,8 @@ def test_add_instance_report_value(self): report_value = self.mtp_generator.instance_list.findall('InternalElement')[0] # whether type and name of the report value obj are correct - assert report_value.get('RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/IndicatorElement/AnaView' + assert report_value.get( + 'RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/IndicatorElement/AnaView' assert report_value.get('Name') == 'services.rand_num_gen.procedures.cont.report_values' # initiate InstanceList for other tests @@ -130,7 +132,8 @@ def test_add_instance_process_value_out(self): value_out = self.mtp_generator.instance_list.findall('InternalElement')[0] # whether type and name of the value out obj are correct - assert value_out.get('RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/IndicatorElement/BinView' + assert value_out.get( + 'RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/IndicatorElement/BinView' assert value_out.get('Name') == 'services.rand_num_gen.procedures.cont.process_value_outs' # initiate InstanceList for other tests @@ -149,7 +152,8 @@ def test_add_instance_procedure(self): node_id = 'ns=3;s=services.rand_num_gen.procedures' self.mtp_generator.create_instance(procedure, node_id) proc = self.mtp_generator.instance_list.findall('InternalElement')[0] - assert proc.get('RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/DiagnosticElement/HealthStateView' + assert proc.get( + 'RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/DiagnosticElement/HealthStateView' assert proc.get('Name') == 'services.rand_num_gen.procedures.HealthStateView' # add report value @@ -157,7 +161,8 @@ def test_add_instance_procedure(self): for report_values in procedure.report_values.values(): self.mtp_generator.create_instance(report_values, node_id_rv) rv = self.mtp_generator.instance_list.findall('InternalElement')[1] - assert rv.get('RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/IndicatorElement/AnaView' + assert rv.get( + 'RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/IndicatorElement/AnaView' assert rv.get('Name') == 'services.rand_num_gen.procedures.cont.report_values' # initiate InstanceList for other tests @@ -170,7 +175,8 @@ def test_add_instance_service(self): node_id = 'ns=3;s=services' self.mtp_generator.create_instance(service, node_id) serv = self.mtp_generator.instance_list.findall('InternalElement')[0] - assert serv.get('RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/ServiceControl' + assert serv.get( + 'RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/ServiceControl' assert serv.get('Name') == 'services.ServiceControl' # add config para @@ -191,7 +197,8 @@ def test_add_instance_active_element_plt(self): node_id = 'ns=3;s=active_elements.pid_ctrl' self.mtp_generator.create_instance(pid_ctrl, node_id) active_element = self.mtp_generator.instance_list.findall('InternalElement')[0] - assert active_element.get('RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/ActiveElement/PIDCtrl' + assert active_element.get( + 'RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/ActiveElement/PIDCtrl' assert active_element.get('Name') == 'active_elements.pid_ctrl' # initiate InstanceList for other tests @@ -202,7 +209,8 @@ def test_add_instance_active_element_analogue_drive(self): node_id = 'ns=3;s=active_elements.analogue_drive' self.mtp_generator.create_instance(analogue_drive, node_id) act = self.mtp_generator.instance_list.findall('InternalElement')[0] - assert act.get('RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/ActiveElement/AnaDrv' + assert act.get( + 'RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/ActiveElement/AnaDrv' assert act.get('Name') == 'active_elements.analogue_drive' # initiate InstanceList for other tests @@ -233,7 +241,8 @@ def test_add_attr_to_instance(self): assert len(self.mtp_generator.opcua_server.findall(".//ExternalInterface")) == 28 # each ExternalInterface has three attributes - assert len(self.mtp_generator.opcua_server.findall(".//ExternalInterface")[0].findall('Attribute')) == 3 + assert len(self.mtp_generator.opcua_server.findall( + ".//ExternalInterface")[0].findall('Attribute')) == 3 # check, if ExternalInterface is connected to attribute of Instance linked_id_ei = self.mtp_generator.opcua_server.findall(".//ExternalInterface")[2].get('ID') diff --git a/tests/test_mtp_generator_structure.py b/tests/test_mtp_generator_structure.py index a700230..2093fd4 100644 --- a/tests/test_mtp_generator_structure.py +++ b/tests/test_mtp_generator_structure.py @@ -2,7 +2,7 @@ test the basic manifest structure """ import pytest -from mtppy.mtp_generator import MTPGenerator +from MTPPy_Async.src.mtppy.mtp_generator import MTPGenerator # basic infos needed to initiate mtp_generator writer_info_dict = {'WriterName': 'tud/plt/shk', 'WriterID': 'tud/plt', 'WriterVendor': 'tud', @@ -18,7 +18,8 @@ def setup_class(self): """ initiate mtp generator and ModuleTypePackage """ - self.mtp_generator = MTPGenerator(writer_info_dict, export_manifest_path, manifest_template_path) + self.mtp_generator = MTPGenerator( + writer_info_dict, export_manifest_path, manifest_template_path) self.mtp_generator.add_module_type_package('1.0', 'mtp_test', '') self.module_type_package = self.mtp_generator.module_type_package @@ -71,7 +72,8 @@ def test_edit_writer_infos_error(self): # a KeyError should be raised with pytest.raises(KeyError): - mtp_generator = MTPGenerator(writer_info_dict1, export_manifest_path, manifest_template_path) + mtp_generator = MTPGenerator( + writer_info_dict1, export_manifest_path, manifest_template_path) mtp_generator.edit_writer_information() def test_add_supported_role_class(self): @@ -79,7 +81,8 @@ def test_add_supported_role_class(self): self.mtp_generator.apply_add_supported_role_class() for internal_element in self.mtp_generator.root.iter('InternalElement'): supported_role_class = internal_element.find('SupportedRoleClass') - assert supported_role_class.get('RefRoleClassPath') == 'AutomationMLBaseRoleClassLib/AutomationMLBaseRole' + assert supported_role_class.get( + 'RefRoleClassPath') == 'AutomationMLBaseRoleClassLib/AutomationMLBaseRole' def test_random_id_generator(self): random_id = self.mtp_generator.random_id_generator() diff --git a/tests/test_pid_ctrl.py b/tests/test_pid_ctrl.py index 5d510d2..eb291e0 100644 --- a/tests/test_pid_ctrl.py +++ b/tests/test_pid_ctrl.py @@ -1,4 +1,4 @@ -from mtppy.active_elements import PIDCtrl +from MTPPy_Async.src.mtppy.active_elements import PIDCtrl from time import sleep @@ -48,7 +48,8 @@ def test_set_sp(): for command in [-500, -10, 500, 1000, 10000]: pid_ctrl = init_pid_ctrl(op_mode=op_mode, src_mode=src_mode) eval(f'pid_ctrl.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, changes expected: {changes_expected}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, changes expected: {changes_expected}') if changes_expected: if -10 <= command <= 1000: assert pid_ctrl.get_sp() == command @@ -70,7 +71,8 @@ def test_set_mv_man(): for command in [-500, -10, 500, 1000, 10000]: pid_ctrl = init_pid_ctrl(op_mode=op_mode, src_mode=src_mode) eval(f'pid_ctrl.{set_command}({command})') - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, changes expected: {changes_expected}') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, changes expected: {changes_expected}') if changes_expected: if -10 <= command <= 1000: assert pid_ctrl.get_mv() == command diff --git a/tests/test_procedure_control.py b/tests/test_procedure_control.py index 9227e29..197dcce 100644 --- a/tests/test_procedure_control.py +++ b/tests/test_procedure_control.py @@ -1,5 +1,5 @@ -from mtppy.procedure_control import ProcedureControl -from mtppy.procedure import Procedure +from MTPPy_Async.src.mtppy.procedure_control import ProcedureControl +from MTPPy_Async.src.mtppy.procedure import Procedure from test_operation_source_mode import init_op_source_mode @@ -9,7 +9,8 @@ def init_procedure_control(op_mode='off', src_mode='int'): 1: Procedure(1, 'second', is_default=True), 2: Procedure(2, 'third'), 3: Procedure(3, 'forth')} - procedure_control = ProcedureControl(procedures=dummy_procedures, service_op_src_mode=op_src_mode) + procedure_control = ProcedureControl( + procedures=dummy_procedures, service_op_src_mode=op_src_mode) return procedure_control @@ -44,23 +45,28 @@ def test_set_procedure_op(): def test_set_procedure_int(): procedure_control = init_procedure_control(op_mode='off', src_mode='ext') - check_valid_states(procedure_control, procedure_control.set_procedure_int, change_allowed=False) + check_valid_states(procedure_control, procedure_control.set_procedure_int, + change_allowed=False) check_invalid_states(procedure_control, procedure_control.set_procedure_int) procedure_control = init_procedure_control(op_mode='off', src_mode='int') - check_valid_states(procedure_control, procedure_control.set_procedure_int, change_allowed=False) + check_valid_states(procedure_control, procedure_control.set_procedure_int, + change_allowed=False) check_invalid_states(procedure_control, procedure_control.set_procedure_int) procedure_control = init_procedure_control(op_mode='op', src_mode='ext') - check_valid_states(procedure_control, procedure_control.set_procedure_int, change_allowed=False) + check_valid_states(procedure_control, procedure_control.set_procedure_int, + change_allowed=False) check_invalid_states(procedure_control, procedure_control.set_procedure_int) procedure_control = init_procedure_control(op_mode='op', src_mode='int') - check_valid_states(procedure_control, procedure_control.set_procedure_int, change_allowed=False) + check_valid_states(procedure_control, procedure_control.set_procedure_int, + change_allowed=False) check_invalid_states(procedure_control, procedure_control.set_procedure_int) procedure_control = init_procedure_control(op_mode='aut', src_mode='ext') - check_valid_states(procedure_control, procedure_control.set_procedure_int, change_allowed=False) + check_valid_states(procedure_control, procedure_control.set_procedure_int, + change_allowed=False) check_invalid_states(procedure_control, procedure_control.set_procedure_int) procedure_control = init_procedure_control(op_mode='aut', src_mode='int') @@ -70,19 +76,23 @@ def test_set_procedure_int(): def test_set_procedure_ext(): procedure_control = init_procedure_control(op_mode='off', src_mode='ext') - check_valid_states(procedure_control, procedure_control.set_procedure_ext, change_allowed=False) + check_valid_states(procedure_control, procedure_control.set_procedure_ext, + change_allowed=False) check_invalid_states(procedure_control, procedure_control.set_procedure_ext) procedure_control = init_procedure_control(op_mode='off', src_mode='int') - check_valid_states(procedure_control, procedure_control.set_procedure_ext, change_allowed=False) + check_valid_states(procedure_control, procedure_control.set_procedure_ext, + change_allowed=False) check_invalid_states(procedure_control, procedure_control.set_procedure_ext) procedure_control = init_procedure_control(op_mode='op', src_mode='ext') - check_valid_states(procedure_control, procedure_control.set_procedure_ext, change_allowed=False) + check_valid_states(procedure_control, procedure_control.set_procedure_ext, + change_allowed=False) check_invalid_states(procedure_control, procedure_control.set_procedure_ext) procedure_control = init_procedure_control(op_mode='op', src_mode='int') - check_valid_states(procedure_control, procedure_control.set_procedure_ext, change_allowed=False) + check_valid_states(procedure_control, procedure_control.set_procedure_ext, + change_allowed=False) check_invalid_states(procedure_control, procedure_control.set_procedure_ext) procedure_control = init_procedure_control(op_mode='aut', src_mode='ext') @@ -90,7 +100,8 @@ def test_set_procedure_ext(): check_invalid_states(procedure_control, procedure_control.set_procedure_ext) procedure_control = init_procedure_control(op_mode='aut', src_mode='int') - check_valid_states(procedure_control, procedure_control.set_procedure_ext, change_allowed=False) + check_valid_states(procedure_control, procedure_control.set_procedure_ext, + change_allowed=False) check_invalid_states(procedure_control, procedure_control.set_procedure_ext) @@ -106,4 +117,3 @@ def test_set_procedure_cur(): procedure_control.set_procedure_op(10) procedure_control.set_procedure_cur() assert procedure_control.attributes['ProcedureCur'].value == 0 - diff --git a/tests/test_state_machine.py b/tests/test_state_machine.py index c9c09dd..adc0094 100644 --- a/tests/test_state_machine.py +++ b/tests/test_state_machine.py @@ -1,7 +1,7 @@ from test_procedure_control import init_procedure_control -from mtppy.state_machine import StateMachine -from mtppy.state_codes import StateCodes -from mtppy.command_codes import CommandCodes +from MTPPy_Async.src.mtppy.state_machine import StateMachine +from MTPPy_Async.src.mtppy.state_codes import StateCodes +from MTPPy_Async.src.mtppy.command_codes import CommandCodes StateCodes = StateCodes() CommandCodes = CommandCodes() @@ -175,7 +175,8 @@ def test_set_command(): for op_mode, src_mode, set_command, change_allowed in test_scenario: for state in StateCodes.get_list_str(): for command in CommandCodes.get_list_int(): - state_machine, callback_object = init_state_machine(state=state, op_mode=op_mode, src_mode=src_mode) + state_machine, callback_object = init_state_machine( + state=state, op_mode=op_mode, src_mode=src_mode) eval(f'state_machine.{set_command}({command})') command_str = CommandCodes.int_code[command] # eval(f'state_machine.{command_str}()') @@ -184,6 +185,8 @@ def test_set_command(): else: expected_state = state final_state = state_machine.get_current_state_str() - print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, validity is {change_allowed},', end=' ') - print(f'transition {state}->({command_str})->{final_state} (expected {expected_state})') + print( + f'Scenario: mode {op_mode} {src_mode}, {set_command}, validity is {change_allowed},', end=' ') + print( + f'transition {state}->({command_str})->{final_state} (expected {expected_state})') assert final_state == expected_state From 137bc689ea8fedbec4b1b3bf6c4f8ec1a11f59d5 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Mon, 21 Jul 2025 15:31:09 +0200 Subject: [PATCH 02/61] bugfix in state_change() --- src/mtppy/state_machine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mtppy/state_machine.py b/src/mtppy/state_machine.py index 95baf20..c905f3b 100644 --- a/src/mtppy/state_machine.py +++ b/src/mtppy/state_machine.py @@ -112,7 +112,7 @@ def state_change(self): self._change_state_to(StateCodes.execute) elif self.act_state == StateCodes.starting: self._change_state_to(StateCodes.execute) - elif selt.act_state == StateCodes.execute: + elif self.act_state == StateCodes.execute: self._change_state_to(StateCodes.completing) elif self.act_state == StateCodes.completing: self._change_state_to(StateCodes.completed) From 86883df9c9964b9f0b5ad592f2d0a1966f4b3f4c Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Mon, 21 Jul 2025 15:34:57 +0200 Subject: [PATCH 03/61] hack to allow the states to be async function --- src/mtppy/service.py | 5 +++-- src/mtppy/thread_control.py | 39 ++++++++++++++++++++++++++++++++++--- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/mtppy/service.py b/src/mtppy/service.py index 8ca0433..85eb15e 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -15,15 +15,16 @@ class Service(SUCServiceControl): - def __init__(self, tag_name: str, tag_description: str): + def __init__(self, tag_name: str, tag_description: str, scheduler: callable = None): """ Represents a service of the PEA. :param tag_name: Tag name of the service. :param tag_description: Tag description of the service. + :param scheduler: Scheduler to run the jobs. """ super().__init__(tag_name, tag_description) - self.thread_ctrl = ThreadControl() + self.thread_ctrl = ThreadControl(scheduler) self.op_src_mode = OperationSourceMode() self.configuration_parameters = {} diff --git a/src/mtppy/thread_control.py b/src/mtppy/thread_control.py index d512082..a402980 100644 --- a/src/mtppy/thread_control.py +++ b/src/mtppy/thread_control.py @@ -1,12 +1,21 @@ import logging from threading import Thread +from datetime import datetime +import asyncio + +from apscheduler.schedulers.asyncio import AsyncIOScheduler class ThreadControl: - def __init__(self): + def __init__(self, scheduler: callable = None): """ Represents a thread control to be able to run with multithreading. + :param scheduler: Scheduler to run the jobs. """ + self.scheduler: AsyncIOScheduler = scheduler + self.job_event = asyncio.Event() + self.exception: Exception = None + self.exception_event = asyncio.Event() self.thread = None self.running_state = '' self.requested_state = '' @@ -20,6 +29,30 @@ def request_state(self, state: str, cb_function: callable): def reallocate_running_thread(self): logging.debug(f'Reallocate thread to state {self.requested_state}') if self.requested_state is not self.running_state: - self.thread = Thread(target=self.callback_function) - self.thread.start() + self.job_event.set() + + self.scheduler.add_job( + self.run_job, 'date', run_date=datetime.now(), name=self.requested_state, + misfire_grace_time=None) self.running_state = self.requested_state + + async def run_job(self): + self.job_event.clear() + tasks: list[asyncio.Task] = [ + asyncio.create_task(self.job_event.wait(), name='cancel_event'), + asyncio.create_task(self.callback_function(), name=self.requested_state) + ] + done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED) + + for task in pending: + task.cancel() + + try: + if tasks[1].exception() != None: + raise tasks[1].exception() + except asyncio.exceptions.InvalidStateError as e: + pass + except Exception as e: + self.exception = e + self.exception_event.set() + logging.warning(f"Task {tasks[1].get_name()} raised an exception: {e}") From 784f3477ac3ebb98677f7dbc594973f57f5ca875 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Mon, 21 Jul 2025 17:21:13 +0200 Subject: [PATCH 04/61] added custom logger --- src/mtppy/active_elements.py | 334 +++++++++++++++-------------- src/mtppy/attribute.py | 4 +- src/mtppy/command_en_control.py | 4 +- src/mtppy/indicator_elements.py | 10 +- src/mtppy/opcua_server_pea.py | 16 +- src/mtppy/operation_elements.py | 56 ++--- src/mtppy/operation_source_mode.py | 50 ++--- src/mtppy/procedure.py | 4 +- src/mtppy/procedure_control.py | 14 +- src/mtppy/service.py | 4 +- src/mtppy/state_machine.py | 10 +- src/mtppy/thread_control.py | 8 +- 12 files changed, 269 insertions(+), 245 deletions(-) diff --git a/src/mtppy/active_elements.py b/src/mtppy/active_elements.py index c2c7c96..165db4c 100644 --- a/src/mtppy/active_elements.py +++ b/src/mtppy/active_elements.py @@ -9,6 +9,8 @@ from time import sleep from simple_pid import PID +_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") + class AnaVlv(SUCActiveElement): def __init__(self, tag_name, tag_description='', @@ -78,7 +80,7 @@ def _expect_save_pos(self): if self.attributes['SafePosEn'].value: self._go_save_pos() else: # hold position - logging.debug('Device has no safe position') + _logger.debug('Device has no safe position') self.attributes['SafePosAct'].set_value(True) def _go_save_pos(self): @@ -91,57 +93,57 @@ def _go_save_pos(self): safe_pos = self.attributes['PosMin'].value self.attributes['Pos'].set_value(safe_pos) - logging.debug('Pos set to safe position %s' % safe_pos) + _logger.debug('Pos set to safe position %s' % safe_pos) if self.attributes['PosFbkCalc'].value: self.attributes['PosFbk'].set_value(safe_pos) def set_open_aut(self, value: bool): - logging.debug(f'OpenAut set to {value}') + _logger.debug(f'OpenAut set to {value}') if self.op_src_mode.attributes['StateAutAct'].value: if value and self._run_allowed(): self._run_open_vlv() def set_close_aut(self, value: bool): - logging.debug(f'CloseAut set to {value}') + _logger.debug(f'CloseAut set to {value}') if self.op_src_mode.attributes['StateAutAct'].value: if value and self._run_allowed(): self._run_close_vlv() def set_reset_aut(self, value: bool): - logging.debug(f'ResetAut set to {value}') + _logger.debug(f'ResetAut set to {value}') if self.op_src_mode.attributes['StateAutAct'].value and value: self._reset_vlv() def set_open_op(self, value: bool): - logging.debug(f'OpenOp set to {value}') + _logger.debug(f'OpenOp set to {value}') if self.op_src_mode.attributes['StateOpAct'].value: if value and self._run_allowed(): self._run_open_vlv() self.attributes['OpenOp'].value = False def set_close_op(self, value: bool): - logging.debug(f'CloseOp set to {value}') + _logger.debug(f'CloseOp set to {value}') if self.op_src_mode.attributes['StateOpAct'].value: if value and self._run_allowed(): self._run_close_vlv() self.attributes['CloseOp'].value = False def set_reset_op(self, value: bool): - logging.debug(f'ResetOp set to {value}') + _logger.debug(f'ResetOp set to {value}') if self.op_src_mode.attributes['StateOpAct'].value and value: self._reset_vlv() self.attributes['ResetOp'].set_value(False) def _run_allowed(self): if self.attributes['PermEn'].value and self.attributes['Permit'].value == 0: - logging.debug(f'Permission is not given') + _logger.debug(f'Permission is not given') return False if self.attributes['IntlEn'].value and self.attributes['Interlock'].value == 0: - logging.debug(f'Interlock is active') + _logger.debug(f'Interlock is active') return False if self.attributes['ProtEn'].value and self.attributes['Protect'].value == 0: - logging.debug(f'Protect is active') + _logger.debug(f'Protect is active') return False return True @@ -173,12 +175,12 @@ def _reset_vlv(self): self.attributes['CloseFbk'].set_value(False) def set_pos_int(self, value: float): - logging.debug(f'PosInt set to {value}') + _logger.debug(f'PosInt set to {value}') if self.op_src_mode.attributes['SrcIntAct'].value: self._set_pos(value) def set_pos_man(self, value: float): - logging.debug(f'PosMan set to {value}') + _logger.debug(f'PosMan set to {value}') if self.op_src_mode.attributes['SrcManAct'].value: self._set_pos(value) @@ -198,7 +200,7 @@ def valid_value(self, value: float): def _set_pos(self, value: float): if not self._run_allowed(): - logging.debug('No position change is allowed') + _logger.debug('No position change is allowed') return if self.valid_value(value): @@ -211,42 +213,42 @@ def _set_pos(self, value: float): # if SafePosAct is active, safety setting for the position is accepted elif self.attributes['SafePosAct'].value or \ (self.attributes['PermEn'].value and self.attributes['Permit'].value == 0): - logging.debug('manual or internal position specification inactive') + _logger.debug('manual or internal position specification inactive') return self.attributes['Pos'].set_value(value) - logging.debug('Pos set to %s' % value) + _logger.debug('Pos set to %s' % value) if self.attributes['PosFbkCalc'].value: self.attributes['PosFbk'].set_value(value) else: - logging.debug('Pos cannot be set to %s (out of range)' % value) + _logger.debug('Pos cannot be set to %s (out of range)' % value) def set_pos_rbk(self, value: float): corr_value = self._correct_value(value) self.attributes['PosRbk'].set_value(corr_value) - logging.debug(f'PosRbk set to {value}') + _logger.debug(f'PosRbk set to {value}') def set_pos_fbk(self, value: float): if not self.attributes['PosFbkCalc'].value: corr_value = self._correct_value(value) self.attributes['PosFbk'].set_value(corr_value) - logging.debug(f'PosFbk set to {value}') + _logger.debug(f'PosFbk set to {value}') def set_open_fbk(self, value: bool): if not self.attributes['OpenFbkCalc'].value: self.attributes['OpenFbk'].set_value(value) - logging.debug(f'OpenFbk set to {value}') + _logger.debug(f'OpenFbk set to {value}') def set_close_fbk(self, value: bool): if not self.attributes['CloseFbkCalc'].value: self.attributes['CloseFbk'].set_value(value) - logging.debug(f'CloseFbk set to {value}') + _logger.debug(f'CloseFbk set to {value}') def set_permit(self, value: bool): if not self.attributes['PermEn'].value: value = True self.attributes['Permit'].set_value(value) - logging.debug('Permit set to %s' % value) + _logger.debug('Permit set to %s' % value) # safety position should not be activated for permit mode self.attributes['SafePosAct'].set_value(False) @@ -254,7 +256,7 @@ def set_interlock(self, value: bool): if not self.attributes['IntlEn'].value: value = True self.attributes['Interlock'].set_value(value) - logging.debug('Interlock set to %s' % value) + _logger.debug('Interlock set to %s' % value) self._expect_save_pos() def set_protect(self, value: bool): @@ -263,7 +265,7 @@ def set_protect(self, value: bool): if value: self._reset_vlv() self.attributes['Protect'].set_value(value) - logging.debug('Protect set to %s' % value) + _logger.debug('Protect set to %s' % value) self._expect_save_pos() def get_pos(self): @@ -338,7 +340,7 @@ def _go_save_pos(self): safe_pos = self.attributes['PosMin'].value self.attributes['Pos'].set_value(safe_pos) - logging.debug('Pos set to safe position %s' % safe_pos) + _logger.debug('Pos set to safe position %s' % safe_pos) if self.attributes['MonEn'].value: self.monitored_values.pos = safe_pos self.monitor_pos_thread = threading.Thread(target=self.monitor_position_reached) @@ -394,7 +396,7 @@ def monitor_static_error(self): """ while True: if self.monitored_values.stop_event_lock.is_set(): - logging.debug('static monitoring stopped') + _logger.debug('static monitoring stopped') break states, control_signals = self.compare_states_control_signals( self.attributes['MonStatTi'].value) @@ -402,7 +404,7 @@ def monitor_static_error(self): if not all(states): # if the states of valve changed if all(control_signals): # but the control signals did not change self.attributes['MonStatErr'].set_value(True) - logging.debug('Static error set to True') + _logger.debug('Static error set to True') self._handle_monitored_error() def monitor_dynamic_error(self): @@ -411,7 +413,7 @@ def monitor_dynamic_error(self): """ while True: if self.monitored_values.stop_event_lock.is_set(): - logging.debug('dynamic monitoring stopped') + _logger.debug('dynamic monitoring stopped') break states, control_signals = self.compare_states_control_signals( self.attributes['MonDynTi'].value) @@ -419,7 +421,7 @@ def monitor_dynamic_error(self): if all(states): # if the states of valve did not changed if not all(control_signals): # but a control command is executed self.attributes['MonDynErr'].set_value(True) - logging.debug('Dynamic error set to True') + _logger.debug('Dynamic error set to True') self._handle_monitored_error() def monitor_position_reached(self): @@ -432,29 +434,29 @@ def monitor_position_reached(self): self.attributes['PosReachedFbk'].set_value(PosReachedFbk) if not PosReachedFbk: self.attributes['MonPosErr'].set_value(True) - logging.debug('position error set to True') + _logger.debug('position error set to True') self._handle_monitored_error() def _handle_monitored_error(self): if self.attributes['MonSafePos']: - logging.debug('set valve to safety position') + _logger.debug('set valve to safety position') if self.attributes['SafePosEn'].value: self._go_save_pos() else: - logging.debug('Device has no safe position') + _logger.debug('Device has no safe position') self.attributes['SafePosAct'].set_value(True) def start_monitor(self): if self.attributes['MonEn'].value: self.monitor_static_thread = threading.Thread(target=self.monitor_static_error) self.monitor_static_thread.start() - logging.debug('static monitoring start') + _logger.debug('static monitoring start') self.monitor_dynamic_thread = threading.Thread(target=self.monitor_dynamic_error) self.monitor_dynamic_thread.start() - logging.debug('dynamic monitoring start') + _logger.debug('dynamic monitoring start') def set_open_aut(self, value: bool): - logging.debug('OpenAut set to %s' % value) + _logger.debug('OpenAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value: if value and self._run_allowed(): if self.attributes['MonEn'].value: @@ -470,11 +472,11 @@ def set_close_aut(self, value: bool): self.monitored_values.lock.acquire() self.monitored_values.close_aut = value self.monitored_values.lock.release() - logging.debug('CloseAut set to %s' % value) + _logger.debug('CloseAut set to %s' % value) self._run_close_vlv() def set_reset_aut(self, value: bool): - logging.debug('ResetAut set to %s' % value) + _logger.debug('ResetAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and value: if self.attributes['MonEn'].value: self.monitored_values.lock.acquire() @@ -483,7 +485,7 @@ def set_reset_aut(self, value: bool): self._reset_vlv() def set_open_op(self, value: bool): - logging.debug('OpenOp set to %s' % value) + _logger.debug('OpenOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value: if value and self._run_allowed(): if self.attributes['MonEn'].value: @@ -494,7 +496,7 @@ def set_open_op(self, value: bool): self.attributes['OpenOp'].value = False def set_close_op(self, value: bool): - logging.debug('CloseOp set to %s' % value) + _logger.debug('CloseOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value: if value and self._run_allowed(): if self.attributes['MonEn'].value: @@ -505,7 +507,7 @@ def set_close_op(self, value: bool): self.attributes['CloseOp'].value = False def set_reset_op(self, value: bool): - logging.debug('ResetOp set to %s' % value) + _logger.debug('ResetOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and value: if self.attributes['MonEn'].value: self.monitored_values.lock.acquire() @@ -525,11 +527,11 @@ def _set_pos(self, value: float): # if SafePosAct is active, safety setting for the position is accepted elif self.attributes['SafePosAct'].value or \ (self.attributes['PermEn'].value and self.attributes['Permit'].value == 0): - logging.debug('manual or internal position specification inactive') + _logger.debug('manual or internal position specification inactive') return self.attributes['Pos'].set_value(value) - logging.debug('Pos set to %s' % value) + _logger.debug('Pos set to %s' % value) # start monitor_pos_thread to after pos has been changed # if the position dose not change, there is no need to check the desired position @@ -546,7 +548,7 @@ def _set_pos(self, value: float): else: self.attributes['PosFbk'].set_value(value) else: - logging.debug('Pos cannot be set to %s (out of range)' % value) + _logger.debug('Pos cannot be set to %s (out of range)' % value) def set_stop_monitor(self): self.monitored_values.stop_event_lock.set() @@ -603,7 +605,7 @@ def _expect_save_pos(self): if self.attributes['SafePosEn'].value: self._go_safe_pos() else: - logging.debug('Device has no safe position') + _logger.debug('Device has no safe position') self.attributes['SafePosAct'].set_value(True) def _go_safe_pos(self): @@ -613,51 +615,51 @@ def _go_safe_pos(self): self._run_close_vlv() def set_open_aut(self, value: bool): - logging.debug('OpenAut set to %s' % value) + _logger.debug('OpenAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value: if value and self._run_allowed(): self._run_open_vlv() def set_close_aut(self, value: bool): - logging.debug('CloseAut set to %s' % value) + _logger.debug('CloseAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value: if value and self._run_allowed(): self._run_close_vlv() def set_reset_aut(self, value: bool): - logging.debug('ResetAut set to %s' % value) + _logger.debug('ResetAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and value: self._reset_vlv() def set_open_op(self, value: bool): - logging.debug('OpenOp set to %s' % value) + _logger.debug('OpenOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value: if value and self._run_allowed(): self._run_open_vlv() self.attributes['OpenOp'].value = False def set_close_op(self, value: bool): - logging.debug('CloseOp set to %s' % value) + _logger.debug('CloseOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value: if value and self._run_allowed(): self._run_close_vlv() self.attributes['CloseOp'].value = False def set_reset_op(self, value: bool): - logging.debug('ResetOp set to %s' % value) + _logger.debug('ResetOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and value: self._reset_vlv() self.attributes['ResetOp'].set_value(False) def _run_allowed(self): if self.attributes['PermEn'].value and self.attributes['Permit'].value == 0: - logging.debug('Permission is not given') + _logger.debug('Permission is not given') return False if self.attributes['IntlEn'].value and self.attributes['Interlock'].value == 0: - logging.debug('Interlock is active') + _logger.debug('Interlock is active') return False if self.attributes['ProtEn'].value and self.attributes['Protect'].value == 0: - logging.debug('Protect is active') + _logger.debug('Protect is active') return False return True @@ -694,25 +696,25 @@ def _reset_vlv(self): def set_open_fbk(self, value: bool): if not self.attributes['OpenFbkCalc'].value: self.attributes['OpenFbk'].set_value(value) - logging.debug('OpenFbk set to %s' % value) + _logger.debug('OpenFbk set to %s' % value) def set_close_fbk(self, value: bool): if not self.attributes['CloseFbkCalc'].value: self.attributes['CloseFbk'].set_value(value) - logging.debug('CloseFbk set to %s' % value) + _logger.debug('CloseFbk set to %s' % value) def set_permit(self, value: bool): if not self.attributes['PermEn'].value: value = True self.attributes['Permit'].set_value(value) - logging.debug('Permit set to %s' % value) + _logger.debug('Permit set to %s' % value) self.attributes['SafePosAct'].set_value(False) def set_interlock(self, value: bool): if not self.attributes['IntlEn'].value: value = True self.attributes['Interlock'].set_value(value) - logging.debug('Interlock set to %s' % value) + _logger.debug('Interlock set to %s' % value) self._expect_save_pos() def set_protect(self, value: bool): @@ -721,7 +723,7 @@ def set_protect(self, value: bool): if value: self._reset_vlv() self.attributes['Protect'].set_value(value) - logging.debug('Protect set to %s' % value) + _logger.debug('Protect set to %s' % value) self._expect_save_pos() def get_open_fbk(self): @@ -808,7 +810,7 @@ def monitor_static_error(self): """ while True: if self.monitored_values.stop_event_lock.is_set(): - logging.debug('static monitoring stopped') + _logger.debug('static monitoring stopped') break states, control_signals = self.compare_states_control_signals( self.attributes['MonStatTi'].value) @@ -816,7 +818,7 @@ def monitor_static_error(self): if not all(states): # if the states of valve changed if all(control_signals): # but the control signals did not change self.attributes['MonStatErr'].set_value(True) - logging.debug('Static error set to True') + _logger.debug('Static error set to True') self._handle_monitored_error() def monitor_dynamic_error(self): @@ -825,7 +827,7 @@ def monitor_dynamic_error(self): """ while True: if self.monitored_values.stop_event_lock.is_set(): - logging.debug('dynamic monitoring stopped') + _logger.debug('dynamic monitoring stopped') break states, control_signals = self.compare_states_control_signals( self.attributes['MonDynTi'].value) @@ -833,16 +835,16 @@ def monitor_dynamic_error(self): if all(states): # if the states of valve did not changed if not all(control_signals): # but a control command is executed self.attributes['MonDynErr'].set_value(True) - logging.debug('Dynamic error set to True') + _logger.debug('Dynamic error set to True') self._handle_monitored_error() def _handle_monitored_error(self): if self.attributes['MonSafePos']: - logging.debug('set valve to safety position') + _logger.debug('set valve to safety position') if self.attributes['SafePosEn'].value: self._go_safe_pos() else: - logging.debug('Device has no safe position') + _logger.debug('Device has no safe position') self.attributes['SafePosAct'].set_value(True) def start_monitor(self): @@ -852,13 +854,13 @@ def start_monitor(self): if self.attributes['MonEn'].value: self.monitor_static_thread = threading.Thread(target=self.monitor_static_error) self.monitor_static_thread.start() - logging.debug('static monitoring start') + _logger.debug('static monitoring start') self.monitor_dynamic_thread = threading.Thread(target=self.monitor_dynamic_error) self.monitor_dynamic_thread.start() - logging.debug('dynamic monitoring start') + _logger.debug('dynamic monitoring start') def set_open_aut(self, value: bool): - logging.debug('OpenAut set to %s' % value) + _logger.debug('OpenAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value: if value and self._run_allowed(): if self.attributes['MonEn'].value: @@ -874,11 +876,11 @@ def set_close_aut(self, value: bool): self.monitored_values.lock.acquire() self.monitored_values.close_aut = value self.monitored_values.lock.release() - logging.debug('CloseAut set to %s' % value) + _logger.debug('CloseAut set to %s' % value) self._run_close_vlv() def set_reset_aut(self, value: bool): - logging.debug('ResetAut set to %s' % value) + _logger.debug('ResetAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and value: if self.attributes['MonEn'].value: self.monitored_values.lock.acquire() @@ -887,7 +889,7 @@ def set_reset_aut(self, value: bool): self._reset_vlv() def set_open_op(self, value: bool): - logging.debug('OpenOp set to %s' % value) + _logger.debug('OpenOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value: if value and self._run_allowed(): if self.attributes['MonEn'].value: @@ -898,7 +900,7 @@ def set_open_op(self, value: bool): self.attributes['OpenOp'].value = False def set_close_op(self, value: bool): - logging.debug('CloseOp set to %s' % value) + _logger.debug('CloseOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value: if value and self._run_allowed(): if self.attributes['MonEn'].value: @@ -909,7 +911,7 @@ def set_close_op(self, value: bool): self.attributes['CloseOp'].value = False def set_reset_op(self, value: bool): - logging.debug('ResetOp set to %s' % value) + _logger.debug('ResetOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and value: if self.attributes['MonEn'].value: self.monitored_values.lock.acquire() @@ -981,64 +983,64 @@ def _go_safe_pos(self): def _run_allowed(self): if self.attributes['PermEn'].value and self.attributes['Permit'].value == 0: - logging.debug('Permission is not given') + _logger.debug('Permission is not given') return False if self.attributes['IntlEn'].value and self.attributes['Interlock'].value == 0: - logging.debug('Interlock is active') + _logger.debug('Interlock is active') return False if self.attributes['ProtEn'].value and self.attributes['Protect'].value == 0: - logging.debug('Protect is active') + _logger.debug('Protect is active') return False if not self.attributes['Trip'].value: - logging.debug('tripped') + _logger.debug('tripped') return False return True def set_fwd_op(self, value: bool): - logging.debug('FwdOp set to %s' % value) + _logger.debug('FwdOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and self.fwd_en: if value and self._run_allowed(): self._run_fwd_drv() self.attributes['FwdOp'].value = False def set_rev_op(self, value: bool): - logging.debug('RevOp set to %s' % value) + _logger.debug('RevOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and self.rev_en: if value and self._run_allowed(): self._run_rev_drv() self.attributes['RevOp'].value = False def set_stop_op(self, value: bool): - logging.debug('StopOp set to %s' % value) + _logger.debug('StopOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and value: self._stop_drv() self.attributes['StopOp'].set_value(False) def set_reset_op(self, value: bool): - logging.debug('ResetOp set to %s' % value) + _logger.debug('ResetOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and value: self._reset_drv() self.attributes['ResetOp'].set_value(False) def set_fwd_aut(self, value: bool): - logging.debug('FwdAut set to %s' % value) + _logger.debug('FwdAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and self.fwd_en: if value and self._run_allowed(): self._run_fwd_drv() def set_rev_aut(self, value: bool): - logging.debug('RevAut set to %s' % value) + _logger.debug('RevAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and self.rev_en: if value and self._run_allowed(): self._run_rev_drv() def set_stop_aut(self, value: bool): - logging.debug('StopAut set to %s' % value) + _logger.debug('StopAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and value: self._stop_drv() def set_reset_aut(self, value: bool): - logging.debug('ResetAut set to %s' % value) + _logger.debug('ResetAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and value: self._reset_drv() @@ -1075,30 +1077,30 @@ def _reset_drv(self): def set_rev_fbk(self, value: bool): if not self.attributes['RevFbkCalc'].value: self.attributes['RevFbk'].set_value(value) - logging.debug('RevFbk set to %s' % value) + _logger.debug('RevFbk set to %s' % value) def set_fwd_fbk(self, value: bool): if not self.attributes['FwdFbkCalc'].value: self.attributes['FwdFbk'].set_value(value) - logging.debug('FwdFbk set to %s' % value) + _logger.debug('FwdFbk set to %s' % value) def set_trip(self, value: bool): self.attributes['Trip'].set_value(value) - logging.debug('Trip set to %s' % value) + _logger.debug('Trip set to %s' % value) self._expect_save_pos() def set_permit(self, value: bool): if not self.attributes['PermEn'].value: value = True self.attributes['Permit'].set_value(value) - logging.debug('Permit set to %s' % value) + _logger.debug('Permit set to %s' % value) self.attributes['SafePosAct'].set_value(False) def set_interlock(self, value: bool): if not self.attributes['IntlEn'].value: value = True self.attributes['Interlock'].set_value(value) - logging.debug('Interlock set to %s' % value) + _logger.debug('Interlock set to %s' % value) self._expect_save_pos() def set_protect(self, value: bool): @@ -1107,7 +1109,7 @@ def set_protect(self, value: bool): if value: self._reset_drv() self.attributes['Protect'].set_value(value) - logging.debug('Protect set to %s' % value) + _logger.debug('Protect set to %s' % value) self._expect_save_pos() def get_fwd_fbk(self): @@ -1195,7 +1197,7 @@ def compare_states_control_signals(self, monitor_time): def monitor_static_error(self): while True: if self.monitored_values.stop_event_lock.is_set(): - logging.debug('static monitoring stopped') + _logger.debug('static monitoring stopped') break states, control_signals = self.compare_states_control_signals( self.attributes['MonStatTi'].value) @@ -1203,13 +1205,13 @@ def monitor_static_error(self): if not all(states): if all(control_signals): self.attributes['MonStatErr'].set_value(True) - logging.debug('Static error set to True') + _logger.debug('Static error set to True') self._handle_monitored_error() def monitor_dynamic_error(self): while True: if self.monitored_values.stop_event_lock.is_set(): - logging.debug('dynamic monitoring stopped') + _logger.debug('dynamic monitoring stopped') break states, control_signals = self.compare_states_control_signals( self.attributes['MonDynTi'].value) @@ -1217,11 +1219,11 @@ def monitor_dynamic_error(self): if all(states): if not all(control_signals): self.attributes['MonDynErr'].set_value(True) - logging.debug('Dynamic error set to True') + _logger.debug('Dynamic error set to True') self._handle_monitored_error() def _handle_monitored_error(self): - logging.debug('set valve to safety position') + _logger.debug('set valve to safety position') self._go_safe_pos() self.attributes['SafePosAct'].set_value(True) @@ -1229,13 +1231,13 @@ def start_monitor(self): if self.attributes['MonEn'].value: self.monitor_static_thread = threading.Thread(target=self.monitor_static_error) self.monitor_static_thread.start() - logging.debug('static monitoring start') + _logger.debug('static monitoring start') self.monitor_dynamic_thread = threading.Thread(target=self.monitor_dynamic_error) self.monitor_dynamic_thread.start() - logging.debug('dynamic monitoring start') + _logger.debug('dynamic monitoring start') def set_fwd_op(self, value: bool): - logging.debug('FwdOp set to %s' % value) + _logger.debug('FwdOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and self.fwd_en: if value and self._run_allowed(): self._run_fwd_drv() @@ -1246,7 +1248,7 @@ def set_fwd_op(self, value: bool): self.attributes['FwdOp'].value = False def set_rev_op(self, value: bool): - logging.debug('RevOp set to %s' % value) + _logger.debug('RevOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and self.rev_en: if value and self._run_allowed(): @@ -1258,7 +1260,7 @@ def set_rev_op(self, value: bool): self.attributes['RevOp'].value = False def set_stop_op(self, value: bool): - logging.debug('StopOp set to %s' % value) + _logger.debug('StopOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and value: if self.attributes['MonEn'].value: self.monitored_values.lock.acquire() @@ -1268,7 +1270,7 @@ def set_stop_op(self, value: bool): self.attributes['StopOp'].set_value(False) def set_reset_op(self, value: bool): - logging.debug('ResetOp set to %s' % value) + _logger.debug('ResetOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and value: if self.attributes['MonEn'].value: self.monitored_values.lock.acquire() @@ -1278,7 +1280,7 @@ def set_reset_op(self, value: bool): self.attributes['ResetOp'].set_value(False) def set_fwd_aut(self, value: bool): - logging.debug('FwdAut set to %s' % value) + _logger.debug('FwdAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and self.fwd_en: if value and self._run_allowed(): if self.attributes['MonEn'].value: @@ -1288,7 +1290,7 @@ def set_fwd_aut(self, value: bool): self._run_fwd_drv() def set_rev_aut(self, value: bool): - logging.debug('RevAut set to %s' % value) + _logger.debug('RevAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and self.rev_en: if value and self._run_allowed(): if self.attributes['MonEn'].value: @@ -1298,7 +1300,7 @@ def set_rev_aut(self, value: bool): self._run_rev_drv() def set_stop_aut(self, value: bool): - logging.debug('StopAut set to %s' % value) + _logger.debug('StopAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and value: if self.attributes['MonEn'].value: self.monitored_values.lock.acquire() @@ -1307,7 +1309,7 @@ def set_stop_aut(self, value: bool): self._stop_drv() def set_reset_aut(self, value: bool): - logging.debug('ResetAut set to %s' % value) + _logger.debug('ResetAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and value: if self.attributes['MonEn'].value: self.monitored_values.lock.acquire() @@ -1401,64 +1403,64 @@ def _go_save_pos(self): def _run_allowed(self): if self.attributes['PermEn'].value and self.attributes['Permit'].value == 0: - logging.debug('Permission is not given') + _logger.debug('Permission is not given') return False if self.attributes['IntlEn'].value and self.attributes['Interlock'].value == 0: - logging.debug('Interlock is active') + _logger.debug('Interlock is active') return False if self.attributes['ProtEn'].value and self.attributes['Protect'].value == 0: - logging.debug('Protect is active') + _logger.debug('Protect is active') return False if not self.attributes['Trip'].value: - logging.debug('Drive protection triggered') + _logger.debug('Drive protection triggered') return False return True def set_fwd_op(self, value: bool): - logging.debug('FwdOp set to %s' % value) + _logger.debug('FwdOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and self.fwd_en: if value and self._run_allowed(): self._run_fwd_drv() self.attributes['FwdOp'].value = False def set_rev_op(self, value: bool): - logging.debug('RevOp set to %s' % value) + _logger.debug('RevOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and self.rev_en: if value and self._run_allowed(): self._run_rev_drv() self.attributes['RevOp'].value = False def set_stop_op(self, value: bool): - logging.debug('StopOp set to %s' % value) + _logger.debug('StopOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and value: self._stop_drv() self.attributes['StopOp'].set_value(False) def set_reset_op(self, value: bool): - logging.debug('ResetOp set to %s' % value) + _logger.debug('ResetOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and value: self._reset_drv() self.attributes['ResetOp'].set_value(False) def set_fwd_aut(self, value: bool): - logging.debug('FwdAut set to %s' % value) + _logger.debug('FwdAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and self.fwd_en: if value and self._run_allowed(): self._run_fwd_drv() def set_rev_aut(self, value: bool): - logging.debug('RevAut set to %s' % value) + _logger.debug('RevAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and self.rev_en: if value and self._run_allowed(): self._run_rev_drv() def set_stop_aut(self, value: bool): - logging.debug('StopAut set to %s' % value) + _logger.debug('StopAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and value: self._stop_drv() def set_reset_aut(self, value: bool): - logging.debug('ResetAut set to %s' % value) + _logger.debug('ResetAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and value: self._reset_drv() @@ -1493,12 +1495,12 @@ def _reset_drv(self): self._stop_drv() def set_rpm_int(self, value: float): - logging.debug('RpmInt set to %s' % value) + _logger.debug('RpmInt set to %s' % value) if self.op_src_mode.attributes['SrcIntAct'].value: self._set_rpm(value) def set_rpm_man(self, value: float): - logging.debug('RpmIntMan set to %s' % value) + _logger.debug('RpmIntMan set to %s' % value) if self.op_src_mode.attributes['SrcManAct'].value: self._set_rpm(value) @@ -1519,50 +1521,50 @@ def _correct_value(self, value: float): def _set_rpm(self, value: float): if self.valid_value(value): self.attributes['Rpm'].set_value(value) - logging.debug('Rpm set to %s' % value) + _logger.debug('Rpm set to %s' % value) if self.attributes['RpmFbkCalc'].value: self.attributes['RpmFbk'].set_value(value) else: - logging.debug('Rpm cannot be set to %s (out of range)' % value) + _logger.debug('Rpm cannot be set to %s (out of range)' % value) def set_rpm_rbk(self, value: float): corr_value = self._correct_value(value) self.attributes['RpmRbk'].set_value(corr_value) - logging.debug('RpmRbk set to %s' % corr_value) + _logger.debug('RpmRbk set to %s' % corr_value) def set_rpm_fbk(self, value: float): if not self.attributes['RpmFbkCalc'].value: corr_value = self._correct_value(value) self.attributes['RpmFbk'].set_value(corr_value) - logging.debug('RpmFbk set to %s' % corr_value) + _logger.debug('RpmFbk set to %s' % corr_value) def set_rev_fbk(self, value: bool): if not self.attributes['RevFbkCalc'].value: self.attributes['RevFbk'].set_value(value) - logging.debug('RevFbk set to %s' % value) + _logger.debug('RevFbk set to %s' % value) def set_fwd_fbk(self, value: bool): if not self.attributes['FwdFbkCalc'].value: self.attributes['FwdFbk'].set_value(value) - logging.debug('FwdFbk set to %s' % value) + _logger.debug('FwdFbk set to %s' % value) def set_trip(self, value: bool): self.attributes['Trip'].set_value(value) - logging.debug('Trip set to %s' % value) + _logger.debug('Trip set to %s' % value) self._expect_save_pos() def set_permit(self, value: bool): if not self.attributes['PermEn'].value: value = True self.attributes['Permit'].set_value(value) - logging.debug('Permit set to %s' % value) + _logger.debug('Permit set to %s' % value) self._expect_save_pos() def set_interlock(self, value: bool): if not self.attributes['IntlEn'].value: value = True self.attributes['Interlock'].set_value(value) - logging.debug('Interlock set to %s' % value) + _logger.debug('Interlock set to %s' % value) self._expect_save_pos() def set_protect(self, value: bool): @@ -1571,7 +1573,7 @@ def set_protect(self, value: bool): if value: self._reset_drv() self.attributes['Protect'].set_value(value) - logging.debug('Protect set to %s' % value) + _logger.debug('Protect set to %s' % value) self._expect_save_pos() def get_rpm(self): @@ -1685,7 +1687,7 @@ def compare_states_control_signals(self, monitor_time): def monitor_static_error(self): while True: if self.monitored_values.stop_event_lock.is_set(): - logging.debug('static monitoring stopped') + _logger.debug('static monitoring stopped') break states, control_signals = self.compare_states_control_signals( self.attributes['MonStatTi'].value) @@ -1693,13 +1695,13 @@ def monitor_static_error(self): if not all(states): if all(control_signals): self.attributes['MonStatErr'].set_value(True) - logging.debug('Static error set to True') + _logger.debug('Static error set to True') self._handle_monitored_error() def monitor_dynamic_error(self): while True: if self.monitored_values.stop_event_lock.is_set(): - logging.debug('dynamic monitoring stopped') + _logger.debug('dynamic monitoring stopped') break states, control_signals = self.compare_states_control_signals( self.attributes['MonDynTi'].value) @@ -1707,18 +1709,18 @@ def monitor_dynamic_error(self): if all(states): if not all(control_signals): self.attributes['MonDynErr'].set_value(True) - logging.debug('Dynamic error set to True') + _logger.debug('Dynamic error set to True') self._handle_monitored_error() def monitor_rpm_error(self): while True: if self.monitored_values.stop_event_lock.is_set(): - logging.debug('rpm monitoring stopped') + _logger.debug('rpm monitoring stopped') break rpm_error = self.monitored_values.rpm - self.attributes['RpmFbk'].value if rpm_error: self.attributes['RpmErr'].set_value(rpm_error) - logging.debug(f'Rpm error set to {rpm_error}') + _logger.debug(f'Rpm error set to {rpm_error}') self._handle_monitored_error() sleep(0.01) @@ -1726,31 +1728,31 @@ def monitor_rpm_error(self): def monitor_rpm_high_limit(self): while True: if self.monitored_values.stop_event_lock.is_set(): - logging.debug('rpm high limit monitoring stopped') + _logger.debug('rpm high limit monitoring stopped') break self.monitored_values.lock.acquire() high_limit_alarm = self.attributes['RpmFbk'].value > self.attributes['RpmAHLim'].value self.monitored_values.lock.release() if high_limit_alarm: self.attributes['RpmAHAct'].set_value(True) - logging.debug('Rpm limit high set to True') + _logger.debug('Rpm limit high set to True') sleep(0.01) def monitor_rpm_low_limit(self): while True: if self.monitored_values.stop_event_lock.is_set(): - logging.debug('rpm low limit monitoring stopped') + _logger.debug('rpm low limit monitoring stopped') break self.monitored_values.lock.acquire() low_limit_alarm = self.attributes['RpmFbk'].value < self.attributes['RpmALLim'].value self.monitored_values.lock.release() if low_limit_alarm: self.attributes['RpmALAct'].set_value(True) - logging.debug('Rpm limit low set to True') + _logger.debug('Rpm limit low set to True') sleep(0.01) def _handle_monitored_error(self): - logging.debug('set valve to safety position') + _logger.debug('set valve to safety position') self._go_save_pos() self.attributes['SafePosAct'].set_value(True) @@ -1758,29 +1760,29 @@ def start_monitor(self): if self.attributes['MonEn'].value: self.monitor_static_thread = threading.Thread(target=self.monitor_static_error) self.monitor_static_thread.start() - logging.debug('static monitoring start') + _logger.debug('static monitoring start') self.monitor_dynamic_thread = threading.Thread(target=self.monitor_dynamic_error) self.monitor_dynamic_thread.start() - logging.debug('dynamic monitoring start') + _logger.debug('dynamic monitoring start') self.monitor_rpm_error_thread = threading.Thread(target=self.monitor_rpm_error) self.monitor_rpm_error_thread.start() - logging.debug('rpm error monitoring start') + _logger.debug('rpm error monitoring start') if self.attributes['RpmAHEn'].value: self.monitor_rpm_limit_high_thread = threading.Thread( target=self.monitor_rpm_high_limit) self.monitor_rpm_limit_high_thread.start() - logging.debug('rpm high limit monitoring start') + _logger.debug('rpm high limit monitoring start') if self.attributes['RpmALEn'].value: self.monitor_rpm_limit_low_thread = threading.Thread(target=self.monitor_rpm_low_limit) self.monitor_rpm_limit_low_thread.start() - logging.debug('rpm low limit monitoring start') + _logger.debug('rpm low limit monitoring start') def set_fwd_op(self, value: bool): - logging.debug('FwdOp set to %s' % value) + _logger.debug('FwdOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and self.fwd_en: if value and self._run_allowed(): self._run_fwd_drv() @@ -1791,7 +1793,7 @@ def set_fwd_op(self, value: bool): self.attributes['FwdOp'].value = False def set_rev_op(self, value: bool): - logging.debug('RevOp set to %s' % value) + _logger.debug('RevOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and self.rev_en: if value and self._run_allowed(): @@ -1803,7 +1805,7 @@ def set_rev_op(self, value: bool): self.attributes['RevOp'].value = False def set_stop_op(self, value: bool): - logging.debug('StopOp set to %s' % value) + _logger.debug('StopOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and value: if self.attributes['MonEn'].value: self.monitored_values.lock.acquire() @@ -1813,7 +1815,7 @@ def set_stop_op(self, value: bool): self.attributes['StopOp'].set_value(False) def set_reset_op(self, value: bool): - logging.debug('ResetOp set to %s' % value) + _logger.debug('ResetOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and value: if self.attributes['MonEn'].value: self.monitored_values.lock.acquire() @@ -1823,7 +1825,7 @@ def set_reset_op(self, value: bool): self.attributes['ResetOp'].set_value(False) def set_fwd_aut(self, value: bool): - logging.debug('FwdAut set to %s' % value) + _logger.debug('FwdAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and self.fwd_en: if value and self._run_allowed(): if self.attributes['MonEn'].value: @@ -1833,7 +1835,7 @@ def set_fwd_aut(self, value: bool): self._run_fwd_drv() def set_rev_aut(self, value: bool): - logging.debug('RevAut set to %s' % value) + _logger.debug('RevAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and self.rev_en: if value and self._run_allowed(): if self.attributes['MonEn'].value: @@ -1843,7 +1845,7 @@ def set_rev_aut(self, value: bool): self._run_rev_drv() def set_stop_aut(self, value: bool): - logging.debug('StopAut set to %s' % value) + _logger.debug('StopAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and value: if self.attributes['MonEn'].value: self.monitored_values.lock.acquire() @@ -1852,7 +1854,7 @@ def set_stop_aut(self, value: bool): self._stop_drv() def set_reset_aut(self, value: bool): - logging.debug('ResetAut set to %s' % value) + _logger.debug('ResetAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and value: if self.attributes['MonEn'].value: self.monitored_values.lock.acquire() @@ -1867,7 +1869,7 @@ def _set_rpm(self, value: float): self.monitored_values.lock.acquire() self.monitored_values.rpm = value self.monitored_values.lock.release() - logging.debug('Rpm set to %s' % value) + _logger.debug('Rpm set to %s' % value) if self.attributes['RpmFbkCalc'].value: if self.attributes['MonEn'].value: @@ -1878,7 +1880,7 @@ def _set_rpm(self, value: float): self.attributes['RpmFbk'].set_value(value) else: - logging.debug('Rpm cannot be set to %s (out of range)' % value) + _logger.debug('Rpm cannot be set to %s (out of range)' % value) def set_stop_monitor(self): self.monitored_values.stop_event_lock.set() @@ -1907,21 +1909,21 @@ def set_limits(self, mv_min, mv_max): self.ctrl.output_limits = (mv_min, mv_max) def start(self): - logging.debug('Starting PID controller...') + _logger.debug('Starting PID controller...') self.ctrl.setpoint = self.sp self.stopped = False self.stop_flag = False self.thread.start() - logging.debug('Started') + _logger.debug('Started') def stop(self): - logging.debug('Stopping PID controller...') + _logger.debug('Stopping PID controller...') self.stopped = False self.stop_flag = True while not self.stopped: sleep(0.1) self.ctrl.reset() - logging.debug('Stopped') + _logger.debug('Stopped') def set_sp(self, sp): self.sp = sp @@ -2038,31 +2040,31 @@ def _set_sp(self, value, v_min, v_max): if self._valid_value(value, v_min, v_max): self.attributes['SP'].set_value(value) self.ctrl.set_sp(value) - logging.debug('SP set to %s' % value) + _logger.debug('SP set to %s' % value) else: - logging.debug('SP cannot be set to %s (out of range)' % value) + _logger.debug('SP cannot be set to %s (out of range)' % value) def set_sp_man(self, value): - logging.debug('SPMan set to %s' % value) + _logger.debug('SPMan set to %s' % value) if self.op_src_mode.attributes['SrcManAct'].value and self.op_src_mode.attributes['StateAutAct'].value: self._set_sp(value, self.sp_man_min, self.sp_man_max) def set_sp_int(self, value): - logging.debug('SPInt set to %s' % value) + _logger.debug('SPInt set to %s' % value) if self.op_src_mode.attributes['SrcIntAct'].value and self.op_src_mode.attributes['StateAutAct'].value: self._set_sp(value, self.sp_int_min, self.sp_int_max) def set_mv_man(self, value): - logging.debug('MVMan set to %s' % value) + _logger.debug('MVMan set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value: self._set_mv(value, self.mv_min, self.mv_max) def _set_mv(self, value, v_min, v_max): if self._valid_value(value, v_min, v_max): self.attributes['MV'].set_value(value) - logging.debug('MV set to %s' % value) + _logger.debug('MV set to %s' % value) else: - logging.debug('MV cannot be set to %s (out of range)' % value) + _logger.debug('MV cannot be set to %s (out of range)' % value) def _update_mv_int_loop(self): while not self._stop_update_mv_flag: diff --git a/src/mtppy/attribute.py b/src/mtppy/attribute.py index 5a59873..e29f65b 100644 --- a/src/mtppy/attribute.py +++ b/src/mtppy/attribute.py @@ -1,5 +1,7 @@ import logging +_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") + class Attribute: def __init__(self, name: str, data_type, init_value, sub_cb=None): @@ -34,7 +36,7 @@ def set_value(self, value): if self.comm_obj.write_value_callback is not None: self.comm_obj.write_value_callback(self.value) - logging.debug(f'New value for {self.name} is {self.value}') + _logger.debug(f'New value for {self.name} is {self.value}') return True def _correct_type(self, value): diff --git a/src/mtppy/command_en_control.py b/src/mtppy/command_en_control.py index cc6ea9c..23a45cc 100644 --- a/src/mtppy/command_en_control.py +++ b/src/mtppy/command_en_control.py @@ -1,5 +1,7 @@ import logging +_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") + class CommandEnControl: def __init__(self): @@ -101,7 +103,7 @@ def execute(self, state: str): :param state: Given state. :return: """ - logging.debug(f'CommandEn changed to correspond {state}') + _logger.debug(f'CommandEn changed to correspond {state}') exec(f'self._execute_{state}()') def _execute_undefined(self): diff --git a/src/mtppy/indicator_elements.py b/src/mtppy/indicator_elements.py index 8dab9b2..abf932a 100644 --- a/src/mtppy/indicator_elements.py +++ b/src/mtppy/indicator_elements.py @@ -3,6 +3,8 @@ from MTPPy_Async.src.mtppy.attribute import Attribute from MTPPy_Async.src.mtppy.suc_data_assembly import SUCIndicatorElement +_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") + class AnaView(SUCIndicatorElement): def __init__(self, tag_name: str, tag_description: str = '', v_scl_min: float = 0, v_scl_max: float = 100, @@ -23,7 +25,7 @@ def __init__(self, tag_name: str, tag_description: str = '', v_scl_min: float = def set_v(self, value): self.attributes['V'].set_value(value) - logging.debug('V set to %s' % value) + _logger.debug('V set to %s' % value) class BinView(SUCIndicatorElement): @@ -42,7 +44,7 @@ def __init__(self, tag_name: str, tag_description: str = '', v_state_0: str = 'f def set_v(self, value): self.attributes['V'].set_value(value) - logging.debug('V set to %s' % value) + _logger.debug('V set to %s' % value) class DIntView(SUCIndicatorElement): @@ -64,7 +66,7 @@ def __init__(self, tag_name: str, tag_description: str = '', v_scl_min: int = 0, def set_v(self, value): self.attributes['V'].set_value(value) - logging.debug('V set to %s' % value) + _logger.debug('V set to %s' % value) class StringView(SUCIndicatorElement): @@ -78,4 +80,4 @@ def __init__(self, tag_name: str, tag_description: str = ''): def set_v(self, value): self.attributes['V'].set_value(value) - logging.debug('V set to %s' % value) + _logger.debug('V set to %s' % value) diff --git a/src/mtppy/opcua_server_pea.py b/src/mtppy/opcua_server_pea.py index 33d69b7..5e88b3b 100644 --- a/src/mtppy/opcua_server_pea.py +++ b/src/mtppy/opcua_server_pea.py @@ -5,6 +5,8 @@ from MTPPy_Async.src.mtppy.suc_data_assembly import SUCDataAssembly, SUCActiveElement from MTPPy_Async.src.mtppy.mtp_generator import MTPGenerator +_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") + class OPCUAServerPEA: def __init__(self, mtp_generator: MTPGenerator = None, endpoint: str = 'opc.tcp://127.0.0.1:4840/'): @@ -44,7 +46,7 @@ def _init_opcua_server(self): Initialises an OPC UA server and sets the endpoint. :return: """ - logging.info(f'Initialisation of OPC UA server: {self.endpoint}') + _logger.info(f'Initialisation of OPC UA server: {self.endpoint}') self.opcua_server = Server() self.opcua_server.set_endpoint(self.endpoint) # self.opcua_ns = self.opcua_server.register_namespace('namespace_idx') @@ -81,7 +83,7 @@ def _build_opcua_server(self): Creates an OPC UA server instance including required nodes according to defined data assemblies. :return: """ - logging.info(f'Adding OPC UA nodes to the server structure according to the PEA structure:') + _logger.info(f'Adding OPC UA nodes to the server structure according to the PEA structure:') ns = self.opcua_ns server = self.opcua_server.get_objects_node() @@ -97,13 +99,13 @@ def _build_opcua_server(self): self.mtp.add_opcua_server(self.endpoint) for service in self.service_set.values(): - logging.info(f'- service {service.tag_name}') + _logger.info(f'- service {service.tag_name}') self._create_opcua_objects_for_folders(service, services_node_id, services_node) act_elem_node_id = f'ns={ns};s=active_elements' act_elem_node = server.add_folder(act_elem_node_id, "active_elements") for active_element in self.active_elements.values(): - logging.info(f'- active element {active_element.tag_name}') + _logger.info(f'- active element {active_element.tag_name}') self._create_opcua_objects_for_folders(active_element, act_elem_node_id, act_elem_node) # add SupportedRoleClass to all InternalElements @@ -112,7 +114,7 @@ def _build_opcua_server(self): # export manifest.aml if self.mtp: - logging.info(f'MTP manifest export to {self.mtp.export_path}') + _logger.info(f'MTP manifest export to {self.mtp.export_path}') self.mtp.export_manifest() def _create_opcua_objects_for_folders(self, data_assembly: SUCDataAssembly, parent_opcua_prefix: str, parent_opcua_object): @@ -183,7 +185,7 @@ def _create_opcua_objects_for_leaves(self, opcua_object, parent_opcua_prefix: st opcua_type = self._infer_data_type(attr.type) opcua_node_obj = parent_opcua_object.add_variable(attribute_node_id, attr.name, attr.init_value, varianttype=opcua_type) - logging.debug( + _logger.debug( f'OPCUA Node: {attribute_node_id}, Name: {attr.name}, Value: {attr.init_value}') opcua_node_obj.set_writable(False) opcua_comm_obj = OPCUACommunicationObject(opcua_node_obj, node_id=opcua_node_obj) @@ -317,7 +319,7 @@ def datachange_notification(self, node, val, data): callback(val) except Exception as exc: pass - logging.warning(f'Something wrong with callback {callback}: {exc}') + _logger.warning(f'Something wrong with callback {callback}: {exc}') def find_set_callback(self, node_id): """ diff --git a/src/mtppy/operation_elements.py b/src/mtppy/operation_elements.py index 41ea8ee..133a536 100644 --- a/src/mtppy/operation_elements.py +++ b/src/mtppy/operation_elements.py @@ -4,6 +4,8 @@ from MTPPy_Async.src.mtppy.operation_source_mode import OperationSourceMode from MTPPy_Async.src.mtppy.suc_data_assembly import SUCOperationElement +_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") + class AnaServParam(SUCOperationElement): def __init__(self, tag_name: str, tag_description: str = '', v_min: float = 0, v_max: float = 100, @@ -35,17 +37,17 @@ def __init__(self, tag_name: str, tag_description: str = '', v_min: float = 0, v self._add_attribute(Attribute('Sync', bool, False)) def set_v_op(self, value): - logging.debug('VOp set to %s' % value) + _logger.debug('VOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct']: self.set_v_req(value) def set_v_int(self, value): - logging.debug('VInt set to %s' % value) + _logger.debug('VInt set to %s' % value) if self.op_src_mode.attributes['StateAutAct'] and self.op_src_mode.attributes['SrcIntAct']: self.set_v_req(value) def set_v_ext(self, value): - logging.debug('VExt set to %s' % value) + _logger.debug('VExt set to %s' % value) if self.op_src_mode.attributes['StateAutAct'] and self.op_src_mode.attributes['SrcExtAct']: self.set_v_req(value) @@ -58,22 +60,22 @@ def valid_value(self, value): def set_v_req(self, value): if self.valid_value(value): self.attributes['VReq'].set_value(value) - logging.debug('VReq set to %s' % value) + _logger.debug('VReq set to %s' % value) else: - logging.debug('VReq cannot be set to %s (out of range)' % value) + _logger.debug('VReq cannot be set to %s (out of range)' % value) def set_v_out(self): v_req = self.attributes['VReq'].value self.attributes['VOut'].set_value(v_req) self.set_v_fbk(v_req) - logging.debug('VOut set to %s' % v_req) + _logger.debug('VOut set to %s' % v_req) def get_v_out(self): return self.attributes['VOut'].value def set_v_fbk(self, value): self.attributes['VFbk'].set_value(value) - logging.debug('VFbk set to %s' % value) + _logger.debug('VFbk set to %s' % value) class BinServParam(SUCOperationElement): @@ -99,36 +101,36 @@ def __init__(self, tag_name: str, tag_description: str = '', v_state_0: str = 'f self._add_attribute(Attribute('Sync', bool, False)) def set_v_op(self, value): - logging.debug('VOp set to %s' % value) + _logger.debug('VOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct']: self.set_v_req(value) def set_v_int(self, value): - logging.debug('VInt set to %s' % value) + _logger.debug('VInt set to %s' % value) if self.op_src_mode.attributes['StateAutAct'] and self.op_src_mode.attributes['SrcIntAct']: self.set_v_req(value) def set_v_ext(self, value): - logging.debug('VExt set to %s' % value) + _logger.debug('VExt set to %s' % value) if self.op_src_mode.attributes['StateAutAct'] and self.op_src_mode.attributes['SrcExtAct']: self.set_v_req(value) def set_v_req(self, value): self.attributes['VReq'].set_value(value) - logging.debug('VReq set to %s' % value) + _logger.debug('VReq set to %s' % value) def set_v_out(self): v_req = self.attributes['VReq'].value self.attributes['VOut'].set_value(v_req) self.set_v_fbk(v_req) - logging.debug('VOut set to %s' % v_req) + _logger.debug('VOut set to %s' % v_req) def get_v_out(self): return self.attributes['VOut'].value def set_v_fbk(self, value): self.attributes['VFbk'].set_value(value) - logging.debug('VFbk set to %s' % value) + _logger.debug('VFbk set to %s' % value) class DIntServParam(SUCOperationElement): @@ -161,17 +163,17 @@ def __init__(self, tag_name: str, tag_description: str = '', v_min: int = 0, v_m self._add_attribute(Attribute('Sync', bool, False)) def set_v_op(self, value): - logging.debug('VOp set to %s' % value) + _logger.debug('VOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct']: self.set_v_req(value) def set_v_int(self, value): - logging.debug('VInt set to %s' % value) + _logger.debug('VInt set to %s' % value) if self.op_src_mode.attributes['StateAutAct'] and self.op_src_mode.attributes['SrcIntAct']: self.set_v_req(value) def set_v_ext(self, value): - logging.debug('VExt set to %s' % value) + _logger.debug('VExt set to %s' % value) if self.op_src_mode.attributes['StateAutAct'] and self.op_src_mode.attributes['SrcExtAct']: self.set_v_req(value) @@ -184,22 +186,22 @@ def valid_value(self, value): def set_v_req(self, value): if self.valid_value(value): self.attributes['VReq'].set_value(value) - logging.debug('VReq set to %s' % value) + _logger.debug('VReq set to %s' % value) else: - logging.debug('VReq cannot be set to %s (out of range)' % value) + _logger.debug('VReq cannot be set to %s (out of range)' % value) def set_v_out(self): v_req = self.attributes['VReq'].value self.attributes['VOut'].set_value(v_req) self.set_v_fbk(v_req) - logging.debug('VOut set to %s' % v_req) + _logger.debug('VOut set to %s' % v_req) def get_v_out(self): return self.attributes['VOut'].value def set_v_fbk(self, value): self.attributes['VFbk'].set_value(value) - logging.debug('VFbk set to %s' % value) + _logger.debug('VFbk set to %s' % value) class StringServParam(SUCOperationElement): @@ -220,17 +222,17 @@ def __init__(self, tag_name: str, tag_description: str = ''): self._add_attribute(Attribute('Sync', bool, False)) def set_v_op(self, value): - logging.debug('VOp set to %s' % value) + _logger.debug('VOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct']: self.set_v_req(value) def set_v_int(self, value): - logging.debug('VInt set to %s' % value) + _logger.debug('VInt set to %s' % value) if self.op_src_mode.attributes['StateAutAct'] and self.op_src_mode.attributes['SrcIntAct']: self.set_v_req(value) def set_v_ext(self, value): - logging.debug('VExt set to %s' % value) + _logger.debug('VExt set to %s' % value) if self.op_src_mode.attributes['StateAutAct'] and self.op_src_mode.attributes['SrcExtAct']: self.set_v_req(value) @@ -240,19 +242,19 @@ def valid_value(self, value): def set_v_req(self, value): if self.valid_value(value): self.attributes['VReq'].set_value(value) - logging.debug('VReq set to %s' % value) + _logger.debug('VReq set to %s' % value) else: - logging.debug('VReq cannot be set to %s (out of range)' % value) + _logger.debug('VReq cannot be set to %s (out of range)' % value) def set_v_out(self): v_req = self.attributes['VReq'].value self.attributes['VOut'].set_value(v_req) self.set_v_fbk(v_req) - logging.debug('VOut set to %s' % v_req) + _logger.debug('VOut set to %s' % v_req) def get_v_out(self): return self.attributes['VOut'].value def set_v_fbk(self, value): self.attributes['VFbk'].set_value(value) - logging.debug('VFbk set to %s' % value) + _logger.debug('VFbk set to %s' % value) diff --git a/src/mtppy/operation_source_mode.py b/src/mtppy/operation_source_mode.py index ea99daa..5dc0935 100644 --- a/src/mtppy/operation_source_mode.py +++ b/src/mtppy/operation_source_mode.py @@ -2,6 +2,8 @@ from MTPPy_Async.src.mtppy.attribute import Attribute +_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") + class OperationSourceMode: def __init__(self): @@ -62,32 +64,32 @@ def add_exit_automatic_callback(self, callback: callable): def _enter_off(self): if len(self.enter_offline_callbacks): - logging.debug('Applying enter offline mode callbacks') + _logger.debug('Applying enter offline mode callbacks') [cb() for cb in self.enter_offline_callbacks] def _exit_off(self): if len(self.exit_offline_callbacks): - logging.debug('Applying exit offline mode callbacks') + _logger.debug('Applying exit offline mode callbacks') [cb() for cb in self.exit_offline_callbacks] def _enter_op(self): if len(self.enter_operator_callbacks): - logging.debug('Applying enter operator mode callbacks') + _logger.debug('Applying enter operator mode callbacks') [cb() for cb in self.enter_operator_callbacks] def _exit_op(self): if len(self.exit_operator_callbacks): - logging.debug('Applying exit operator mode callbacks') + _logger.debug('Applying exit operator mode callbacks') [cb() for cb in self.exit_operator_callbacks] def _enter_aut(self): if len(self.enter_automatic_callbacks): - logging.debug('Applying enter automatic mode callbacks') + _logger.debug('Applying enter automatic mode callbacks') [cb() for cb in self.enter_automatic_callbacks] def _exit_aut(self): if len(self.exit_automatic_callbacks): - logging.debug('Applying exit automatic mode callbacks') + _logger.debug('Applying exit automatic mode callbacks') [cb() for cb in self.exit_automatic_callbacks] def _opmode_to_off(self): @@ -105,7 +107,7 @@ def _opmode_to_off(self): self._exit_aut() self._enter_off() - logging.debug('Operation mode is now off') + _logger.debug('Operation mode is now off') self._src_to_off() def _opmode_to_aut(self): @@ -123,7 +125,7 @@ def _opmode_to_aut(self): self._exit_op() self._enter_aut() - logging.debug('Operation mode is now aut') + _logger.debug('Operation mode is now aut') self._src_to_int() def _opmode_to_op(self): @@ -140,46 +142,46 @@ def _opmode_to_op(self): self._exit_aut() self._enter_op() - logging.debug('Operation mode is now op') + _logger.debug('Operation mode is now op') self._src_to_off() def set_state_channel(self, value: bool): - logging.debug('Operation mode channel is now %s' % value) + _logger.debug('Operation mode channel is now %s' % value) def set_state_aut_aut(self, value: bool): - logging.debug(f'StateAutAut set to {value}') + _logger.debug(f'StateAutAut set to {value}') if self.attributes['StateChannel'].value and value: if self.attributes['StateOffAct'] or self.attributes['StateOpAct']: self._opmode_to_aut() def set_state_aut_op(self, value: bool): - logging.debug(f'StateAutOp set to {value}') + _logger.debug(f'StateAutOp set to {value}') if not self.attributes['StateChannel'].value and value: if self.attributes['StateOffAct'] or self.attributes['StateOpAct']: self._opmode_to_aut() self.attributes['StateAutOp'].set_value(False) def set_state_off_aut(self, value: bool): - logging.debug(f'StateOffAut set to {value}') + _logger.debug(f'StateOffAut set to {value}') if self.attributes['StateChannel'].value and value and self.switch_to_offline_mode_allowed: if self.attributes['StateAutAct'] or self.attributes['StateOpAct']: self._opmode_to_off() def set_state_off_op(self, value: bool): - logging.debug(f'StateOffOp set to {value}') + _logger.debug(f'StateOffOp set to {value}') if not self.attributes['StateChannel'].value and value and self.switch_to_offline_mode_allowed: if self.attributes['StateAutAct'] or self.attributes['StateOpAct']: self._opmode_to_off() self.attributes['StateOffOp'].set_value(False) def set_state_op_aut(self, value: bool): - logging.debug(f'StateOpAut set to {value}') + _logger.debug(f'StateOpAut set to {value}') if self.attributes['StateChannel'].value and value: if self.attributes['StateOffAct'] or self.attributes['StateOpAct']: self._opmode_to_op() def set_state_op_op(self, value: bool): - logging.debug(f'StateOpOp set to {value}') + _logger.debug(f'StateOpOp set to {value}') if not self.attributes['StateChannel'].value and value: if self.attributes['StateOffAct'] or self.attributes['StateAutAct']: self._opmode_to_op() @@ -188,20 +190,20 @@ def set_state_op_op(self, value: bool): def _src_to_off(self): self.attributes['SrcIntAct'].set_value(False) self.attributes['SrcExtAct'].set_value(False) - logging.debug('Source mode is now off') + _logger.debug('Source mode is now off') def _src_to_int(self): self.attributes['SrcIntAct'].set_value(True) self.attributes['SrcExtAct'].set_value(False) - logging.debug('Source mode is now int') + _logger.debug('Source mode is now int') def _src_to_ext(self): self.attributes['SrcIntAct'].set_value(False) self.attributes['SrcExtAct'].set_value(True) - logging.debug('Source mode is now ext') + _logger.debug('Source mode is now ext') def set_src_channel(self, value: bool): - logging.debug('Source mode channel is now %s' % value) + _logger.debug('Source mode channel is now %s' % value) def set_src_ext_aut(self, value: bool): if not self.attributes['StateOffAct'].value and value: @@ -256,20 +258,20 @@ def __init__(self): def _src_to_off(self): self.attributes['SrcIntAct'].set_value(False) self.attributes['SrcManAct'].set_value(False) - logging.debug('Source mode is now off') + _logger.debug('Source mode is now off') def _src_to_int(self): self.attributes['SrcIntAct'].set_value(True) self.attributes['SrcManAct'].set_value(False) - logging.debug('Source mode is now int') + _logger.debug('Source mode is now int') def _src_to_man(self): self.attributes['SrcIntAct'].set_value(False) self.attributes['SrcManAct'].set_value(True) - logging.debug('Source mode is now man') + _logger.debug('Source mode is now man') def set_src_channel(self, value: bool): - logging.debug('Source mode channel is now %s' % value) + _logger.debug('Source mode channel is now %s' % value) def set_src_man_aut(self, value: bool): if not self.attributes['StateOffAct'].value and value: diff --git a/src/mtppy/procedure.py b/src/mtppy/procedure.py index 7790212..ebda96a 100644 --- a/src/mtppy/procedure.py +++ b/src/mtppy/procedure.py @@ -2,6 +2,8 @@ from MTPPy_Async.src.mtppy.suc_data_assembly import * +_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") + class Procedure(SUCServiceProcedure): def __init__(self, procedure_id: int, tag_name: str, tag_description: str = '', is_self_completing: bool = False, @@ -66,6 +68,6 @@ def apply_procedure_parameters(self): Applies procedure parameters. :return: """ - logging.debug('Applying procedure parameters') + _logger.debug('Applying procedure parameters') for procedure_parameter in self.procedure_parameters.values(): procedure_parameter.set_v_out() diff --git a/src/mtppy/procedure_control.py b/src/mtppy/procedure_control.py index 68eae2b..e2a5d12 100644 --- a/src/mtppy/procedure_control.py +++ b/src/mtppy/procedure_control.py @@ -3,6 +3,8 @@ from MTPPy_Async.src.mtppy.attribute import Attribute from MTPPy_Async.src.mtppy.operation_source_mode import OperationSourceMode +_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") + class ProcedureControl: def __init__(self, procedures: dict, service_op_src_mode: OperationSourceMode): @@ -24,17 +26,17 @@ def __init__(self, procedures: dict, service_op_src_mode: OperationSourceMode): self.default_procedure_id = None def set_procedure_op(self, value: int): - logging.debug('ProcedureOP set to %s' % value) + _logger.debug('ProcedureOP set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value: self.set_procedure_req(value) def set_procedure_int(self, value: int): - logging.debug('ProcedureInt set to %s' % value) + _logger.debug('ProcedureInt set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and self.op_src_mode.attributes['SrcIntAct'].value: self.set_procedure_req(value) def set_procedure_ext(self, value: int): - logging.debug('ProcedureExt set to %s' % value) + _logger.debug('ProcedureExt set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and self.op_src_mode.attributes['SrcExtAct'].value: self.set_procedure_req(value) @@ -47,14 +49,14 @@ def valid_value(self, value: int): def set_procedure_req(self, value: int): if self.valid_value(value): self.attributes['ProcedureReq'].set_value(value) - logging.debug('ProcedureReq set to %s' % value) + _logger.debug('ProcedureReq set to %s' % value) else: - logging.debug('ProcedureReq cannot be set to %s (out of range)' % value) + _logger.debug('ProcedureReq cannot be set to %s (out of range)' % value) def set_procedure_cur(self): procedure_req = self.attributes['ProcedureReq'].value self.attributes['ProcedureCur'].set_value(procedure_req) - logging.debug('ProcedureCur set to %s' % procedure_req) + _logger.debug('ProcedureCur set to %s' % procedure_req) def get_procedure_cur(self): return self.attributes['ProcedureCur'].value diff --git a/src/mtppy/service.py b/src/mtppy/service.py index 85eb15e..80217be 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -11,6 +11,8 @@ from MTPPy_Async.src.mtppy.procedure import Procedure from MTPPy_Async.src.mtppy.suc_data_assembly import SUCOperationElement +_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") + StateCodes = StateCodes() @@ -81,7 +83,7 @@ def apply_configuration_parameters(self): Applies configuration parameters. :return: """ - logging.debug('Applying service configuration parameters') + _logger.debug('Applying service configuration parameters') for configuration_parameter in self.configuration_parameters.values(): configuration_parameter.set_v_out() diff --git a/src/mtppy/state_machine.py b/src/mtppy/state_machine.py index c905f3b..39184b6 100644 --- a/src/mtppy/state_machine.py +++ b/src/mtppy/state_machine.py @@ -9,6 +9,8 @@ StateCodes = StateCodes() CommandCodes = CommandCodes() +_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") + class StateMachine: def __init__(self, operation_source_mode: OperationSourceMode, @@ -52,16 +54,16 @@ def set_command_ext(self, value: int): def command_execution(self, com_var: int): if com_var not in CommandCodes.get_list_int(): - logging.debug(f'Command Code {com_var} does not exist') + _logger.debug(f'Command Code {com_var} does not exist') return cmd_str = CommandCodes.int_code[com_var] if not self.command_en_ctrl.is_enabled(cmd_str): - logging.debug( + _logger.debug( f'CommandEn does not permit to execute {cmd_str} from state {self.get_current_state_str()}') return else: - logging.debug(f'CommandEn permits to execute {cmd_str}') + _logger.debug(f'CommandEn permits to execute {cmd_str}') eval(f'self.{CommandCodes.int_code[com_var]}()') @@ -138,7 +140,7 @@ def _change_state_to(self, new_state: int): self.command_en_ctrl.execute(new_state_str) self.attributes['CommandEn'].set_value(self.command_en_ctrl.get_command_en()) self.execution_routine() - logging.debug(f'Service state changed to {new_state}') + _logger.debug(f'Service state changed to {new_state}') def get_current_state_str(self): return StateCodes.int_code[self.act_state] diff --git a/src/mtppy/thread_control.py b/src/mtppy/thread_control.py index a402980..0bf713c 100644 --- a/src/mtppy/thread_control.py +++ b/src/mtppy/thread_control.py @@ -5,6 +5,8 @@ from apscheduler.schedulers.asyncio import AsyncIOScheduler +_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") + class ThreadControl: def __init__(self, scheduler: callable = None): @@ -22,12 +24,12 @@ def __init__(self, scheduler: callable = None): self.callback_function = None def request_state(self, state: str, cb_function: callable): - logging.debug(f'State {state} requested') + _logger.debug(f'State {state} requested') self.requested_state = state self.callback_function = cb_function def reallocate_running_thread(self): - logging.debug(f'Reallocate thread to state {self.requested_state}') + _logger.debug(f'Reallocate thread to state {self.requested_state}') if self.requested_state is not self.running_state: self.job_event.set() @@ -55,4 +57,4 @@ async def run_job(self): except Exception as e: self.exception = e self.exception_event.set() - logging.warning(f"Task {tasks[1].get_name()} raised an exception: {e}") + _logger.warning(f"Task {tasks[1].get_name()} raised an exception: {e}") From b075580e8e864f0913b132b760d49a2a73af7a04 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Wed, 23 Jul 2025 13:02:48 +0200 Subject: [PATCH 05/61] Revert "hack to allow the states to be async function" This reverts commit 86883df9c9964b9f0b5ad592f2d0a1966f4b3f4c. --- src/mtppy/service.py | 5 ++--- src/mtppy/thread_control.py | 39 +++---------------------------------- 2 files changed, 5 insertions(+), 39 deletions(-) diff --git a/src/mtppy/service.py b/src/mtppy/service.py index 80217be..f73d090 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -17,16 +17,15 @@ class Service(SUCServiceControl): - def __init__(self, tag_name: str, tag_description: str, scheduler: callable = None): + def __init__(self, tag_name: str, tag_description: str): """ Represents a service of the PEA. :param tag_name: Tag name of the service. :param tag_description: Tag description of the service. - :param scheduler: Scheduler to run the jobs. """ super().__init__(tag_name, tag_description) - self.thread_ctrl = ThreadControl(scheduler) + self.thread_ctrl = ThreadControl() self.op_src_mode = OperationSourceMode() self.configuration_parameters = {} diff --git a/src/mtppy/thread_control.py b/src/mtppy/thread_control.py index 0bf713c..98f5ca9 100644 --- a/src/mtppy/thread_control.py +++ b/src/mtppy/thread_control.py @@ -1,23 +1,14 @@ import logging from threading import Thread -from datetime import datetime -import asyncio - -from apscheduler.schedulers.asyncio import AsyncIOScheduler _logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") class ThreadControl: - def __init__(self, scheduler: callable = None): + def __init__(self): """ Represents a thread control to be able to run with multithreading. - :param scheduler: Scheduler to run the jobs. """ - self.scheduler: AsyncIOScheduler = scheduler - self.job_event = asyncio.Event() - self.exception: Exception = None - self.exception_event = asyncio.Event() self.thread = None self.running_state = '' self.requested_state = '' @@ -31,30 +22,6 @@ def request_state(self, state: str, cb_function: callable): def reallocate_running_thread(self): _logger.debug(f'Reallocate thread to state {self.requested_state}') if self.requested_state is not self.running_state: - self.job_event.set() - - self.scheduler.add_job( - self.run_job, 'date', run_date=datetime.now(), name=self.requested_state, - misfire_grace_time=None) + self.thread = Thread(target=self.callback_function) + self.thread.start() self.running_state = self.requested_state - - async def run_job(self): - self.job_event.clear() - tasks: list[asyncio.Task] = [ - asyncio.create_task(self.job_event.wait(), name='cancel_event'), - asyncio.create_task(self.callback_function(), name=self.requested_state) - ] - done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED) - - for task in pending: - task.cancel() - - try: - if tasks[1].exception() != None: - raise tasks[1].exception() - except asyncio.exceptions.InvalidStateError as e: - pass - except Exception as e: - self.exception = e - self.exception_event.set() - _logger.warning(f"Task {tasks[1].get_name()} raised an exception: {e}") From 51d9cf807df313f2a0b632b9ae148d5ac10c96e2 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Sat, 26 Jul 2025 16:58:52 +0200 Subject: [PATCH 06/61] added exception handling --- src/mtppy/service.py | 2 +- src/mtppy/thread_control.py | 28 ++++++++++++++++++++++------ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/mtppy/service.py b/src/mtppy/service.py index f73d090..613358e 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -25,7 +25,7 @@ def __init__(self, tag_name: str, tag_description: str): """ super().__init__(tag_name, tag_description) - self.thread_ctrl = ThreadControl() + self.thread_ctrl = ThreadControl(service_name=tag_name) self.op_src_mode = OperationSourceMode() self.configuration_parameters = {} diff --git a/src/mtppy/thread_control.py b/src/mtppy/thread_control.py index 98f5ca9..fa6832d 100644 --- a/src/mtppy/thread_control.py +++ b/src/mtppy/thread_control.py @@ -1,20 +1,24 @@ import logging -from threading import Thread +from threading import Thread, Event +from collections.abc import Callable _logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") class ThreadControl: - def __init__(self): + def __init__(self, service_name: str = ''): """ Represents a thread control to be able to run with multithreading. """ - self.thread = None + self.service_name = service_name + self.thread: Thread = None self.running_state = '' self.requested_state = '' - self.callback_function = None + self.callback_function: Callable = None + self.exception_event = Event() + self.exception: Exception = None - def request_state(self, state: str, cb_function: callable): + def request_state(self, state: str, cb_function: Callable): _logger.debug(f'State {state} requested') self.requested_state = state self.callback_function = cb_function @@ -22,6 +26,18 @@ def request_state(self, state: str, cb_function: callable): def reallocate_running_thread(self): _logger.debug(f'Reallocate thread to state {self.requested_state}') if self.requested_state is not self.running_state: - self.thread = Thread(target=self.callback_function) + self.thread = Thread(target=self.run_thread, args=(self.callback_function,), + name=f"{self.service_name}_{self.requested_state}") self.thread.start() self.running_state = self.requested_state + + def run_thread(self, target_function: Callable): + """ + Runs the given target function in a new thread. + :param target_function: The function to run in the thread. + """ + try: + target_function() + except Exception as e: + self.exception = e + self.exception_event.set() From 28ccd21436c518c9fa7247576135474834090d38 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Mon, 28 Jul 2025 11:50:32 +0200 Subject: [PATCH 07/61] added/updated function docstrings to new format --- src/mtppy/attribute.py | 34 +++++--- src/mtppy/command_codes.py | 8 +- src/mtppy/command_en_control.py | 44 ++++++---- src/mtppy/communication_object.py | 6 +- src/mtppy/mtp_generator.py | 131 +++++++++++++++++++----------- src/mtppy/opcua_server_pea.py | 107 ++++++++++++++---------- src/mtppy/procedure.py | 49 +++++++---- src/mtppy/procedure_control.py | 6 +- src/mtppy/service.py | 53 ++++++------ src/mtppy/state_codes.py | 18 ++-- src/mtppy/state_machine.py | 8 +- src/mtppy/thread_control.py | 19 ++++- 12 files changed, 310 insertions(+), 173 deletions(-) diff --git a/src/mtppy/attribute.py b/src/mtppy/attribute.py index e29f65b..89df12c 100644 --- a/src/mtppy/attribute.py +++ b/src/mtppy/attribute.py @@ -8,10 +8,12 @@ def __init__(self, name: str, data_type, init_value, sub_cb=None): """ Atttribute represents an elementary object (attribute or parameter) of a data assembly. Depending on whether subscription callback is defined or not, it might be an monitored object or a static OPC UA node. - :param name: Attribute name. - :param data_type: Attribute type. - :param init_value: Initial value of the attribute. - :param sub_cb: Subscription callback if applied. + + Args: + name (str): Attribute name. + data_type (type): Attribute type. + init_value (type): Initial value of the attribute. + sub_cb (callable, optional): Subscription callback if applied. """ self.name = name self.type = data_type @@ -24,8 +26,12 @@ def __init__(self, name: str, data_type, init_value, sub_cb=None): def set_value(self, value): """ Set value of the attribute. - :param value: Value. - :return: Returns True if value was applied. + + Args: + value (type): Value. + + Returns: + bool: Returns True if value was applied. """ self.value = self._correct_type(value) @@ -42,8 +48,15 @@ def set_value(self, value): def _correct_type(self, value): """ Converts a value to the attribute type. - :param value: Value. - :return: Converted value. If conversion is not possible, returns a default value of that type. + + Args: + value (type): Value. + + Returns: + type: Converted value. If conversion is not possible, returns a default value of that type. + + Raises: + Exception: If conversion fails. """ try: converted_value = self.type(value) @@ -54,7 +67,8 @@ def _correct_type(self, value): def attach_communication_object(self, communication_object): """ Attach a communication object to the attribute, e.g. if an OPC UA node needs to be created for the attribute. - :param communication_object: Communication object. - :return: + + Args: + communication_object (type): Communication object. """ self.comm_obj = communication_object diff --git a/src/mtppy/command_codes.py b/src/mtppy/command_codes.py index 3f3964d..ac0c049 100644 --- a/src/mtppy/command_codes.py +++ b/src/mtppy/command_codes.py @@ -29,13 +29,17 @@ def __init__(self): def get_list_int(self): """ Provides a list of all command codes in int format. - :return: + + Returns: + list: List of command codes in int format. """ return list(self.int_code.keys()) def get_list_str(self): """ Provides a list of all command codes in string format. - :return: + + Returns: + list: List of command codes in string format. """ return list(self.int_code.values()) diff --git a/src/mtppy/command_en_control.py b/src/mtppy/command_en_control.py index 23a45cc..fa553d6 100644 --- a/src/mtppy/command_en_control.py +++ b/src/mtppy/command_en_control.py @@ -28,7 +28,6 @@ def __init__(self): def set_default(self): """ Applies default values to all command enable flags. - :return: """ for command_en in self.command_en: self.command_en[command_en]['value'] = self.command_en[command_en]['default'] @@ -36,7 +35,6 @@ def set_default(self): def disable_all(self): """ Sets False to all command enable flags. - :return: """ for command_en in self.command_en: self.command_en[command_en]['value'] = False @@ -44,8 +42,12 @@ def disable_all(self): def is_enabled(self, cmd: str): """ Checks if a given command is enabled right now. - :param cmd: Command to check. - :return: Command enable flag state. + + Args: + cmd (str): Command to check. + + Returns: + bool: Command enable flag state. """ if cmd in self.command_en.keys(): return self.command_en[cmd]['value'] @@ -55,7 +57,9 @@ def is_enabled(self, cmd: str): def get_command_en(self): """ Calculates a sum of all command enable flags. - :return: Sum of all command enable flags. + + Returns: + int: Sum of all command enable flags. """ command_en_sum = 0 for command in self.command_en.values(): @@ -66,9 +70,10 @@ def get_command_en(self): def set_command_en(self, cmd: str, value: bool): """ Sets a specific command enable to a given value. - :param cmd: Command enable flag. - :param value: Value to be set. - :return: + + Args: + cmd (str): Command enable flag. + value (bool): Value to be set. """ if cmd in self.command_en.keys(): self.command_en[cmd]['value'] = value @@ -76,37 +81,44 @@ def set_command_en(self, cmd: str, value: bool): def enable_hold_loop(self, value: bool): """ Enables hold loop. - :param value: True if the hold loop is to active and False if to disable. - :return: + + Args: + value (bool): True if the hold loop is to active and False if to disable. """ self.hold_enabled = value def enable_pause_loop(self, value: bool): """ Enables pause loop. - :param value: True if the pause loop is to active and False if to disable. - :return: + + Args: + value (bool): True if the pause loop is to active and False if to disable. """ self.pause_enabled = value def enable_restart(self, value: bool): """ Enables restart loop. - :param value: True if the restart command is to active and False if to disable. - :return: + + Args: + value (bool): True if the restart command is to active and False if to disable. """ self.restart_enabled = value def execute(self, state: str): """ Executes command enable changes required for a certain state. - :param state: Given state. - :return: + + Args: + state (str): Given state. """ _logger.debug(f'CommandEn changed to correspond {state}') exec(f'self._execute_{state}()') def _execute_undefined(self): + """ + Disables all command enable flags. + """ self.disable_all() def _execute_idle(self): diff --git a/src/mtppy/communication_object.py b/src/mtppy/communication_object.py index 7010678..b7cfa6f 100644 --- a/src/mtppy/communication_object.py +++ b/src/mtppy/communication_object.py @@ -2,8 +2,10 @@ class OPCUACommunicationObject: def __init__(self, opcua_node_obj, node_id): """ Represents a communication object for OPC UA for an attribute instance. - :param opcua_node_obj: OPC UA node object. - :param node_id: OPCUA node id. + + Args: + opcua_node_obj (object): OPC UA node object. + node_id (str): OPCUA node id. """ self.opcua_node_obj = opcua_node_obj self.node_id = node_id diff --git a/src/mtppy/mtp_generator.py b/src/mtppy/mtp_generator.py index 5bf659d..cb5603c 100644 --- a/src/mtppy/mtp_generator.py +++ b/src/mtppy/mtp_generator.py @@ -8,9 +8,11 @@ class MTPGenerator: def __init__(self, writer_infos: dict, export_path: str, manifest_template_path: str): """ Represents an MTP generator. - :param writer_infos: Writer info section that will be put in the header of the generate manifest. - :param export_path: Path where to save the generated MTP manifest. - :param manifest_template_path: Path where the manifest template is placed. + + Args: + writer_infos (dict): Writer info section that will be put in the header of the generated manifest. + export_path (str): Path where to save the generated MTP manifest. + manifest_template_path (str): Path where the manifest template is placed. """ # path of a manifest template containing general infos @@ -73,10 +75,12 @@ def edit_writer_information(self): def add_module_type_package(self, version: str, name: str, description: str): """ - add internal element module type package to instance hierarchy mtp - :param version: version of mtp - :param name: name of mtp - :param description: description of mtp + Add internal element module type package to instance hierarchy MTP. + + Args: + version (str): Version of MTP. + name (str): Name of MTP. + description (str): Description of MTP. """ # create internal element @@ -106,10 +110,14 @@ def __add_communication_set(self): def create_instance(self, data_assembly: SUCDataAssembly, opc_node_id: str) -> ET.Element: """ - create instance for each data assembly - :param data_assembly: data assembly object - :param opc_node_id: opcua node id used to construct instance name - :return: manifest instance of data assembly + Create instance for each data assembly. + + Args: + data_assembly (SUCDataAssembly): Data assembly object. + opc_node_id (str): OPC UA node ID used to construct instance name. + + Returns: + ET.Element: Manifest instance of data assembly. """ instance_id = self.random_id_generator() instance_name = opc_node_id.split('=')[-1] @@ -123,13 +131,16 @@ def create_instance(self, data_assembly: SUCDataAssembly, opc_node_id: str) -> E def add_data_assembly_to_instance_list(self, instance_name: str, instance_id: str, instance_basic_type: str, instance_type_name: str) -> ET.Element: """ - add data object (subclasses of data assembly) to instance list - :param instance_name: name of data object - :param instance_id: id of data object - :param instance_basic_type: type of data assembly (service, Procedure, operation element etc.) - :param instance_type_name: used to distinguish data objects that have the same parent type (e.g. AnaSerParam, - StringSerParam, DIntSerParam, etc.) - :return: instance of data assembly + Add data object (subclasses of data assembly) to instance list. + + Args: + instance_name (str): Name of data object. + instance_id (str): ID of data object. + instance_basic_type (str): Type of data assembly (service, Procedure, operation element etc.). + instance_type_name (str): Used to distinguish data objects that have the same parent type. + + Returns: + ET.Element: Instance of data assembly. """ if instance_basic_type == 'Service': name = instance_name + '.ServiceControl' @@ -158,10 +169,14 @@ def add_data_assembly_to_instance_list(self, instance_name: str, instance_id: st def create_components_for_services(self, data_assembly: SUCDataAssembly, section: str) -> str: """ - create components for InstanceHierarchy Services - :param parent_elem: parent element of current component (e.g. parent of a procedure is a service) - :param data_assembly: data assembly object - :return: component of services: eg. service, procedure, config parameter, procedure parameter etc. + Create components for InstanceHierarchy Services. + + Args: + parent_elem (ET.Element): Parent element of current component (e.g. parent of a procedure is a service). + data_assembly (SUCDataAssembly): Data assembly object. + + Returns: + ET.Element: Component of services (e.g., service, procedure, config parameter, procedure parameter, etc.). """ services_component_id = self.random_id_generator() link_id = self.add_components_to_services(data_assembly, services_component_id, section) @@ -242,9 +257,11 @@ def add_components_to_services(self, data_assembly: SUCDataAssembly, sc_id: str, @staticmethod def add_linked_attr(parent: ET.Element, link_id: str): """ - add attribute whose value refers to an InternalElement under InstanceList - :param parent: parent of attribute - :param link_id: id linking to another element + Add attribute whose value refers to an InternalElement under InstanceList. + + Args: + parent (ET.Element): Parent of attribute. + link_id (str): ID linking to another element. """ attr = ET.SubElement(parent, 'Attribute') attr.set('Name', 'RefID') @@ -254,8 +271,10 @@ def add_linked_attr(parent: ET.Element, link_id: str): def __add_services_set(self, linked_id: str): """ - ServiceSet is an InternalElement which belongs to ModuleTypePackage - :param linked_id: Url or ID referring to instance hierarchy services + ServiceSet is an InternalElement which belongs to ModuleTypePackage. + + Args: + linked_id (str): Url or ID referring to instance hierarchy services. """ self.services_set = ET.SubElement(self.module_type_package, 'InternalElement') self.generate_attributes(self.services_set, 'Services', self.random_id_generator(), @@ -266,8 +285,12 @@ def __add_services_set(self, linked_id: str): def __add_service_to_service_set(self, name: str, refPath: str, linked_id: str): """ - service object in this function is an ExternalDataConnector referring to InstanceHierarchy: Services which - could be created in the same manifest or in a separate .aml file. + Service object in this function is an ExternalDataConnector referring to InstanceHierarchy: Services. + + Args: + name (str): Name of the service. + refPath (str): Reference path for the service. + linked_id (str): ID linking to InstanceHierarchy: Services. """ service = ET.SubElement(self.services_set, 'ExternalInterface') self.generate_attributes(service, name, self.random_id_generator(), refPath) @@ -276,11 +299,13 @@ def __add_service_to_service_set(self, name: str, refPath: str, linked_id: str): @staticmethod def add_attr_to_instance(parent_elem: ET.Element, name: str, default_value: str, attr_id: str): """ - add attrbutes of each data assembly to the corresponding instance - :param parent_elem: data assembly to which the attributes should be added - :param name: attribute name - :param default_value: default value of attribute - :param attr_id: each attribute has the same ID as the external interface of source list + Add attributes of each data assembly to the corresponding instance. + + Args: + parent_elem (ET.Element): Data assembly to which the attributes should be added. + name (str): Attribute name. + default_value (str): Default value of the attribute. + attr_id (str): ID of the attribute, same as the external interface of source list. """ attr = ET.SubElement(parent_elem, 'Attribute') attr.set('Name', name) @@ -299,8 +324,10 @@ def add_attr_to_instance(parent_elem: ET.Element, name: str, default_value: str, def add_opcua_server(self, endpoint: str): """ - add InternalElement opcua server to ModuleTypePackage/CommunicationSet/SourceList - :param endpoint: endpoint of opc ua server + Add InternalElement OPC UA server to ModuleTypePackage/CommunicationSet/SourceList. + + Args: + endpoint (str): Endpoint of OPC UA server. """ self.opcua_server = ET.SubElement(self.source_list, 'InternalElement') self.generate_attributes(self.opcua_server, 'OPCServer', self.random_id_generator(), @@ -313,11 +340,13 @@ def add_opcua_server(self, endpoint: str): def add_external_interface(self, opc_node_id: str, opc_ns: int, linked_attr_id: str, access=1): """ - add opc ua node als ExternalInterface to ModuleTypePackage/CommunicationSet/SourceList/OPCUAServer - :param opc_node_id: opc ua node id - :param opc_ns: opc ua namespace - :param linked_attr_id: id connecting external interface and the corresponding attribute of data assembly - :param access: access level of each external element + Add OPC UA node as ExternalInterface to ModuleTypePackage/CommunicationSet/SourceList/OPCUAServer. + + Args: + opc_node_id (str): OPC UA node ID. + opc_ns (int): OPC UA namespace. + linked_attr_id (str): ID connecting external interface and the corresponding attribute of data assembly. + access (int, optional): Access level of each external element. Defaults to 1. """ node_identifier = opc_node_id node_name = opc_node_id.split('=')[-1] @@ -362,8 +391,10 @@ def apply_add_supported_role_class(self): def add_supported_role_class(self, parent: ET.Element): """ - add SupportedRoleClass to all InternalElement - :param parent: internal element + Add SupportedRoleClass to all InternalElement. + + Args: + parent (ET.Element): Internal element. """ for internal_element in parent.findall('InternalElement'): SupportedRoleClass = ET.SubElement(internal_element, 'SupportedRoleClass') @@ -376,8 +407,10 @@ def add_supported_role_class(self, parent: ET.Element): @staticmethod def random_id_generator() -> str: """ - # generate random id for different elements - :return: id which contains five parts + Generate random ID for different elements. + + Returns: + str: ID which contains five parts. """ # generate random id for different elements id1 = ''.join(random.sample(string.ascii_letters + string.digits, 4)) @@ -400,9 +433,11 @@ def export_manifest(self): def pretty_print(self, elem: ET.Element, level=0): """ - add indents to exported .aml file - :param elem: root of the element tree - :param level: layer of the element tree, 0 represents the first layer (root) + Add indents to exported .aml file. + + Args: + elem (ET.Element): Root of the element tree. + level (int): Layer of the element tree, 0 represents the first layer (root). """ i = "\n" + level * "\t" if len(elem): diff --git a/src/mtppy/opcua_server_pea.py b/src/mtppy/opcua_server_pea.py index 5e88b3b..f1d410b 100644 --- a/src/mtppy/opcua_server_pea.py +++ b/src/mtppy/opcua_server_pea.py @@ -12,9 +12,10 @@ class OPCUAServerPEA: def __init__(self, mtp_generator: MTPGenerator = None, endpoint: str = 'opc.tcp://127.0.0.1:4840/'): """ Defines an OPC UA server for PEA. - :param mtp_generator: Instance of an MTP generator. If specified, an MTP file is generated each time - the class is instantiated. If not specified, no MTP file is going to be generated. - :param endpoint: Endpoint of the OPC UA server. If not specified, opc.tcp://0.0.0.0:4840/ is used. + + Args: + mtp_generator (MTPGenerator): Instance of an MTP generator. + endpoint (str): Endpoint of the OPC UA server. """ self.service_set = {} self.active_elements = {} @@ -28,23 +29,24 @@ def __init__(self, mtp_generator: MTPGenerator = None, endpoint: str = 'opc.tcp: def add_service(self, service: Service): """ Add a service to the PEA. - :param service: Service instance. - :return: + + Args: + service (Service): Service instance. """ self.service_set[service.tag_name] = service def add_active_element(self, active_element: SUCActiveElement): """ Add an active element to the PEA. - :param active_element: Active element (e.g. AnaVlv, BinVlv, etc.) - :return: + + Args: + active_element (SUCActiveElement): Active element (e.g., AnaVlv, BinVlv, etc.). """ self.active_elements[active_element.tag_name] = active_element def _init_opcua_server(self): """ - Initialises an OPC UA server and sets the endpoint. - :return: + Initializes an OPC UA server and sets the endpoint. """ _logger.info(f'Initialisation of OPC UA server: {self.endpoint}') self.opcua_server = Server() @@ -54,34 +56,39 @@ def _init_opcua_server(self): def get_opcua_server(self): """ Get an OPC UA server instance object. - :return: + + Returns: + Server: OPC UA server instance. """ return self.opcua_server def get_opcua_ns(self): """ Get an OPC UA server namespace index. - :return: + + Returns: + int: Namespace index. """ return self.opcua_ns def run_opcua_server(self): """ Starts the OPC UA server instance. - :return: """ self.opcua_server.start() self._build_opcua_server() self._start_subscription() def set_services_in_idle(self): + """ + Sets all services to idle state. + """ for service in self.service_set.values(): service.init_idle_state() def _build_opcua_server(self): """ Creates an OPC UA server instance including required nodes according to defined data assemblies. - :return: """ _logger.info(f'Adding OPC UA nodes to the server structure according to the PEA structure:') ns = self.opcua_ns @@ -120,10 +127,11 @@ def _build_opcua_server(self): def _create_opcua_objects_for_folders(self, data_assembly: SUCDataAssembly, parent_opcua_prefix: str, parent_opcua_object): """ Iterates over data assemblies to create OPC UA folders. - :param data_assembly: Data assembly. - :param parent_opcua_prefix: Prefix as a string to add in front of the data assembly tag. - :param parent_opcua_object: Parent OPC UA node where the data assembly is to add to. - :return: + + Args: + data_assembly (SUCDataAssembly): Data assembly. + parent_opcua_prefix (str): Prefix to add in front of the data assembly tag. + parent_opcua_object: Parent OPC UA node where the data assembly is added. """ da_node_id = f'{parent_opcua_prefix}.{data_assembly.tag_name}' da_node = parent_opcua_object.add_folder(da_node_id, data_assembly.tag_name) @@ -172,11 +180,12 @@ def _create_opcua_objects_for_folders(self, data_assembly: SUCDataAssembly, pare def _create_opcua_objects_for_leaves(self, opcua_object, parent_opcua_prefix: str, parent_opcua_object, par_instance): """ Iterates over end objects (leaves) of data assemblies to create corresponding OPC UA nodes. - :param opcua_object: Element of a data assembly that an OPC UA node is to create for. - :param parent_opcua_prefix: Prefix as a string to add in front of the data assembly tag. - :param parent_opcua_object: Parent OPC UA node where the data assembly is to add to. - :param par_instance: Parameter instance. - :return: + + Args: + opcua_object: Element of a data assembly that an OPC UA node is created for. + parent_opcua_prefix (str): Prefix to add in front of the data assembly tag. + parent_opcua_object: Parent OPC UA node where the data assembly is added. + par_instance: Parameter instance. """ for attr in opcua_object.attributes.values(): attribute_node_id = f'{parent_opcua_prefix}.{attr.name}' @@ -221,9 +230,13 @@ def _create_opcua_objects_for_leaves(self, opcua_object, parent_opcua_prefix: st @staticmethod def _infer_data_type(attribute_data_type): """ - Translate a python data type to a suitable OPC UA data type. - :param attribute_data_type: Python variable. - :return: OPC UA data type + Translate a Python data type to a suitable OPC UA data type. + + Args: + attribute_data_type (type): Python variable type. + + Returns: + ua.VariantType: OPC UA data type. """ if attribute_data_type == int: return ua.VariantType.Int64 @@ -239,7 +252,6 @@ def _infer_data_type(attribute_data_type): def _start_subscription(self): """ Subscribes to defined OPC UA nodes. - :return: """ handler = Marshalling() handler.import_subscription_list(self.subscription_list) @@ -256,10 +268,11 @@ def __init__(self): def append(self, node_id, cb_value_change): """ - Add an subscription entity. - :param node_id: OPC UA node. - :param cb_value_change: Callback function for a value change. - :return: + Add a subscription entity. + + Args: + node_id: OPC UA node. + cb_value_change (function): Callback function for a value change. """ identifier = node_id.nodeid.Identifier self.sub_list[identifier] = {'node_id': node_id, 'callback': cb_value_change} @@ -267,7 +280,9 @@ def append(self, node_id, cb_value_change): def get_nodeid_list(self): """ Extract a list of node ids in the subscription list. - :return: List of node ids. + + Returns: + list: List of node ids. """ if len(self.sub_list) == 0: return None @@ -280,8 +295,12 @@ def get_nodeid_list(self): def get_callback(self, node_id): """ Get a callback function for a specific OPC UA node. - :param node_id: OPC UA node id - :return: + + Args: + node_id: OPC UA node id. + + Returns: + function: Callback function. """ identifier = node_id.nodeid.Identifier if identifier in self.sub_list.keys(): @@ -300,18 +319,20 @@ def __init__(self): def import_subscription_list(self, subscription_list: SubscriptionList): """ Import a subscription list. - :param subscription_list: Subscription list. - :return: + + Args: + subscription_list (SubscriptionList): Subscription list. """ self.subscription_list = subscription_list def datachange_notification(self, node, val, data): """ Executes a callback function if data value changes. - :param node: OPC UA node. - :param val: Value after change. - :param data: Not used. - :return: + + Args: + node: OPC UA node. + val: Value after change. + data: Not used. """ callback = self.find_set_callback(node) if callback is not None: @@ -324,7 +345,11 @@ def datachange_notification(self, node, val, data): def find_set_callback(self, node_id): """ Finds a callback function to a specific OPC UA node by nodeid. - :param node_id: Node id. - :return: Callback function. + + Args: + node_id: Node id. + + Returns: + function: Callback function. """ return self.subscription_list.get_callback(node_id) diff --git a/src/mtppy/procedure.py b/src/mtppy/procedure.py index ebda96a..b4bc868 100644 --- a/src/mtppy/procedure.py +++ b/src/mtppy/procedure.py @@ -10,11 +10,13 @@ def __init__(self, procedure_id: int, tag_name: str, tag_description: str = '', is_default: bool = False): """ Represents a procedure of a service. - :param procedure_id: Procedure id. - :param tag_name: Tag name of the procedure. - :param tag_description: Tag description of the procedure. - :param is_self_completing: Self-completing or not. - :param is_default: Default or not. + + Args: + procedure_id (int): Procedure id. + tag_name (str): Tag name of the procedure. + tag_description (str): Tag description of the procedure. + is_self_completing (bool): Self-completing or not. + is_default (bool): Default or not. """ super().__init__(procedure_id, tag_name, tag_description, is_self_completing, is_default) self.procedure_parameters = {} @@ -25,8 +27,12 @@ def __init__(self, procedure_id: int, tag_name: str, tag_description: str = '', def add_procedure_parameter(self, procedure_parameter: SUCOperationElement): """ Adds a procedure parameter to the procedure. - :param procedure_parameter: Procedure parameter. - :return: + + Args: + procedure_parameter (SUCOperationElement): Procedure parameter. + + Raises: + TypeError: If procedure_parameter is not an instance of SUCOperationElement. """ if isinstance(procedure_parameter, SUCOperationElement): self.procedure_parameters[procedure_parameter.tag_name] = procedure_parameter @@ -35,17 +41,25 @@ def add_procedure_parameter(self, procedure_parameter: SUCOperationElement): def add_procedure_value_in(self, process_value_in): """ - Adds an value in to the procedure. NOT IMPLEMENTED. - :param process_value_in: Value in. - :return: + Adds a value in to the procedure. NOT IMPLEMENTED. + + Args: + process_value_in: Value in. + + Raises: + NotImplementedError: Always raised as the method is not implemented. """ raise NotImplementedError() def add_report_value(self, report_value: SUCIndicatorElement): """ Adds a report value to the procedure. - :param report_value: Report value. - :return: + + Args: + report_value (SUCIndicatorElement): Report value. + + Raises: + TypeError: If report_value is not an instance of SUCIndicatorElement. """ if isinstance(report_value, SUCIndicatorElement): self.report_values[report_value.tag_name] = report_value @@ -54,9 +68,13 @@ def add_report_value(self, report_value: SUCIndicatorElement): def add_procedure_value_out(self, process_value_out: SUCIndicatorElement): """ - Adds an value out to the procedure. - :param process_value_out: Value in. - :return: + Adds a value out to the procedure. + + Args: + process_value_out (SUCIndicatorElement): Value out. + + Raises: + TypeError: If process_value_out is not an instance of SUCIndicatorElement. """ if isinstance(process_value_out, SUCIndicatorElement): self.process_value_outs[process_value_out.tag_name] = process_value_out @@ -66,7 +84,6 @@ def add_procedure_value_out(self, process_value_out: SUCIndicatorElement): def apply_procedure_parameters(self): """ Applies procedure parameters. - :return: """ _logger.debug('Applying procedure parameters') for procedure_parameter in self.procedure_parameters.values(): diff --git a/src/mtppy/procedure_control.py b/src/mtppy/procedure_control.py index e2a5d12..cd87a86 100644 --- a/src/mtppy/procedure_control.py +++ b/src/mtppy/procedure_control.py @@ -10,8 +10,10 @@ class ProcedureControl: def __init__(self, procedures: dict, service_op_src_mode: OperationSourceMode): """ Represents the procedure control. - :param procedures: Procedures. - :param service_op_src_mode: Operation and source mode of the service. + + Args: + procedures (dict): Procedures. + service_op_src_mode (OperationSourceMode): Operation and source mode of the service. """ self.attributes = { 'ProcedureOp': Attribute('ProcedureOp', int, init_value=0, sub_cb=self.set_procedure_op), diff --git a/src/mtppy/service.py b/src/mtppy/service.py index 613358e..40779e1 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -20,8 +20,10 @@ class Service(SUCServiceControl): def __init__(self, tag_name: str, tag_description: str): """ Represents a service of the PEA. - :param tag_name: Tag name of the service. - :param tag_description: Tag description of the service. + + Args: + tag_name (str): Tag name of the service. + tag_description (str): Tag description of the service. """ super().__init__(tag_name, tag_description) @@ -44,9 +46,15 @@ def __init__(self, tag_name: str, tag_description: str): self.op_src_mode.add_exit_offline_callback(self.init_idle_state) def init_idle_state(self): + """ + Initializes the idle state. + """ self.state_change_callback() def state_change_callback(self): + """ + Callback for state changes. + """ if self.op_src_mode.attributes['StateOffAct'].value: return @@ -60,6 +68,15 @@ def state_change_callback(self): self.op_src_mode.allow_switch_to_offline_mode(False) def is_state(self, state_str): + """ + Checks if the current state matches the given state. + + Args: + state_str (str): State to check. + + Returns: + bool: True if the current state matches, False otherwise. + """ if state_str is self.state_machine.get_current_state_str(): return True else: @@ -67,20 +84,23 @@ def is_state(self, state_str): return False def state_change(self): + """ + Changes the state. + """ self.state_machine.state_change() def add_configuration_parameter(self, configuration_parameter: SUCOperationElement): """ Adds a configuration parameter to the service. - :param configuration_parameter: Configuration parameter. - :return: + + Args: + configuration_parameter (SUCOperationElement): Configuration parameter to add. """ self.configuration_parameters[configuration_parameter.tag_name] = configuration_parameter def apply_configuration_parameters(self): """ Applies configuration parameters. - :return: """ _logger.debug('Applying service configuration parameters') for configuration_parameter in self.configuration_parameters.values(): @@ -88,9 +108,10 @@ def apply_configuration_parameters(self): def add_procedure(self, procedure: Procedure): """ - Adds procedure to the service. - :param procedure: Procedure. - :return: + Adds a procedure to the service. + + Args: + procedure (Procedure): Procedure to add. """ self.procedures[procedure.attributes['ProcedureId'].value] = procedure if procedure.attributes['IsDefault'].value: @@ -103,7 +124,6 @@ def add_procedure(self, procedure: Procedure): def idle(self): """ Idle state. - :return: """ pass @@ -111,7 +131,6 @@ def idle(self): def starting(self): """ Starting state. - :return: """ pass @@ -119,7 +138,6 @@ def starting(self): def execute(self): """ Execute state. - :return: """ pass @@ -127,7 +145,6 @@ def execute(self): def completing(self): """ Completing state. - :return: """ pass @@ -135,7 +152,6 @@ def completing(self): def completed(self): """ Completed state. - :return: """ pass @@ -143,7 +159,6 @@ def completed(self): def pausing(self): """ Pausing state. - :return: """ pass @@ -151,7 +166,6 @@ def pausing(self): def paused(self): """ Paused state. - :return: """ pass @@ -159,7 +173,6 @@ def paused(self): def resuming(self): """ Resuming state. - :return: """ pass @@ -167,7 +180,6 @@ def resuming(self): def holding(self): """ Holding state. - :return: """ pass @@ -175,7 +187,6 @@ def holding(self): def held(self): """ Held state. - :return: """ pass @@ -183,7 +194,6 @@ def held(self): def unholding(self): """ Unholding state. - :return: """ pass @@ -191,7 +201,6 @@ def unholding(self): def stopping(self): """ Stopping state. - :return: """ pass @@ -199,7 +208,6 @@ def stopping(self): def stopped(self): """ Stopped state. - :return: """ pass @@ -207,7 +215,6 @@ def stopped(self): def aborting(self): """ Aborting state. - :return: """ pass @@ -215,7 +222,6 @@ def aborting(self): def aborted(self): """ Aborted state. - :return: """ pass @@ -223,6 +229,5 @@ def aborted(self): def resetting(self): """ Resetting state. - :return: """ pass diff --git a/src/mtppy/state_codes.py b/src/mtppy/state_codes.py index 0c51c5d..add3ff1 100644 --- a/src/mtppy/state_codes.py +++ b/src/mtppy/state_codes.py @@ -1,9 +1,9 @@ class StateCodes: def __init__(self): """ - List of state codes as int and string. + Initializes the StateCodes class with state codes as integers and strings. """ - #self.undefined = 1 + # self.undefined = 1 self.stopped = 4 self.starting = 8 self.idle = 16 @@ -22,7 +22,7 @@ def __init__(self): self.completed = 131072 self.int_code = {} - #self.int_code[1] ='undefined' + # self.int_code[1] ='undefined' self.int_code[4] = 'stopped' self.int_code[8] = 'starting' self.int_code[16] = 'idle' @@ -42,14 +42,18 @@ def __init__(self): def get_list_int(self): """ - Gets a list of state as int. - :return: List of states. + Gets a list of state codes as integers. + + Returns: + list: List of state codes as integers. """ return list(self.int_code.keys()) def get_list_str(self): """ - Gets a list of state as string.. - :return: List of states. + Gets a list of state codes as strings. + + Returns: + list: List of state codes as strings. """ return list(self.int_code.values()) diff --git a/src/mtppy/state_machine.py b/src/mtppy/state_machine.py index 39184b6..900086c 100644 --- a/src/mtppy/state_machine.py +++ b/src/mtppy/state_machine.py @@ -18,9 +18,11 @@ def __init__(self, operation_source_mode: OperationSourceMode, execution_routine: callable): """ Represents a state machine for a service. - :param operation_source_mode: Operation and source mode control. - :param procedure_control: Procedure control. - :param execution_routine: Execution routine for state changing. + + Args: + operation_source_mode (OperationSourceMode): Operation and source mode control. + procedure_control (ProcedureControl): Procedure control. + execution_routine (callable): Execution routine for state changing. """ self.attributes = { diff --git a/src/mtppy/thread_control.py b/src/mtppy/thread_control.py index fa6832d..5b059df 100644 --- a/src/mtppy/thread_control.py +++ b/src/mtppy/thread_control.py @@ -9,6 +9,9 @@ class ThreadControl: def __init__(self, service_name: str = ''): """ Represents a thread control to be able to run with multithreading. + + Args: + service_name (str): Name of the service. """ self.service_name = service_name self.thread: Thread = None @@ -19,11 +22,21 @@ def __init__(self, service_name: str = ''): self.exception: Exception = None def request_state(self, state: str, cb_function: Callable): + """ + Requests a state change and sets the callback function. + + Args: + state (str): The requested state. + cb_function (Callable): The callback function to execute. + """ _logger.debug(f'State {state} requested') self.requested_state = state self.callback_function = cb_function def reallocate_running_thread(self): + """ + Reallocates the running thread to the requested state. + """ _logger.debug(f'Reallocate thread to state {self.requested_state}') if self.requested_state is not self.running_state: self.thread = Thread(target=self.run_thread, args=(self.callback_function,), @@ -33,8 +46,10 @@ def reallocate_running_thread(self): def run_thread(self, target_function: Callable): """ - Runs the given target function in a new thread. - :param target_function: The function to run in the thread. + Runs the given target function. Sets the exception if it occurs. + + Args: + target_function (Callable): The function to run in the thread. """ try: target_function() From ec65641c85338916ab3e85f9fbc2d40c089458e1 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Tue, 29 Jul 2025 15:17:43 +0200 Subject: [PATCH 08/61] added thread stop implementation --- src/mtppy/thread_control.py | 40 +++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/src/mtppy/thread_control.py b/src/mtppy/thread_control.py index 5b059df..c024aee 100644 --- a/src/mtppy/thread_control.py +++ b/src/mtppy/thread_control.py @@ -1,20 +1,35 @@ import logging -from threading import Thread, Event +from threading import Thread, Event, current_thread from collections.abc import Callable _logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") +class StoppableThread(Thread): + """ + A thread that can be told stop by setting an event. + """ + + def __init__(self, target=None, name=None, args=(), kwargs=None): + super().__init__(target=target, name=name, args=args, kwargs=kwargs) + self.stop_event = Event() + + def stop(self): + self.stop_event.set() + + class ThreadControl: - def __init__(self, service_name: str = ''): + def __init__(self, service_name: str = '', state_change_function: Callable = None): """ Represents a thread control to be able to run with multithreading. Args: service_name (str): Name of the service. + state_change_function (Callable): Function to call after a state completes. """ self.service_name = service_name - self.thread: Thread = None + self.state_change_function = state_change_function + self.thread: StoppableThread = None self.running_state = '' self.requested_state = '' self.callback_function: Callable = None @@ -39,8 +54,13 @@ def reallocate_running_thread(self): """ _logger.debug(f'Reallocate thread to state {self.requested_state}') if self.requested_state is not self.running_state: - self.thread = Thread(target=self.run_thread, args=(self.callback_function,), - name=f"{self.service_name}_{self.requested_state}") + # stop the current thread if it is running + if self.thread and self.thread.is_alive() and self.thread is not current_thread(): + _logger.debug(f'Stopping thread {self.thread.name}') + self.thread.stop() + + self.thread = StoppableThread(target=self.run_thread, args=(self.callback_function,), + name=f"{self.service_name}_{self.requested_state}") self.thread.start() self.running_state = self.requested_state @@ -52,7 +72,15 @@ def run_thread(self, target_function: Callable): target_function (Callable): The function to run in the thread. """ try: - target_function() + try: + target_function() + # changes state for transitional states + # if self.state_change_function: + # self.state_change_function() + # Catch InterruptedError thrown by stop events. Should not cause an error. + except InterruptedError: + _logger.debug("Stop event was set, stopping thread execution.") + except Exception as e: self.exception = e self.exception_event.set() From a599378ecae22d70661dd02c733bb5783baf1272 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Tue, 29 Jul 2025 15:18:08 +0200 Subject: [PATCH 09/61] implemented default state functions --- src/mtppy/service.py | 168 ++++++++++++++++++++++++++++--------- src/mtppy/state_machine.py | 8 +- 2 files changed, 136 insertions(+), 40 deletions(-) diff --git a/src/mtppy/service.py b/src/mtppy/service.py index 40779e1..5ac4e0b 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -1,4 +1,6 @@ import logging +import time +from threading import Event from abc import abstractmethod @@ -17,6 +19,30 @@ class Service(SUCServiceControl): + """Represents a service of the PEA. + + To create a new service, inherit from this class and implement at least the abstract methods. + These include: + - starting + - execute + - completing + + Other methods can be overridden as needed. + By default the higher level methods (on the right side) will call the lower level methods. + See the following diagram for how the methods are called: + + +------------+ +------------+ +------------+ +------------+ +------------+ + | completing | <- | pausing | <- | holding | <- | stopping | <- | aborting | + +------------+ +------------+ +------------+ +------------+ +------------+ + +------------+ +------------+ +------------+ +------------+ + || | paused | <- | held | <- | stopped | <- | aborted | + +------------+ +------------+ +------------+ +------------+ + +------------+ +------------+ +------------+ + | starting | <- | resuming | <- | unholding | + +------------+ +------------+ +------------+ + + """ + def __init__(self, tag_name: str, tag_description: str): """ Represents a service of the PEA. @@ -27,7 +53,6 @@ def __init__(self, tag_name: str, tag_description: str): """ super().__init__(tag_name, tag_description) - self.thread_ctrl = ThreadControl(service_name=tag_name) self.op_src_mode = OperationSourceMode() self.configuration_parameters = {} @@ -39,6 +64,9 @@ def __init__(self, tag_name: str, tag_description: str): procedure_control=self.procedure_control, execution_routine=self.state_change_callback) + self.thread_ctrl = ThreadControl(service_name=tag_name, + state_change_function=self.state_change()) + self.op_src_mode.add_enter_offline_callback(self.state_machine.command_en_ctrl.disable_all) self.op_src_mode.add_exit_offline_callback(self.state_machine.command_en_ctrl.set_default) @@ -80,12 +108,25 @@ def is_state(self, state_str): if state_str is self.state_machine.get_current_state_str(): return True else: + # start the next thread if the state is not the current one self.thread_ctrl.reallocate_running_thread() return False + def get_state_stop_event(self) -> Event: + """ + Returns an event that is set when the state should stop. + + Returns: + Event: Event that is set when the state should stop. + """ + if self.thread_ctrl.thread is None: + raise RuntimeError("Thread is not running.") + + return self.thread_ctrl.thread.stop_event + def state_change(self): """ - Changes the state. + Changes the state. Has to be called by each transitional state method. """ self.state_machine.state_change() @@ -120,19 +161,23 @@ def add_procedure(self, procedure: Procedure): self.procedure_control.attributes['ProcedureInt'].init_value = self.procedure_control.default_procedure_id self.procedure_control.attributes['ProcedureExt'].init_value = self.procedure_control.default_procedure_id - @abstractmethod def idle(self): """ Idle state. """ - pass + _logger.debug(f"{self.tag_name} - Idle -") + cycle = 0 + while self.is_state("idle"): + _logger.debug(f"{self.tag_name} - Idle cycle {cycle}") + cycle += 1 + time.sleep(3) @abstractmethod def starting(self): """ Starting state. """ - pass + self.state_change() @abstractmethod def execute(self): @@ -146,88 +191,133 @@ def completing(self): """ Completing state. """ - pass + self.state_change() - @abstractmethod def completed(self): """ Completed state. """ - pass + if self.state_machine.get_current_state_str() == "completed": + _logger.debug(f"{self.tag_name} - Completed -") + else: + pass - @abstractmethod def pausing(self): """ - Pausing state. + Pausing state. If not overridden, it will call the completing method. """ - pass + if self.state_machine.get_current_state_str() == "pausing": + _logger.debug(f"{self.tag_name} - Pausing -") + else: + pass + # call the completing method to also execute the logic for the completing state + self.completing() - @abstractmethod def paused(self): """ Paused state. """ - pass + if self.state_machine.get_current_state_str() == "paused": + _logger.debug(f"{self.tag_name} - Paused -") + else: + pass - @abstractmethod def resuming(self): """ - Resuming state. + Resuming state. If not overridden, it will call the starting method. """ - pass + if self.state_machine.get_current_state_str() == "resuming": + _logger.debug(f"{self.tag_name} - Resuming -") + else: + pass + # call the starting method to also execute the logic for the starting state + self.starting() - @abstractmethod def holding(self): """ - Holding state. + Holding state. If not overridden, it will call the pausing method. """ - pass + if self.state_machine.get_current_state_str() == "holding": + _logger.debug(f"{self.tag_name} - Holding -") + else: + pass + # call the pausing method to also execute the logic for the pausing state + self.pausing() - @abstractmethod def held(self): """ - Held state. + Held state. If not overridden, it will call the paused method. """ - pass + if self.state_machine.get_current_state_str() == "held": + _logger.debug(f"{self.tag_name} - Held -") + else: + pass + # call the paused method to also execute the logic for the paused state + self.paused() - @abstractmethod def unholding(self): """ - Unholding state. + Unholding state. If not overridden, it will call the resuming method. """ - pass + if self.state_machine.get_current_state_str() == "unholding": + _logger.debug(f"{self.tag_name} - Unholding -") + else: + pass + # call the resuming method to also execute the logic for the resuming state + self.resuming() - @abstractmethod def stopping(self): """ - Stopping state. + Stopping state. If not overridden, it will call the holding method. """ - pass + if self.state_machine.get_current_state_str() == "stopping": + _logger.debug(f"{self.tag_name} - Stopping -") + else: + pass + # call the holding method to also execute the logic for the holding state + self.holding() - @abstractmethod def stopped(self): """ - Stopped state. + Stopped state. If not overridden, it will call the held method. """ - pass + if self.state_machine.get_current_state_str() == "stopped": + _logger.debug(f"{self.tag_name} - Stopped -") + else: + pass + # call the held method to also execute the logic for the held state + self.held() - @abstractmethod def aborting(self): """ - Aborting state. + Aborting state. If not overridden, it will call the stopping method. """ - pass + if self.state_machine.get_current_state_str() == "aborting": + _logger.debug(f"{self.tag_name} - Aborting -") + else: + pass + # call the stopping method to also execute the logic for the stopping state + self.stopping() - @abstractmethod def aborted(self): """ - Aborted state. + Aborted state. If not overridden, it will call the stopped method. """ - pass + if self.state_machine.get_current_state_str() == "aborted": + _logger.debug(f"{self.tag_name} - Aborted -") + else: + pass + # call the stopped method to also execute the logic for the stopped state + self.stopped() - @abstractmethod def resetting(self): """ Resetting state. """ - pass + if self.state_machine.get_current_state_str() == "resetting": + _logger.debug(f"{self.tag_name} - Resetting -") + else: + pass + # Reset the state machine to idle + self.thread_ctrl.exception = None + self.state_change() diff --git a/src/mtppy/state_machine.py b/src/mtppy/state_machine.py index 900086c..c69bba2 100644 --- a/src/mtppy/state_machine.py +++ b/src/mtppy/state_machine.py @@ -144,5 +144,11 @@ def _change_state_to(self, new_state: int): self.execution_routine() _logger.debug(f'Service state changed to {new_state}') - def get_current_state_str(self): + def get_current_state_str(self) -> str: + """ + Get the current state as a string. + + Returns: + str: Current state as a string. + """ return StateCodes.int_code[self.act_state] From 90f21765d86bbbd56a21eefadc2f8c5a713f8bc6 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Thu, 31 Jul 2025 18:17:19 +0200 Subject: [PATCH 10/61] reset command variable to 0 after execution --- src/mtppy/state_machine.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/mtppy/state_machine.py b/src/mtppy/state_machine.py index c69bba2..07c2aea 100644 --- a/src/mtppy/state_machine.py +++ b/src/mtppy/state_machine.py @@ -67,8 +67,17 @@ def command_execution(self, com_var: int): else: _logger.debug(f'CommandEn permits to execute {cmd_str}') + # execute the command, if it is enabled will change the state thread to the requested state eval(f'self.{CommandCodes.int_code[com_var]}()') + # reset the command operation code + if self.op_src_mode.attributes['StateOpAct'].value: + self.attributes['CommandOp'].set_value(0) + elif self.op_src_mode.attributes['StateAutAct'].value and self.op_src_mode.attributes['SrcIntAct'].value: + self.attributes['CommandInt'].set_value(0) + elif self.op_src_mode.attributes['StateAutAct'].value and self.op_src_mode.attributes['SrcExtAct'].value: + self.attributes['CommandExt'].set_value(0) + def start(self): if self.command_en_ctrl.is_enabled('start'): self.procedure_control.set_procedure_cur() From 2d5e8fa70210df7ce630cec4f34446d08d6769ed Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Thu, 31 Jul 2025 19:50:25 +0200 Subject: [PATCH 11/61] fix bug where the CommandEn is not updated when exciting offline --- src/mtppy/service.py | 1 + src/mtppy/state_machine.py | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/mtppy/service.py b/src/mtppy/service.py index 5ac4e0b..39a27de 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -70,6 +70,7 @@ def __init__(self, tag_name: str, tag_description: str): self.op_src_mode.add_enter_offline_callback(self.state_machine.command_en_ctrl.disable_all) self.op_src_mode.add_exit_offline_callback(self.state_machine.command_en_ctrl.set_default) + self.op_src_mode.add_exit_offline_callback(self.state_machine.update_command_en) self.op_src_mode.add_exit_offline_callback(self.apply_configuration_parameters) self.op_src_mode.add_exit_offline_callback(self.init_idle_state) diff --git a/src/mtppy/state_machine.py b/src/mtppy/state_machine.py index 07c2aea..c48fa62 100644 --- a/src/mtppy/state_machine.py +++ b/src/mtppy/state_machine.py @@ -149,10 +149,16 @@ def _change_state_to(self, new_state: int): self.attributes['StateCur'].set_value(new_state) new_state_str = StateCodes.int_code[new_state] self.command_en_ctrl.execute(new_state_str) - self.attributes['CommandEn'].set_value(self.command_en_ctrl.get_command_en()) + self.update_command_en() self.execution_routine() _logger.debug(f'Service state changed to {new_state}') + def update_command_en(self): + """ + Updates the CommandEn attribute based on the current enabled flags. + """ + self.attributes['CommandEn'].set_value(self.command_en_ctrl.get_command_en()) + def get_current_state_str(self) -> str: """ Get the current state as a string. From 67216c633cfcfbd5e2f5bfc60f6318fa4f158d44 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Thu, 31 Jul 2025 20:17:27 +0200 Subject: [PATCH 12/61] ability to stop idle Thread when switching to offline --- src/mtppy/service.py | 3 ++- src/mtppy/thread_control.py | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/mtppy/service.py b/src/mtppy/service.py index 39a27de..0a3d4db 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -68,6 +68,7 @@ def __init__(self, tag_name: str, tag_description: str): state_change_function=self.state_change()) self.op_src_mode.add_enter_offline_callback(self.state_machine.command_en_ctrl.disable_all) + self.op_src_mode.add_enter_offline_callback(self.thread_ctrl.stop_thread) self.op_src_mode.add_exit_offline_callback(self.state_machine.command_en_ctrl.set_default) self.op_src_mode.add_exit_offline_callback(self.state_machine.update_command_en) @@ -168,7 +169,7 @@ def idle(self): """ _logger.debug(f"{self.tag_name} - Idle -") cycle = 0 - while self.is_state("idle"): + while self.is_state("idle") and not self.thread_ctrl.thread.stop_event.is_set(): _logger.debug(f"{self.tag_name} - Idle cycle {cycle}") cycle += 1 time.sleep(3) diff --git a/src/mtppy/thread_control.py b/src/mtppy/thread_control.py index c024aee..6957518 100644 --- a/src/mtppy/thread_control.py +++ b/src/mtppy/thread_control.py @@ -54,10 +54,7 @@ def reallocate_running_thread(self): """ _logger.debug(f'Reallocate thread to state {self.requested_state}') if self.requested_state is not self.running_state: - # stop the current thread if it is running - if self.thread and self.thread.is_alive() and self.thread is not current_thread(): - _logger.debug(f'Stopping thread {self.thread.name}') - self.thread.stop() + self.stop_thread(stop_if_current_thread=False) self.thread = StoppableThread(target=self.run_thread, args=(self.callback_function,), name=f"{self.service_name}_{self.requested_state}") @@ -84,3 +81,15 @@ def run_thread(self, target_function: Callable): except Exception as e: self.exception = e self.exception_event.set() + + def stop_thread(self, stop_if_current_thread: bool = True): + """ + Stops the current thread if it is running. + + Args: + stop_if_current_thread (bool): If True, stops the thread also if it is the current thread. + """ + if self.thread and self.thread.is_alive(): + if stop_if_current_thread or self.thread is not current_thread(): + _logger.debug(f'Stopping thread {self.thread.name}') + self.thread.stop() From 286bd34b4aa3e340e7ae5a01d6a9a2e5a47e0c65 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Fri, 1 Aug 2025 11:52:55 +0200 Subject: [PATCH 13/61] fix ProcedureReq not being set bei is_default --- src/mtppy/procedure.py | 4 +++- src/mtppy/service.py | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/mtppy/procedure.py b/src/mtppy/procedure.py index b4bc868..9a9bca9 100644 --- a/src/mtppy/procedure.py +++ b/src/mtppy/procedure.py @@ -12,12 +12,14 @@ def __init__(self, procedure_id: int, tag_name: str, tag_description: str = '', Represents a procedure of a service. Args: - procedure_id (int): Procedure id. + procedure_id (int): Procedure id. Can't be equal or less than 0. tag_name (str): Tag name of the procedure. tag_description (str): Tag description of the procedure. is_self_completing (bool): Self-completing or not. is_default (bool): Default or not. """ + if procedure_id <= 0: + raise ValueError(f"{tag_name}: Procedure ID can't be equal or less than 0.") super().__init__(procedure_id, tag_name, tag_description, is_self_completing, is_default) self.procedure_parameters = {} self.process_value_ins = {} diff --git a/src/mtppy/service.py b/src/mtppy/service.py index 0a3d4db..4f9c1bd 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -162,6 +162,8 @@ def add_procedure(self, procedure: Procedure): self.procedure_control.attributes['ProcedureOp'].init_value = self.procedure_control.default_procedure_id self.procedure_control.attributes['ProcedureInt'].init_value = self.procedure_control.default_procedure_id self.procedure_control.attributes['ProcedureExt'].init_value = self.procedure_control.default_procedure_id + self.procedure_control.attributes['ProcedureReq'].init_value = self.procedure_control.default_procedure_id + self.procedure_control.set_procedure_req(self.procedure_control.default_procedure_id) def idle(self): """ From 048b7bef8218ccacd322d5eb23c9043a584dc325 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Fri, 1 Aug 2025 16:01:08 +0200 Subject: [PATCH 14/61] resetting Command variable after starting --- src/mtppy/procedure_control.py | 4 +++- src/mtppy/state_machine.py | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/mtppy/procedure_control.py b/src/mtppy/procedure_control.py index cd87a86..1ea817b 100644 --- a/src/mtppy/procedure_control.py +++ b/src/mtppy/procedure_control.py @@ -53,7 +53,9 @@ def set_procedure_req(self, value: int): self.attributes['ProcedureReq'].set_value(value) _logger.debug('ProcedureReq set to %s' % value) else: - _logger.debug('ProcedureReq cannot be set to %s (out of range)' % value) + self.attributes['ProcedureReq'].set_value(0) + if value != 0: + _logger.warning('ProcedureReq cannot be set to %s (out of range)' % value) def set_procedure_cur(self): procedure_req = self.attributes['ProcedureReq'].value diff --git a/src/mtppy/state_machine.py b/src/mtppy/state_machine.py index c48fa62..369ab03 100644 --- a/src/mtppy/state_machine.py +++ b/src/mtppy/state_machine.py @@ -81,6 +81,9 @@ def command_execution(self, com_var: int): def start(self): if self.command_en_ctrl.is_enabled('start'): self.procedure_control.set_procedure_cur() + self.procedure_control.attributes['ProcedureOp'].set_value(0) + self.procedure_control.attributes['ProcedureInt'].set_value(0) + self.procedure_control.attributes['ProcedureExt'].set_value(0) self.procedure_control.apply_procedure_parameters() self._change_state_to(StateCodes.starting) From a2f3de33b2e20254deb77285c65307700016131d Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Fri, 1 Aug 2025 17:16:36 +0200 Subject: [PATCH 15/61] disable commands if in idle no procedure is requested --- src/mtppy/attribute.py | 15 +++++++++++++++ src/mtppy/procedure_control.py | 11 +++++++++++ src/mtppy/state_machine.py | 34 ++++++++++++++++++++++++++++++++-- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/mtppy/attribute.py b/src/mtppy/attribute.py index 89df12c..82ee288 100644 --- a/src/mtppy/attribute.py +++ b/src/mtppy/attribute.py @@ -72,3 +72,18 @@ def attach_communication_object(self, communication_object): communication_object (type): Communication object. """ self.comm_obj = communication_object + + def attach_subscription_callback(self, sub_cb): + """ + Attach a subscription callback to the attribute. + + Args: + sub_cb (callable): Subscription callback. + """ + self.sub_cb = sub_cb + + def remove_subscription_callback(self): + """ + Remove the subscription callback from the attribute. + """ + self.sub_cb = None diff --git a/src/mtppy/procedure_control.py b/src/mtppy/procedure_control.py index 1ea817b..e8542f4 100644 --- a/src/mtppy/procedure_control.py +++ b/src/mtppy/procedure_control.py @@ -59,11 +59,22 @@ def set_procedure_req(self, value: int): def set_procedure_cur(self): procedure_req = self.attributes['ProcedureReq'].value + + if not self.valid_value(procedure_req): + if procedure_req == 0: + raise ValueError("No procedure requested. Please set a valid procedure ID.") + else: + raise ValueError( + f"No procedure with ID {procedure_req} exists. Please set a valid procedure ID.") + self.attributes['ProcedureCur'].set_value(procedure_req) _logger.debug('ProcedureCur set to %s' % procedure_req) def get_procedure_cur(self): return self.attributes['ProcedureCur'].value + def get_procedure_req(self): + return self.attributes['ProcedureReq'].value + def apply_procedure_parameters(self): self.procedures[self.get_procedure_cur()].apply_procedure_parameters() diff --git a/src/mtppy/state_machine.py b/src/mtppy/state_machine.py index 369ab03..0ecc76a 100644 --- a/src/mtppy/state_machine.py +++ b/src/mtppy/state_machine.py @@ -34,14 +34,25 @@ def __init__(self, operation_source_mode: OperationSourceMode, 'CommandEn': Attribute('CommandEn', int, init_value=0), } - self.op_src_mode = operation_source_mode - self.procedure_control = procedure_control + self.op_src_mode: OperationSourceMode = operation_source_mode + self.procedure_control: ProcedureControl = procedure_control self.execution_routine = execution_routine self.command_en_ctrl = CommandEnControl() self.act_state = StateCodes.idle self.prev_state = StateCodes.idle + self.op_src_mode.add_enter_operator_callback( + # adds function to disable commands if no procedure is set + # lambda allows adding function with argument + lambda: self.procedure_control.attributes['ProcedureReq'].attach_subscription_callback( + self.disable_commands_if_no_procedure) + ) + self.op_src_mode.add_exit_operator_callback( + # removes function to disables commands if no procedure is set + self.procedure_control.attributes['ProcedureReq'].remove_subscription_callback() + ) + def set_command_op(self, value: int): if self.op_src_mode.attributes['StateOpAct'].value: self.command_execution(value) @@ -80,6 +91,9 @@ def command_execution(self, com_var: int): def start(self): if self.command_en_ctrl.is_enabled('start'): + # removes the function to disables commands if no procedure is set + self.procedure_control.attributes['ProcedureReq'].remove_subscription_callback() + self.procedure_control.set_procedure_cur() self.procedure_control.attributes['ProcedureOp'].set_value(0) self.procedure_control.attributes['ProcedureInt'].set_value(0) @@ -106,6 +120,9 @@ def resume(self): def reset(self): if self.command_en_ctrl.is_enabled('reset'): self._change_state_to(StateCodes.resetting) + # adds function to disable commands if no procedure is set + self.procedure_control.attributes['ProcedureReq'].attach_subscription_callback( + self.disable_commands_if_no_procedure) def hold(self): if self.command_en_ctrl.is_enabled('hold'): @@ -170,3 +187,16 @@ def get_current_state_str(self) -> str: str: Current state as a string. """ return StateCodes.int_code[self.act_state] + + def disable_commands_if_no_procedure(self, value: int): + """ + Disables all commands if value is 0 and the current state is idle. + + Args: + value (int): Value of the ProcedureReq attribute. + """ + if self.act_state is StateCodes.idle and value == 0: + self.command_en_ctrl.disable_all() + else: + self.command_en_ctrl.execute(self.get_current_state_str()) + self.update_command_en() From 52889d2535c10431887fc7350383ddeee7c09f70 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Fri, 1 Aug 2025 17:47:05 +0200 Subject: [PATCH 16/61] Revert "changed imports to local version" This reverts commit 64b4225cb2e50a7becfe99d1a8638f2e0307b205. --- examples/virtual_pea_minimal.py | 34 ++++------ examples/virtual_pea_with_recipe.py | 65 +++++++------------ readme.md | 14 ++-- src/mtppy/active_elements.py | 51 +++++---------- src/mtppy/indicator_elements.py | 4 +- src/mtppy/mtp_generator.py | 23 +++---- src/mtppy/opcua_server_pea.py | 21 +++--- src/mtppy/operation_elements.py | 6 +- src/mtppy/operation_source_mode.py | 2 +- src/mtppy/procedure.py | 2 +- src/mtppy/procedure_control.py | 4 +- src/mtppy/service.py | 16 ++--- src/mtppy/state_machine.py | 12 ++-- src/mtppy/suc_data_assembly.py | 2 +- tests/test_ana_drv.py | 59 ++++++----------- tests/test_ana_vlv.py | 29 +++------ tests/test_bin_drv.py | 56 ++++++---------- tests/test_bin_vlv.py | 2 +- tests/test_mon_ana_drv.py | 59 ++++++----------- tests/test_mon_ana_vlv.py | 11 ++-- tests/test_mon_bin_drv.py | 26 +++----- tests/test_mon_bin_vlv.py | 8 +-- ...p_generator_instance_hierarchy_services.py | 36 ++++------ tests/test_mtp_generator_instance_list.py | 39 +++++------ tests/test_mtp_generator_structure.py | 11 ++-- tests/test_pid_ctrl.py | 8 +-- tests/test_procedure_control.py | 38 ++++------- tests/test_state_machine.py | 15 ++--- 28 files changed, 241 insertions(+), 412 deletions(-) diff --git a/examples/virtual_pea_minimal.py b/examples/virtual_pea_minimal.py index 3bfc9a9..8e9f797 100644 --- a/examples/virtual_pea_minimal.py +++ b/examples/virtual_pea_minimal.py @@ -1,10 +1,10 @@ -from MTPPy_Async.src.mtppy.opcua_server_pea import OPCUAServerPEA -from MTPPy_Async.src.mtppy.mtp_generator import MTPGenerator -from MTPPy_Async.src.mtppy.service import Service -from MTPPy_Async.src.mtppy.procedure import Procedure -from MTPPy_Async.src.mtppy.operation_elements import * -from MTPPy_Async.src.mtppy.indicator_elements import * -from MTPPy_Async.src.mtppy.active_elements import * +from mtppy.opcua_server_pea import OPCUAServerPEA +from mtppy.mtp_generator import MTPGenerator +from mtppy.service import Service +from mtppy.procedure import Procedure +from mtppy.operation_elements import * +from mtppy.indicator_elements import * +from mtppy.active_elements import * import time import random @@ -137,44 +137,32 @@ def resetting(self): opcua_server = module.get_opcua_server() opcua_ns = module.get_opcua_ns() time.sleep(1) - input('Press Enter to continue...') print('--- Set procedure parameters to Operator mode ---') - opcua_server.get_node( - 'ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateOpOp').set_value(True) - opcua_server.get_node( - 'ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateOpOp').set_value(True) + opcua_server.get_node('ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateOpOp').set_value(True) + opcua_server.get_node('ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateOpOp').set_value(True) time.sleep(1) - input('Press Enter to continue...') print('--- Set procedure parameter values ---') - opcua_server.get_node( - 'ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VOp').set_value(40) - opcua_server.get_node( - 'ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VOp').set_value(60) + opcua_server.get_node('ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VOp').set_value(40) + opcua_server.get_node('ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VOp').set_value(60) time.sleep(1) - input('Press Enter to continue...') print('--- Set service to Operator mode ---') opcua_server.get_node('ns=3;s=services.rand_num_gen.op_src_mode.StateOpOp').set_value(True) time.sleep(1) - input('Press Enter to continue...') print('--- Start service ---') opcua_server.get_node('ns=3;s=services.rand_num_gen.state_machine.CommandOp').set_value(4) time.sleep(10) - input('Press Enter to continue...') print('--- Complete service ---') opcua_server.get_node('ns=3;s=services.rand_num_gen.state_machine.CommandOp').set_value(1024) time.sleep(1) - input('Press Enter to continue...') print('--- Reset service ---') opcua_server.get_node('ns=3;s=services.rand_num_gen.state_machine.CommandOp').set_value(2) - input('Press Enter to continue...') print('--- Set service dummy to Offline mode ---') opcua_server.get_node('ns=3;s=services.rand_num_gen.op_src_mode.StateOffOp').set_value(True) time.sleep(1) - input('Press Enter to continue...') diff --git a/examples/virtual_pea_with_recipe.py b/examples/virtual_pea_with_recipe.py index 820ed61..903fcd0 100644 --- a/examples/virtual_pea_with_recipe.py +++ b/examples/virtual_pea_with_recipe.py @@ -1,16 +1,13 @@ -from MTPPy_Async.src.mtppy.opcua_server_pea import OPCUAServerPEA -from MTPPy_Async.src.mtppy.mtp_generator import MTPGenerator -from MTPPy_Async.src.mtppy.service import Service -from MTPPy_Async.src.mtppy.procedure import Procedure -from MTPPy_Async.src.mtppy.operation_elements import * -from MTPPy_Async.src.mtppy.indicator_elements import * +from mtppy.opcua_server_pea import OPCUAServerPEA +from mtppy.mtp_generator import MTPGenerator +from mtppy.service import Service +from mtppy.procedure import Procedure +from mtppy.operation_elements import * +from mtppy.indicator_elements import * import time import random from datetime import datetime -import logging - -logging.getLogger("opcua").setLevel(logging.ERROR) class ServiceDummy(Service): @@ -21,10 +18,8 @@ def __init__(self, tag_name, tag_description): def add_service_parameters(self): serv_parameters = [AnaServParam('serv_param_ana', v_min=0, v_max=50, v_scl_min=0, v_scl_max=10, v_unit=23), - DIntServParam('serv_param_dint', v_min=-10, v_max=10, - v_scl_min=0, v_scl_max=-10, v_unit=23), - BinServParam('serv_param_bin', v_state_0='state_0', - v_state_1='state_1'), + DIntServParam('serv_param_dint', v_min=-10, v_max=10, v_scl_min=0, v_scl_max=-10, v_unit=23), + BinServParam('serv_param_bin', v_state_0='state_0', v_state_1='state_1'), StringServParam('serv_param_str') ] [self.add_configuration_parameter(serv_param) for serv_param in serv_parameters] @@ -39,10 +34,8 @@ def add_procedures(self): # Procedure 3 proc_3 = Procedure(2, 'proc_3', is_self_completing=True, is_default=False) proc_parameters = [AnaServParam('proc_param_ana', v_min=0, v_max=50, v_scl_min=0, v_scl_max=10, v_unit=23), - DIntServParam('proc_param_dint', v_min=-10, v_max=10, - v_scl_min=0, v_scl_max=-10, v_unit=23), - BinServParam('proc_param_bin', v_state_0='state_0', - v_state_1='state_1'), + DIntServParam('proc_param_dint', v_min=-10, v_max=10, v_scl_min=0, v_scl_max=-10, v_unit=23), + BinServParam('proc_param_bin', v_state_0='state_0', v_state_1='state_1'), StringServParam('proc_param_str'), ] [proc_3.add_procedure_parameter(proc_param) for proc_param in proc_parameters] @@ -142,14 +135,14 @@ def resetting(self): if __name__ == '__main__': - # writer_info_dict = {'WriterName': 'tud/plt', 'WriterID': 'tud/plt', 'WriterVendor': 'tud', - # 'WriterVendorURL': 'www.tud.de', - # 'WriterVersion': '1.0.0', 'WriterRelease': '', 'LastWritingDateTime': str(datetime.now()), - # 'WriterProjectTitle': 'tu/plt/mtp', 'WriterProjectID': ''} - # export_manifest_path = '../manifest_files/example_recipe_manifest.aml' - # mtp_generator = MTPGenerator(writer_info_dict, export_manifest_path) + writer_info_dict = {'WriterName': 'tud/plt', 'WriterID': 'tud/plt', 'WriterVendor': 'tud', + 'WriterVendorURL': 'www.tud.de', + 'WriterVersion': '1.0.0', 'WriterRelease': '', 'LastWritingDateTime': str(datetime.now()), + 'WriterProjectTitle': 'tu/plt/mtp', 'WriterProjectID': ''} + export_manifest_path = '../manifest_files/example_recipe_manifest.aml' + mtp_generator = MTPGenerator(writer_info_dict, export_manifest_path) - module = OPCUAServerPEA() + module = OPCUAServerPEA(mtp_generator) # Service definition service_1 = ServiceDummy('dummy', 'description') @@ -160,12 +153,10 @@ def resetting(self): module.run_opcua_server() # Test - input('Press Enter to start test...') opcua_server = module.get_opcua_server() opcua_ns = module.get_opcua_ns() time.sleep(1) print('--- Set parameters of service dummy to Operator mode ---') - input('Press Enter to continue...') opcua_server.get_node( 'ns=3;s=services.dummy.configuration_parameters.serv_param_ana.op_src_mode.StateOpOp').set_value(True) opcua_server.get_node( @@ -178,48 +169,36 @@ def resetting(self): time.sleep(1) print('--- Set parameters VOp of service dummy to different values ---') - input('Press Enter to continue...') - opcua_server.get_node( - 'ns=3;s=services.dummy.configuration_parameters.serv_param_ana.VOp').set_value(10.54) - opcua_server.get_node( - 'ns=3;s=services.dummy.configuration_parameters.serv_param_dint.VOp').set_value(-5.11) - opcua_server.get_node( - 'ns=3;s=services.dummy.configuration_parameters.serv_param_bin.VOp').set_value(True) - opcua_server.get_node( - 'ns=3;s=services.dummy.configuration_parameters.serv_param_str.VOp').set_value('hello there') + opcua_server.get_node('ns=3;s=services.dummy.configuration_parameters.serv_param_ana.VOp').set_value(10.54) + opcua_server.get_node('ns=3;s=services.dummy.configuration_parameters.serv_param_dint.VOp').set_value(-5.11) + opcua_server.get_node('ns=3;s=services.dummy.configuration_parameters.serv_param_bin.VOp').set_value(True) + opcua_server.get_node('ns=3;s=services.dummy.configuration_parameters.serv_param_str.VOp').set_value('hello there') time.sleep(1) print('--- Change procedure to 2 ---') - input('Press Enter to continue...') opcua_server.get_node('ns=3;s=services.dummy.procedure_control.ProcedureOp').set_value(2) time.sleep(1) print('--- Set service dummy to Operator mode ---') - input('Press Enter to continue...') opcua_server.get_node('ns=3;s=services.dummy.op_src_mode.StateOpOp').set_value(True) time.sleep(2) print('--- Start service dummy ---') - input('Press Enter to continue...') opcua_server.get_node('ns=3;s=services.dummy.state_machine.CommandOp').set_value(4) - time.sleep(5) + time.sleep(500) print('--- Try to unhold service dummy ---') - input('Press Enter to continue...') opcua_server.get_node('ns=3;s=services.dummy.state_machine.CommandOp').set_value(32) time.sleep(2) print('--- Complete service dummy ---') - input('Press Enter to continue...') opcua_server.get_node('ns=3;s=services.dummy.state_machine.CommandOp').set_value(1024) time.sleep(1) print('--- Reset service dummy ---') - input('Press Enter to continue...') opcua_server.get_node('ns=3;s=services.dummy.state_machine.CommandOp').set_value(2) print('--- Set service dummy to Offline mode ---') - input('Press Enter to continue...') opcua_server.get_node('ns=3;s=services.dummy.op_src_mode.StateOffOp').set_value(True) time.sleep(1) diff --git a/readme.md b/readme.md index 7eecb23..458e3df 100644 --- a/readme.md +++ b/readme.md @@ -75,10 +75,10 @@ After definition of the service set, the MTP file can be generated and the insta ### Service definition ```python -from MTPPy_Async.src.mtppy.service import Service -from MTPPy_Async.src.mtppy.procedure import Procedure -from MTPPy_Async.src.mtppy.operation_elements import * -from MTPPy_Async.src.mtppy.indicator_elements import * +from mtppy.service import Service +from mtppy.procedure import Procedure +from mtppy.operation_elements import * +from mtppy.indicator_elements import * class RandomNumberGenerator(Service): def __init__(self, tag_name, tag_description): @@ -181,8 +181,8 @@ In additional, active elemements that are service-independent should be defined ### PEA definition Now, the service with its procedure can be added to the PEA instance. ```python -from MTPPy_Async.src.mtppy.opcua_server_pea import OPCUAServerPEA -from MTPPy_Async.src.mtppy.active_elements import PIDCtrl +from mtppy.opcua_server_pea import OPCUAServerPEA +from mtppy.active_elements import PIDCtrl module = OPCUAServerPEA() @@ -203,7 +203,7 @@ The last line will start the OPC UA server. ### MTP generation To generate an MTP manifest, instantiate an MTP generator. ```python -from MTPPy_Async.src.mtppy.mtp_generator import MTPGenerator +from mtppy.mtp_generator import MTPGenerator writer_info_dict = {'WriterName': 'tud/plt', 'WriterID': 'tud/plt', 'WriterVendor': 'tud', 'WriterVendorURL': 'www.tud.de', diff --git a/src/mtppy/active_elements.py b/src/mtppy/active_elements.py index 165db4c..bdb610a 100644 --- a/src/mtppy/active_elements.py +++ b/src/mtppy/active_elements.py @@ -1,10 +1,10 @@ import logging import threading -from MTPPy_Async.src.mtppy.attribute import Attribute +from mtppy.attribute import Attribute -from MTPPy_Async.src.mtppy.operation_source_mode import OperationSourceModeActiveElements -from MTPPy_Async.src.mtppy.suc_data_assembly import SUCActiveElement +from mtppy.operation_source_mode import OperationSourceModeActiveElements +from mtppy.suc_data_assembly import SUCActiveElement from time import sleep from simple_pid import PID @@ -41,8 +41,7 @@ def __init__(self, tag_name, tag_description='', self._add_attribute(Attribute('SafePos', bool, init_value=safe_pos)) self._add_attribute(Attribute('SafePosEn', bool, init_value=self.safe_pos_en)) - self._add_attribute(Attribute('SafePosAct', bool, init_value=False) - ) # default value should be true? + self._add_attribute(Attribute('SafePosAct', bool, init_value=False)) # default value should be true? self._add_attribute(Attribute('OpenAut', bool, init_value=0, sub_cb=self.set_open_aut)) self._add_attribute(Attribute('CloseAut', bool, init_value=0, sub_cb=self.set_close_aut)) self._add_attribute(Attribute('OpenOp', bool, init_value=0, sub_cb=self.set_open_op)) @@ -398,8 +397,7 @@ def monitor_static_error(self): if self.monitored_values.stop_event_lock.is_set(): _logger.debug('static monitoring stopped') break - states, control_signals = self.compare_states_control_signals( - self.attributes['MonStatTi'].value) + states, control_signals = self.compare_states_control_signals(self.attributes['MonStatTi'].value) if not all(states): # if the states of valve changed if all(control_signals): # but the control signals did not change @@ -415,8 +413,7 @@ def monitor_dynamic_error(self): if self.monitored_values.stop_event_lock.is_set(): _logger.debug('dynamic monitoring stopped') break - states, control_signals = self.compare_states_control_signals( - self.attributes['MonDynTi'].value) + states, control_signals = self.compare_states_control_signals(self.attributes['MonDynTi'].value) if all(states): # if the states of valve did not changed if not all(control_signals): # but a control command is executed @@ -578,8 +575,7 @@ def __init__(self, tag_name: str, tag_description: str = '', open_fbk_calc: bool self._add_attribute(Attribute('SafePos', bool, init_value=safe_pos)) self._add_attribute(Attribute('SafePosEn', bool, init_value=self.safe_pos_en)) - self._add_attribute(Attribute('SafePosAct', bool, init_value=False) - ) # default value should be true? + self._add_attribute(Attribute('SafePosAct', bool, init_value=False)) # default value should be true? self._add_attribute(Attribute('OpenOp', bool, init_value=0, sub_cb=self.set_open_op)) self._add_attribute(Attribute('CloseOp', bool, init_value=0, sub_cb=self.set_close_op)) self._add_attribute(Attribute('OpenAut', bool, init_value=0, sub_cb=self.set_open_aut)) @@ -812,8 +808,7 @@ def monitor_static_error(self): if self.monitored_values.stop_event_lock.is_set(): _logger.debug('static monitoring stopped') break - states, control_signals = self.compare_states_control_signals( - self.attributes['MonStatTi'].value) + states, control_signals = self.compare_states_control_signals(self.attributes['MonStatTi'].value) if not all(states): # if the states of valve changed if all(control_signals): # but the control signals did not change @@ -829,8 +824,7 @@ def monitor_dynamic_error(self): if self.monitored_values.stop_event_lock.is_set(): _logger.debug('dynamic monitoring stopped') break - states, control_signals = self.compare_states_control_signals( - self.attributes['MonDynTi'].value) + states, control_signals = self.compare_states_control_signals(self.attributes['MonDynTi'].value) if all(states): # if the states of valve did not changed if not all(control_signals): # but a control command is executed @@ -942,8 +936,7 @@ def __init__(self, tag_name, tag_description='', rev_fbk_calc=True, fwd_fbk_calc self.prot_en = prot_en self._add_attribute(Attribute('SafePos', bool, init_value=safe_pos)) - self._add_attribute(Attribute('SafePosAct', bool, init_value=False) - ) # default value should be true? + self._add_attribute(Attribute('SafePosAct', bool, init_value=False)) # default value should be true? self._add_attribute(Attribute('FwdEn', bool, init_value=fwd_en)) self._add_attribute(Attribute('RevEn', bool, init_value=rev_en)) self._add_attribute(Attribute('StopOp', bool, init_value=0, sub_cb=self.set_stop_op)) @@ -1199,8 +1192,7 @@ def monitor_static_error(self): if self.monitored_values.stop_event_lock.is_set(): _logger.debug('static monitoring stopped') break - states, control_signals = self.compare_states_control_signals( - self.attributes['MonStatTi'].value) + states, control_signals = self.compare_states_control_signals(self.attributes['MonStatTi'].value) if not all(states): if all(control_signals): @@ -1213,8 +1205,7 @@ def monitor_dynamic_error(self): if self.monitored_values.stop_event_lock.is_set(): _logger.debug('dynamic monitoring stopped') break - states, control_signals = self.compare_states_control_signals( - self.attributes['MonDynTi'].value) + states, control_signals = self.compare_states_control_signals(self.attributes['MonDynTi'].value) if all(states): if not all(control_signals): @@ -1689,8 +1680,7 @@ def monitor_static_error(self): if self.monitored_values.stop_event_lock.is_set(): _logger.debug('static monitoring stopped') break - states, control_signals = self.compare_states_control_signals( - self.attributes['MonStatTi'].value) + states, control_signals = self.compare_states_control_signals(self.attributes['MonStatTi'].value) if not all(states): if all(control_signals): @@ -1703,8 +1693,7 @@ def monitor_dynamic_error(self): if self.monitored_values.stop_event_lock.is_set(): _logger.debug('dynamic monitoring stopped') break - states, control_signals = self.compare_states_control_signals( - self.attributes['MonDynTi'].value) + states, control_signals = self.compare_states_control_signals(self.attributes['MonDynTi'].value) if all(states): if not all(control_signals): @@ -1771,8 +1760,7 @@ def start_monitor(self): _logger.debug('rpm error monitoring start') if self.attributes['RpmAHEn'].value: - self.monitor_rpm_limit_high_thread = threading.Thread( - target=self.monitor_rpm_high_limit) + self.monitor_rpm_limit_high_thread = threading.Thread(target=self.monitor_rpm_high_limit) self.monitor_rpm_limit_high_thread.start() _logger.debug('rpm high limit monitoring start') @@ -1898,8 +1886,7 @@ def __init__(self, mv_init_value=0, kp=100, ki=10, kd=1, mv_min=0, mv_max=100, s self.mv = mv_init_value self.sample_time = sample_time - self.ctrl = PID(Kp=self.kp, Ki=self.ki, Kd=self.kd, - output_limits=(mv_min, mv_max), sample_time=sample_time) + self.ctrl = PID(Kp=self.kp, Ki=self.ki, Kd=self.kd, output_limits=(mv_min, mv_max), sample_time=sample_time) self.thread = threading.Thread(target=self.loop) self.stop_flag = True @@ -1988,10 +1975,8 @@ def __init__(self, tag_name, tag_description='', self._add_attribute(Attribute('PVSclMin', float, init_value=pv_scl_min)) self._add_attribute(Attribute('PVSclMax', float, init_value=pv_scl_max)) self._add_attribute(Attribute('PVUnit', int, init_value=pv_unit)) - self._add_attribute( - Attribute('SPMan', float, init_value=sp_man_min, sub_cb=self.set_sp_man)) - self._add_attribute( - Attribute('SPInt', float, init_value=sp_int_min, sub_cb=self.set_sp_int)) + self._add_attribute(Attribute('SPMan', float, init_value=sp_man_min, sub_cb=self.set_sp_man)) + self._add_attribute(Attribute('SPInt', float, init_value=sp_int_min, sub_cb=self.set_sp_int)) self._add_attribute(Attribute('SPSclMin', float, init_value=sp_scl_min)) self._add_attribute(Attribute('SPSclMax', float, init_value=sp_scl_max)) self._add_attribute(Attribute('SPUnit', int, init_value=sp_unit)) diff --git a/src/mtppy/indicator_elements.py b/src/mtppy/indicator_elements.py index abf932a..97642f7 100644 --- a/src/mtppy/indicator_elements.py +++ b/src/mtppy/indicator_elements.py @@ -1,7 +1,7 @@ import logging -from MTPPy_Async.src.mtppy.attribute import Attribute -from MTPPy_Async.src.mtppy.suc_data_assembly import SUCIndicatorElement +from mtppy.attribute import Attribute +from mtppy.suc_data_assembly import SUCIndicatorElement _logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") diff --git a/src/mtppy/mtp_generator.py b/src/mtppy/mtp_generator.py index cb5603c..838a383 100644 --- a/src/mtppy/mtp_generator.py +++ b/src/mtppy/mtp_generator.py @@ -1,4 +1,4 @@ -from MTPPy_Async.src.mtppy.suc_data_assembly import * +from mtppy.suc_data_assembly import * import xml.etree.ElementTree as ET import random import string @@ -197,8 +197,7 @@ def add_components_to_services(self, data_assembly: SUCDataAssembly, sc_id: str, elif section == 'configuration_parameters': s_component = ET.SubElement(self.service, 'InternalElement') - s_component.set('RefBaseSystemUnitPath', - 'MTPServiceSUCLib/ServiceParameter/ConfigurationParameter') + s_component.set('RefBaseSystemUnitPath', 'MTPServiceSUCLib/ServiceParameter/ConfigurationParameter') elif section == 'procedures': s_component = ET.SubElement(self.service, 'InternalElement') @@ -224,23 +223,19 @@ def add_components_to_services(self, data_assembly: SUCDataAssembly, sc_id: str, elif section == 'procedure_parameters': s_component = ET.SubElement(self.service_procedure, 'InternalElement') - s_component.set('RefBaseSystemUnitPath', - 'MTPServiceSUCLib/ServiceParameter/ProcedureParameter') + s_component.set('RefBaseSystemUnitPath', 'MTPServiceSUCLib/ServiceParameter/ProcedureParameter') elif section == 'process_value_ins': s_component = ET.SubElement(self.service_procedure, 'InternalElement') - s_component.set('RefBaseSystemUnitPath', - 'MTPServiceSUCLib/ServiceParameter/ProcessValueIn') + s_component.set('RefBaseSystemUnitPath', 'MTPServiceSUCLib/ServiceParameter/ProcessValueIn') elif section == 'report_values': s_component = ET.SubElement(self.service_procedure, 'InternalElement') - s_component.set('RefBaseSystemUnitPath', - 'MTPServiceSUCLib/ServiceParameter/ReportValue') + s_component.set('RefBaseSystemUnitPath', 'MTPServiceSUCLib/ServiceParameter/ReportValue') elif section == 'process_value_outs': s_component = ET.SubElement(self.service_procedure, 'InternalElement') - s_component.set('RefBaseSystemUnitPath', - 'MTPServiceSUCLib/ServiceParameter/ProcessValueOut') + s_component.set('RefBaseSystemUnitPath', 'MTPServiceSUCLib/ServiceParameter/ProcessValueOut') else: raise TypeError('service components type error') @@ -352,8 +347,7 @@ def add_external_interface(self, opc_node_id: str, opc_ns: int, linked_attr_id: node_name = opc_node_id.split('=')[-1] elem = ET.SubElement(self.opcua_server, 'ExternalInterface') - self.generate_attributes(elem, node_name, linked_attr_id, - 'MTPCommunicationICLib/DataItem/OPCUAItem') + self.generate_attributes(elem, node_name, linked_attr_id, 'MTPCommunicationICLib/DataItem/OPCUAItem') # opc ua node attribute: identifier attr_identifier = ET.SubElement(elem, 'Attribute') @@ -398,8 +392,7 @@ def add_supported_role_class(self, parent: ET.Element): """ for internal_element in parent.findall('InternalElement'): SupportedRoleClass = ET.SubElement(internal_element, 'SupportedRoleClass') - SupportedRoleClass.set( - 'RefRoleClassPath', 'AutomationMLBaseRoleClassLib/AutomationMLBaseRole') + SupportedRoleClass.set('RefRoleClassPath', 'AutomationMLBaseRoleClassLib/AutomationMLBaseRole') if internal_element.findall('InternalElement'): self.add_supported_role_class(internal_element) diff --git a/src/mtppy/opcua_server_pea.py b/src/mtppy/opcua_server_pea.py index f1d410b..5a730c2 100644 --- a/src/mtppy/opcua_server_pea.py +++ b/src/mtppy/opcua_server_pea.py @@ -1,15 +1,15 @@ import logging from opcua import Server, ua -from MTPPy_Async.src.mtppy.communication_object import OPCUACommunicationObject -from MTPPy_Async.src.mtppy.service import Service -from MTPPy_Async.src.mtppy.suc_data_assembly import SUCDataAssembly, SUCActiveElement -from MTPPy_Async.src.mtppy.mtp_generator import MTPGenerator +from mtppy.communication_object import OPCUACommunicationObject +from mtppy.service import Service +from mtppy.suc_data_assembly import SUCDataAssembly, SUCActiveElement +from mtppy.mtp_generator import MTPGenerator _logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") class OPCUAServerPEA: - def __init__(self, mtp_generator: MTPGenerator = None, endpoint: str = 'opc.tcp://127.0.0.1:4840/'): + def __init__(self, mtp_generator: MTPGenerator = None, endpoint: str ='opc.tcp://127.0.0.1:4840/'): """ Defines an OPC UA server for PEA. @@ -139,7 +139,7 @@ def _create_opcua_objects_for_folders(self, data_assembly: SUCDataAssembly, pare # type of data assembly (e.g. services, active_elements, procedures etc.) da_type = parent_opcua_prefix.split('=')[-1].split('.')[-1] - folders = ['configuration_parameters', 'procedures', 'procedure_parameters', + folders = ['configuration_parameters', 'procedures','procedure_parameters', 'process_value_ins', 'report_values', 'process_value_outs'] leaves = ['op_src_mode', 'state_machine', 'procedure_control'] @@ -167,11 +167,9 @@ def _create_opcua_objects_for_folders(self, data_assembly: SUCDataAssembly, pare section_node = da_node.add_folder(section_node_id, section_name) if section_name in folders: for parameter in eval(f'data_assembly.{section_name}.values()'): - self._create_opcua_objects_for_folders( - parameter, section_node_id, section_node) + self._create_opcua_objects_for_folders(parameter, section_node_id, section_node) if section_name in leaves: - self._create_opcua_objects_for_leaves( - section, section_node_id, section_node, instance) + self._create_opcua_objects_for_leaves(section, section_node_id, section_node, instance) # create linked obj between instance and service component if self.mtp: @@ -219,8 +217,7 @@ def _create_opcua_objects_for_leaves(self, opcua_object, parent_opcua_prefix: st pass else: if self.mtp: - self.mtp.add_attr_to_instance( - par_instance, attr.name, attr.init_value, linked_id) + self.mtp.add_attr_to_instance(par_instance, attr.name, attr.init_value, linked_id) # We subscribe to nodes that are writable attributes if attr.sub_cb is not None: diff --git a/src/mtppy/operation_elements.py b/src/mtppy/operation_elements.py index 133a536..a4dbadf 100644 --- a/src/mtppy/operation_elements.py +++ b/src/mtppy/operation_elements.py @@ -1,8 +1,8 @@ import logging -from MTPPy_Async.src.mtppy.attribute import Attribute -from MTPPy_Async.src.mtppy.operation_source_mode import OperationSourceMode -from MTPPy_Async.src.mtppy.suc_data_assembly import SUCOperationElement +from mtppy.attribute import Attribute +from mtppy.operation_source_mode import OperationSourceMode +from mtppy.suc_data_assembly import SUCOperationElement _logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") diff --git a/src/mtppy/operation_source_mode.py b/src/mtppy/operation_source_mode.py index 5dc0935..aec133a 100644 --- a/src/mtppy/operation_source_mode.py +++ b/src/mtppy/operation_source_mode.py @@ -1,6 +1,6 @@ import logging -from MTPPy_Async.src.mtppy.attribute import Attribute +from mtppy.attribute import Attribute _logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") diff --git a/src/mtppy/procedure.py b/src/mtppy/procedure.py index 9a9bca9..e3c14f4 100644 --- a/src/mtppy/procedure.py +++ b/src/mtppy/procedure.py @@ -1,6 +1,6 @@ import logging -from MTPPy_Async.src.mtppy.suc_data_assembly import * +from mtppy.suc_data_assembly import * _logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") diff --git a/src/mtppy/procedure_control.py b/src/mtppy/procedure_control.py index e8542f4..7439489 100644 --- a/src/mtppy/procedure_control.py +++ b/src/mtppy/procedure_control.py @@ -1,7 +1,7 @@ import logging -from MTPPy_Async.src.mtppy.attribute import Attribute -from MTPPy_Async.src.mtppy.operation_source_mode import OperationSourceMode +from mtppy.attribute import Attribute +from mtppy.operation_source_mode import OperationSourceMode _logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") diff --git a/src/mtppy/service.py b/src/mtppy/service.py index 4f9c1bd..de776ad 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -4,14 +4,14 @@ from abc import abstractmethod -from MTPPy_Async.src.mtppy.suc_data_assembly import SUCServiceControl -from MTPPy_Async.src.mtppy.thread_control import ThreadControl -from MTPPy_Async.src.mtppy.operation_source_mode import OperationSourceMode -from MTPPy_Async.src.mtppy.state_machine import StateMachine -from MTPPy_Async.src.mtppy.procedure_control import ProcedureControl -from MTPPy_Async.src.mtppy.state_codes import StateCodes -from MTPPy_Async.src.mtppy.procedure import Procedure -from MTPPy_Async.src.mtppy.suc_data_assembly import SUCOperationElement +from mtppy.suc_data_assembly import SUCServiceControl +from mtppy.thread_control import ThreadControl +from mtppy.operation_source_mode import OperationSourceMode +from mtppy.state_machine import StateMachine +from mtppy.procedure_control import ProcedureControl +from mtppy.state_codes import StateCodes +from mtppy.procedure import Procedure +from mtppy.suc_data_assembly import SUCOperationElement _logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") diff --git a/src/mtppy/state_machine.py b/src/mtppy/state_machine.py index 0ecc76a..858c508 100644 --- a/src/mtppy/state_machine.py +++ b/src/mtppy/state_machine.py @@ -1,11 +1,11 @@ import logging -from MTPPy_Async.src.mtppy.attribute import Attribute -from MTPPy_Async.src.mtppy.state_codes import StateCodes -from MTPPy_Async.src.mtppy.command_codes import CommandCodes -from MTPPy_Async.src.mtppy.command_en_control import CommandEnControl -from MTPPy_Async.src.mtppy.operation_source_mode import OperationSourceMode -from MTPPy_Async.src.mtppy.procedure_control import ProcedureControl +from mtppy.attribute import Attribute +from mtppy.state_codes import StateCodes +from mtppy.command_codes import CommandCodes +from mtppy.command_en_control import CommandEnControl +from mtppy.operation_source_mode import OperationSourceMode +from mtppy.procedure_control import ProcedureControl StateCodes = StateCodes() CommandCodes = CommandCodes() diff --git a/src/mtppy/suc_data_assembly.py b/src/mtppy/suc_data_assembly.py index 203889c..a077ed3 100644 --- a/src/mtppy/suc_data_assembly.py +++ b/src/mtppy/suc_data_assembly.py @@ -1,4 +1,4 @@ -from MTPPy_Async.src.mtppy.attribute import Attribute +from mtppy.attribute import Attribute class SUCDataAssembly: diff --git a/tests/test_ana_drv.py b/tests/test_ana_drv.py index b30ac31..e68e83e 100644 --- a/tests/test_ana_drv.py +++ b/tests/test_ana_drv.py @@ -1,4 +1,4 @@ -from MTPPy_Async.src.mtppy.active_elements import AnaDrv +from mtppy.active_elements import AnaDrv def init_ana_drv(op_mode='off', src_mode='int', rev_fbk_calc=True, fwd_fbk_calc=True, rpm_fbk_calc=True, @@ -60,8 +60,7 @@ def test_fwd_rev(): for command in [True, False]: ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True) eval(f'ana_drv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') if set_command in ['set_fwd_op', 'set_fwd_aut']: if command: assert ana_drv.attributes['FwdCtrl'].value == expected_result @@ -112,8 +111,7 @@ def test_fwd_rev_trip(): def test_fwd_rev_permit_en_true(): for op_mode, src_mode, set_command, _ in test_scenario: for command in [True, False]: - ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, - fwd_en=True, rev_en=True, perm_en=True) + ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, perm_en=True) ana_drv.set_permit(False) eval(f'ana_drv.{set_command}({command})') print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: False') @@ -124,12 +122,10 @@ def test_fwd_rev_permit_en_true(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, - fwd_en=True, rev_en=True, perm_en=True) + ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, perm_en=True) ana_drv.set_permit(True) eval(f'ana_drv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert ana_drv.attributes['Permit'].value == True assert ana_drv.attributes['SafePosAct'].value == False @@ -152,12 +148,10 @@ def test_fwd_rev_permit_en_true(): def test_fwd_rev_permit_en_false(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, - fwd_en=True, rev_en=True, perm_en=False) + ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, perm_en=False) ana_drv.set_permit(True) eval(f'ana_drv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert ana_drv.attributes['Permit'].value == True assert ana_drv.attributes['SafePosAct'].value == False @@ -180,8 +174,7 @@ def test_fwd_rev_permit_en_false(): def test_fwd_rev_interlock_en_true(): for op_mode, src_mode, set_command, _ in test_scenario: for command in [True, False]: - ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, - fwd_en=True, rev_en=True, intl_en=True) + ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, intl_en=True) ana_drv.set_interlock(False) eval(f'ana_drv.{set_command}({command})') print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: False') @@ -193,12 +186,10 @@ def test_fwd_rev_interlock_en_true(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, - fwd_en=True, rev_en=True, intl_en=True) + ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, intl_en=True) ana_drv.set_interlock(True) eval(f'ana_drv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert ana_drv.attributes['Interlock'].value == True assert ana_drv.attributes['SafePosAct'].value == False @@ -221,12 +212,10 @@ def test_fwd_rev_interlock_en_true(): def test_fwd_rev_interlock_en_false(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, - fwd_en=True, rev_en=True, intl_en=False) + ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, intl_en=False) ana_drv.set_interlock(True) eval(f'ana_drv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert ana_drv.attributes['Interlock'].value == True assert ana_drv.attributes['SafePosAct'].value == False @@ -249,8 +238,7 @@ def test_fwd_rev_interlock_en_false(): def test_fwd_rev_protect_en_true(): for op_mode, src_mode, set_command, _ in test_scenario: for command in [True, False]: - ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, - fwd_en=True, rev_en=True, prot_en=True) + ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, prot_en=True) ana_drv.set_protect(False) eval(f'ana_drv.{set_command}({command})') print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: False') @@ -262,12 +250,10 @@ def test_fwd_rev_protect_en_true(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, - fwd_en=True, rev_en=True, prot_en=True) + ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, prot_en=True) ana_drv.set_protect(True) eval(f'ana_drv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert ana_drv.attributes['Protect'].value == True assert ana_drv.attributes['SafePosAct'].value == False @@ -290,12 +276,10 @@ def test_fwd_rev_protect_en_true(): def test_fwd_rev_protect_en_false(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, - fwd_en=True, rev_en=True, prot_en=False) + ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, prot_en=False) ana_drv.set_protect(True) eval(f'ana_drv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert ana_drv.attributes['Protect'].value == True assert ana_drv.attributes['SafePosAct'].value == False @@ -333,8 +317,7 @@ def test_fwd_rev_protect_en_false(): def test_reset(): for op_mode, src_mode, set_command, result in test_scenario_reset: for command in [True, False]: - ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, - fwd_en=True, rev_en=True, prot_en=True) + ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, prot_en=True) ana_drv.set_protect(False) assert ana_drv.attributes['Protect'].value == False @@ -369,8 +352,7 @@ def test_stop(): ana_drv.set_fwd_op(True) ana_drv.set_fwd_aut(True) - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {changes_expected}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {changes_expected}') if changes_expected: assert ana_drv.attributes['FwdCtrl'].value == True eval(f'ana_drv.{set_command}({command})') @@ -403,8 +385,7 @@ def test_rpm(): ana_drv = init_ana_drv(op_mode=op_mode, src_mode=src_mode, rpm_fbk_calc=True) eval(f'ana_drv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') if changes_expected: if -10 <= command <= 1000: assert ana_drv.get_rpm() == command diff --git a/tests/test_ana_vlv.py b/tests/test_ana_vlv.py index 59e39f4..0b5c9f9 100644 --- a/tests/test_ana_vlv.py +++ b/tests/test_ana_vlv.py @@ -1,5 +1,5 @@ import pytest -from MTPPy_Async.src.mtppy.active_elements import AnaVlv +from mtppy.active_elements import AnaVlv def init_ana_vlv(op_mode='off', src_mode='int', open_fbk_calc=True, close_fbk_calc=True, pos_fbk_calc=True, @@ -61,8 +61,7 @@ def test_open_close(): for command in [True, False]: ana_vlv = init_ana_vlv(op_mode=op_mode, src_mode=src_mode) eval(f'ana_vlv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') if set_command in ['set_open_op', 'set_open_aut']: if command: assert ana_vlv.attributes['OpenAct'].value == expected_result @@ -106,8 +105,7 @@ def test_open_close_permit_en_true(): ana_vlv = init_ana_vlv(op_mode=op_mode, src_mode=src_mode, perm_en=True) ana_vlv.set_permit(True) eval(f'ana_vlv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert ana_vlv.attributes['Permit'].value == True assert ana_vlv.attributes['SafePosAct'].value == False @@ -141,8 +139,7 @@ def test_open_close_permit_en_false(): ana_vlv = init_ana_vlv(op_mode=op_mode, src_mode=src_mode, perm_en=False) ana_vlv.set_permit(True) eval(f'ana_vlv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert ana_vlv.attributes['Permit'].value == True assert ana_vlv.attributes['SafePosAct'].value == False @@ -190,8 +187,7 @@ def test_open_close_interlock_en_true(): ana_vlv = init_ana_vlv(op_mode=op_mode, src_mode=src_mode, intl_en=True) ana_vlv.set_interlock(True) eval(f'ana_vlv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert ana_vlv.attributes['Interlock'].value == True assert ana_vlv.attributes['SafePosAct'].value == False @@ -225,8 +221,7 @@ def test_open_close_interlock_en_false(): ana_vlv = init_ana_vlv(op_mode=op_mode, src_mode=src_mode, intl_en=False) ana_vlv.set_interlock(True) eval(f'ana_vlv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert ana_vlv.attributes['Interlock'].value == True assert ana_vlv.attributes['SafePosAct'].value == False @@ -277,8 +272,7 @@ def test_open_close_protect_en_true(): assert ana_vlv.attributes['CloseAct'].value == False # state after calling reset eval(f'ana_vlv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') if set_command in ['set_open_op', 'set_open_aut']: if command: @@ -316,8 +310,7 @@ def test_open_close_protect_en_false(): assert ana_vlv.attributes['CloseAct'].value == False # state after calling reset eval(f'ana_vlv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') if set_command in ['set_open_op', 'set_open_aut']: if command: assert ana_vlv.attributes['OpenAct'].value == expected_result @@ -401,8 +394,7 @@ def test_pos_open(): elif op_mode == 'aut': ana_vlv.set_open_aut(True) eval(f'ana_vlv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') if changes_expected: if 2 <= command <= 10: assert ana_vlv.get_pos() == command @@ -424,8 +416,7 @@ def test_pos_close(): elif op_mode == 'aut': ana_vlv.set_close_aut(True) eval(f'ana_vlv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') assert ana_vlv.get_pos() == 2 assert ana_vlv.get_pos_fbk() == 2 diff --git a/tests/test_bin_drv.py b/tests/test_bin_drv.py index 53a8f86..1060f3a 100644 --- a/tests/test_bin_drv.py +++ b/tests/test_bin_drv.py @@ -1,5 +1,5 @@ import pytest -from MTPPy_Async.src.mtppy.active_elements import BinDrv +from mtppy.active_elements import BinDrv def init_bin_drv(op_mode='off', src_mode='int', rev_fbk_calc=True, fwd_fbk_calc=True, @@ -59,8 +59,7 @@ def test_fwd_rev(): for command in [True, False]: bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True) eval(f'bin_drv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') if set_command in ['set_fwd_op', 'set_fwd_aut']: if command: assert bin_drv.attributes['FwdCtrl'].value == expected_result @@ -111,8 +110,7 @@ def test_fwd_rev_trip(): def test_fwd_rev_permit_en_true(): for op_mode, src_mode, set_command, _ in test_scenario: for command in [True, False]: - bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, - fwd_en=True, rev_en=True, perm_en=True) + bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, perm_en=True) bin_drv.set_permit(False) eval(f'bin_drv.{set_command}({command})') print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: False') @@ -125,12 +123,10 @@ def test_fwd_rev_permit_en_true(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, - fwd_en=True, rev_en=True, perm_en=True) + bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, perm_en=True) bin_drv.set_permit(True) eval(f'bin_drv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert bin_drv.attributes['Permit'].value == True assert bin_drv.attributes['SafePosAct'].value == False @@ -161,12 +157,10 @@ def test_fwd_rev_permit_en_true(): def test_fwd_rev_permit_en_false(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - ana_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, - fwd_en=True, rev_en=True, perm_en=False) + ana_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, perm_en=False) ana_drv.set_permit(True) eval(f'ana_drv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert ana_drv.attributes['Permit'].value == True assert ana_drv.attributes['SafePosAct'].value == False @@ -189,8 +183,7 @@ def test_fwd_rev_permit_en_false(): def test_fwd_rev_interlock_en_true(): for op_mode, src_mode, set_command, _ in test_scenario: for command in [True, False]: - bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, - fwd_en=True, rev_en=True, intl_en=True) + bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, intl_en=True) bin_drv.set_interlock(False) eval(f'bin_drv.{set_command}({command})') print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: False') @@ -202,12 +195,10 @@ def test_fwd_rev_interlock_en_true(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, - fwd_en=True, rev_en=True, intl_en=True) + bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, intl_en=True) bin_drv.set_interlock(True) eval(f'bin_drv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert bin_drv.attributes['Interlock'].value == True assert bin_drv.attributes['SafePosAct'].value == False @@ -230,12 +221,10 @@ def test_fwd_rev_interlock_en_true(): def test_fwd_rev_interlock_en_false(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, - fwd_en=True, rev_en=True, intl_en=False) + bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, intl_en=False) bin_drv.set_interlock(True) eval(f'bin_drv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert bin_drv.attributes['Interlock'].value == True assert bin_drv.attributes['SafePosAct'].value == False @@ -258,8 +247,7 @@ def test_fwd_rev_interlock_en_false(): def test_fwd_rev_protect_en_true(): for op_mode, src_mode, set_command, _ in test_scenario: for command in [True, False]: - bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, - fwd_en=True, rev_en=True, prot_en=True) + bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, prot_en=True) bin_drv.set_protect(False) eval(f'bin_drv.{set_command}({command})') print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: False') @@ -271,12 +259,10 @@ def test_fwd_rev_protect_en_true(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, - fwd_en=True, rev_en=True, prot_en=True) + bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, prot_en=True) bin_drv.set_protect(True) eval(f'bin_drv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert bin_drv.attributes['Protect'].value == True assert bin_drv.attributes['SafePosAct'].value == False @@ -299,12 +285,10 @@ def test_fwd_rev_protect_en_true(): def test_fwd_rev_protect_en_false(): for op_mode, src_mode, set_command, expected_result in test_scenario: for command in [True, False]: - bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, - fwd_en=True, rev_en=True, prot_en=False) + bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, prot_en=False) bin_drv.set_protect(True) eval(f'bin_drv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {expected_result}') assert bin_drv.attributes['Protect'].value == True assert bin_drv.attributes['SafePosAct'].value == False @@ -342,8 +326,7 @@ def test_fwd_rev_protect_en_false(): def test_reset(): for op_mode, src_mode, set_command, result in test_scenario_reset: for command in [True, False]: - bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, - fwd_en=True, rev_en=True, prot_en=True) + bin_drv = init_bin_drv(op_mode=op_mode, src_mode=src_mode, fwd_en=True, rev_en=True, prot_en=True) bin_drv.set_protect(False) assert bin_drv.attributes['Protect'].value == False @@ -380,8 +363,7 @@ def test_stop(): elif op_mode == 'aut': bin_drv.set_fwd_aut(True) - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {changes_expected}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, expected: {changes_expected}') if changes_expected: assert bin_drv.attributes['FwdCtrl'].value == True eval(f'bin_drv.{set_command}({command})') diff --git a/tests/test_bin_vlv.py b/tests/test_bin_vlv.py index 3c6782e..26c762a 100644 --- a/tests/test_bin_vlv.py +++ b/tests/test_bin_vlv.py @@ -1,5 +1,5 @@ import pytest -from MTPPy_Async.src.mtppy.active_elements import BinVlv +from mtppy.active_elements import BinVlv def init_bin_vlv(op_mode='off', src_mode='int', open_fbk_calc=True, close_fbk_calc=True, safe_pos=0, safe_pos_en=True, diff --git a/tests/test_mon_ana_drv.py b/tests/test_mon_ana_drv.py index be61f6d..d6724af 100644 --- a/tests/test_mon_ana_drv.py +++ b/tests/test_mon_ana_drv.py @@ -1,5 +1,5 @@ import pytest -from MTPPy_Async.src.mtppy.active_elements import MonAnaDrv +from mtppy.active_elements import MonAnaDrv import time @@ -56,30 +56,25 @@ def init_mon_ana_drv(op_mode='off', src_mode='int', rev_fbk_calc=True, fwd_fbk_c def test_static_error(): for op_mode, src_mode, set_command in test_scenario_no_control_signals: for command in [True, False]: - mon_ana_drv = init_mon_ana_drv( - op_mode=op_mode, src_mode=src_mode, rmp_ah_en=False, rmp_al_en=False) + mon_ana_drv = init_mon_ana_drv(op_mode=op_mode, src_mode=src_mode, rmp_ah_en=False, rmp_al_en=False) mon_ana_drv.start_monitor() eval(f'mon_ana_drv.{set_command}({command})') print(f'Scenario: mode {op_mode} {src_mode}, {set_command}') - # FwdFbk becomes to True without any control signals - mon_ana_drv.attributes['FwdFbk'].set_value(True) + mon_ana_drv.attributes['FwdFbk'].set_value(True) # FwdFbk becomes to True without any control signals time.sleep(0.6) assert mon_ana_drv.attributes['MonStatErr'].value == True assert mon_ana_drv.attributes['SafePosAct'].value == True - # safe position is clockwise rotation - assert mon_ana_drv.attributes['FwdFbk'].value == True + assert mon_ana_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation - # FwdFbk becomes to True without any control signals - mon_ana_drv.attributes['FwdFbk'].set_value(False) + mon_ana_drv.attributes['FwdFbk'].set_value(False) # FwdFbk becomes to True without any control signals time.sleep(0.6) assert mon_ana_drv.attributes['MonStatErr'].value == True assert mon_ana_drv.attributes['SafePosAct'].value == True - # safe position is clockwise rotation - assert mon_ana_drv.attributes['FwdFbk'].value == True + assert mon_ana_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation mon_ana_drv.set_stop_monitor() mon_ana_drv.monitor_static_thread.join() @@ -93,8 +88,7 @@ def test_static_error_within_monitor_time(): for op_mode, src_mode, set_command in test_scenario_no_control_signals: for command in [True, False]: - mon_ana_drv = init_mon_ana_drv( - op_mode=op_mode, src_mode=src_mode, rmp_ah_en=False, rmp_al_en=False) + mon_ana_drv = init_mon_ana_drv(op_mode=op_mode, src_mode=src_mode, rmp_ah_en=False, rmp_al_en=False) mon_ana_drv.start_monitor() eval(f'mon_ana_drv.{set_command}({command})') @@ -121,8 +115,7 @@ def test_static_error_within_monitor_time(): def test_dynamic_error_fwd_stop(): for op_mode, src_mode, fwd_command, stop_command in test_scenario_control_signals_fwd: - mon_ana_drv = init_mon_ana_drv( - op_mode=op_mode, src_mode=src_mode, rmp_ah_en=False, rmp_al_en=False) + mon_ana_drv = init_mon_ana_drv(op_mode=op_mode, src_mode=src_mode, rmp_ah_en=False, rmp_al_en=False) mon_ana_drv.start_monitor() eval(f'mon_ana_drv.{fwd_command}(True)') @@ -135,8 +128,7 @@ def test_dynamic_error_fwd_stop(): assert mon_ana_drv.attributes['MonDynErr'].value == True assert mon_ana_drv.attributes['MonStatErr'].value == False assert mon_ana_drv.attributes['SafePosAct'].value == True - # safe position is clockwise rotation - assert mon_ana_drv.attributes['FwdFbk'].value == True + assert mon_ana_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation eval(f'mon_ana_drv.{stop_command}(True)') print(f'Scenario: mode {op_mode} {src_mode}, {stop_command}') @@ -148,8 +140,7 @@ def test_dynamic_error_fwd_stop(): assert mon_ana_drv.attributes['MonDynErr'].value == True assert mon_ana_drv.attributes['MonStatErr'].value == False assert mon_ana_drv.attributes['SafePosAct'].value == True - # safe position is clockwise rotation - assert mon_ana_drv.attributes['FwdFbk'].value == True + assert mon_ana_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation mon_ana_drv.set_stop_monitor() mon_ana_drv.monitor_static_thread.join() @@ -166,8 +157,7 @@ def test_dynamic_error_fwd_stop(): def test_dynamic_error_rev_stop(): for op_mode, src_mode, rev_command, stop_command in test_scenario_control_signals_rev: - mon_ana_drv = init_mon_ana_drv( - op_mode=op_mode, src_mode=src_mode, rmp_ah_en=False, rmp_al_en=False) + mon_ana_drv = init_mon_ana_drv(op_mode=op_mode, src_mode=src_mode, rmp_ah_en=False, rmp_al_en=False) mon_ana_drv.start_monitor() eval(f'mon_ana_drv.{rev_command}(True)') @@ -179,8 +169,7 @@ def test_dynamic_error_rev_stop(): assert mon_ana_drv.attributes['MonDynErr'].value == True assert mon_ana_drv.attributes['MonStatErr'].value == False assert mon_ana_drv.attributes['SafePosAct'].value == True - # safe position is clockwise rotation - assert mon_ana_drv.attributes['FwdFbk'].value == True + assert mon_ana_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation eval(f'mon_ana_drv.{stop_command}(True)') print(f'Scenario: mode {op_mode} {src_mode}, {stop_command}') @@ -192,8 +181,7 @@ def test_dynamic_error_rev_stop(): assert mon_ana_drv.attributes['MonDynErr'].value == True assert mon_ana_drv.attributes['MonStatErr'].value == False assert mon_ana_drv.attributes['SafePosAct'].value == True - # safe position is clockwise rotation - assert mon_ana_drv.attributes['FwdFbk'].value == True + assert mon_ana_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation mon_ana_drv.set_stop_monitor() mon_ana_drv.monitor_static_thread.join() @@ -210,8 +198,7 @@ def test_dynamic_error_rev_stop(): def test_dynamic_error_reset(): for op_mode, src_mode, set_command in test_scenario_control_signals_reset: - mon_ana_drv = init_mon_ana_drv( - op_mode=op_mode, src_mode=src_mode, rmp_ah_en=False, rmp_al_en=False) + mon_ana_drv = init_mon_ana_drv(op_mode=op_mode, src_mode=src_mode, rmp_ah_en=False, rmp_al_en=False) mon_ana_drv.start_monitor() # set valve to open @@ -232,8 +219,7 @@ def test_dynamic_error_reset(): assert mon_ana_drv.attributes['MonDynErr'].value == True assert mon_ana_drv.attributes['MonStatErr'].value == False assert mon_ana_drv.attributes['SafePosAct'].value == True - # safe position is clockwise rotation - assert mon_ana_drv.attributes['FwdFbk'].value == True + assert mon_ana_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation mon_ana_drv.set_stop_monitor() mon_ana_drv.monitor_static_thread.join() @@ -256,10 +242,8 @@ def test_rpm_error(): time.sleep(0.5) eval(f'mon_ana_drv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') - # RpmFbk is 400, it does not reach desired rpm (500 or 700) - mon_ana_drv.attributes['RpmFbk'].set_value(400) + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') + mon_ana_drv.attributes['RpmFbk'].set_value(400) # RpmFbk is 400, it does not reach desired rpm (500 or 700) time.sleep(0.5) if -10 <= command <= 1000: if command == 500: @@ -270,8 +254,7 @@ def test_rpm_error(): assert mon_ana_drv.attributes['RpmErr'].value == -410 assert mon_ana_drv.attributes['SafePosAct'].value == True - # safe position is clockwise rotation - assert mon_ana_drv.attributes['FwdFbk'].value == True + assert mon_ana_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation mon_ana_drv.set_stop_monitor() mon_ana_drv.monitor_static_thread.join() @@ -287,8 +270,7 @@ def test_rpm_high_limit_alarm(): mon_ana_drv.start_monitor() time.sleep(0.5) eval(f'mon_ana_drv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') time.sleep(0.5) if -10 <= command <= 1000: @@ -311,8 +293,7 @@ def test_rpm_low_limit_alarm(): mon_ana_drv.start_monitor() time.sleep(0.5) eval(f'mon_ana_drv.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command} changes expected: {changes_expected}') time.sleep(0.5) assert mon_ana_drv.attributes['RpmALAct'].value == True diff --git a/tests/test_mon_ana_vlv.py b/tests/test_mon_ana_vlv.py index 8beebcc..25d591e 100644 --- a/tests/test_mon_ana_vlv.py +++ b/tests/test_mon_ana_vlv.py @@ -1,5 +1,5 @@ import pytest -from MTPPy_Async.src.mtppy.active_elements import MonAnaVlv +from mtppy.active_elements import MonAnaVlv import time @@ -67,8 +67,7 @@ def test_static_error(): assert mon_ana_vlv.attributes['MonStatErr'].value == True assert mon_ana_vlv.attributes['MonDynErr'].value == False assert mon_ana_vlv.attributes['SafePosAct'].value == True - # safe position of valve is open - assert mon_ana_vlv.attributes['OpenFbk'].value == True + assert mon_ana_vlv.attributes['OpenFbk'].value == True # safe position of valve is open time.sleep(0.5) @@ -79,8 +78,7 @@ def test_static_error(): assert mon_ana_vlv.attributes['MonStatErr'].value == True assert mon_ana_vlv.attributes['MonDynErr'].value == False assert mon_ana_vlv.attributes['SafePosAct'].value == True - # safe position of valve is open - assert mon_ana_vlv.attributes['OpenFbk'].value == True + assert mon_ana_vlv.attributes['OpenFbk'].value == True # safe position of valve is open mon_ana_vlv.set_stop_monitor() mon_ana_vlv.monitor_static_thread.join() @@ -169,8 +167,7 @@ def test_pos_open(): mon_ana_vlv.set_open_aut(True) eval(f'mon_ana_vlv.{set_command}({command})') print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, {command}') - # position tolerance is 1, pos_fbk is 2.5 --> pos error - mon_ana_vlv.attributes['PosFbk'].set_value(7.5) + mon_ana_vlv.attributes['PosFbk'].set_value(7.5) # position tolerance is 1, pos_fbk is 2.5 --> pos error time.sleep(1.5) # time for monitoring pos error is 1s if 2 <= command < 6 or 8 < command <= 10: # out of the pos tolerance range assert mon_ana_vlv.attributes['PosReachedFbk'].value == False diff --git a/tests/test_mon_bin_drv.py b/tests/test_mon_bin_drv.py index 79f3682..cf2120e 100644 --- a/tests/test_mon_bin_drv.py +++ b/tests/test_mon_bin_drv.py @@ -1,5 +1,5 @@ import pytest -from MTPPy_Async.src.mtppy.active_elements import MonBinDrv +from mtppy.active_elements import MonBinDrv import time @@ -59,23 +59,19 @@ def test_static_error(): eval(f'mon_bin_drv.{set_command}({command})') print(f'Scenario: mode {op_mode} {src_mode}, {set_command}') - # FwdFbk becomes to True without any control signals - mon_bin_drv.attributes['FwdFbk'].set_value(True) + mon_bin_drv.attributes['FwdFbk'].set_value(True) # FwdFbk becomes to True without any control signals time.sleep(0.6) assert mon_bin_drv.attributes['MonStatErr'].value == True assert mon_bin_drv.attributes['SafePosAct'].value == True - # safe position is clockwise rotation - assert mon_bin_drv.attributes['FwdFbk'].value == True + assert mon_bin_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation - # FwdFbk becomes to True without any control signals - mon_bin_drv.attributes['FwdFbk'].set_value(False) + mon_bin_drv.attributes['FwdFbk'].set_value(False) # FwdFbk becomes to True without any control signals time.sleep(0.6) assert mon_bin_drv.attributes['MonStatErr'].value == True assert mon_bin_drv.attributes['SafePosAct'].value == True - # safe position is clockwise rotation - assert mon_bin_drv.attributes['FwdFbk'].value == True + assert mon_bin_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation mon_bin_drv.set_stop_monitor() mon_bin_drv.monitor_static_thread.join() @@ -129,8 +125,7 @@ def test_dynamic_error_fwd_stop(): assert mon_bin_drv.attributes['MonDynErr'].value == True assert mon_bin_drv.attributes['MonStatErr'].value == False assert mon_bin_drv.attributes['SafePosAct'].value == True - # safe position is clockwise rotation - assert mon_bin_drv.attributes['FwdFbk'].value == True + assert mon_bin_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation eval(f'mon_bin_drv.{stop_command}(True)') print(f'Scenario: mode {op_mode} {src_mode}, {stop_command}') @@ -142,8 +137,7 @@ def test_dynamic_error_fwd_stop(): assert mon_bin_drv.attributes['MonDynErr'].value == True assert mon_bin_drv.attributes['MonStatErr'].value == False assert mon_bin_drv.attributes['SafePosAct'].value == True - # safe position is clockwise rotation - assert mon_bin_drv.attributes['FwdFbk'].value == True + assert mon_bin_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation mon_bin_drv.set_stop_monitor() mon_bin_drv.monitor_static_thread.join() @@ -171,8 +165,7 @@ def test_dynamic_error_rev_stop(): assert mon_bin_drv.attributes['MonDynErr'].value == True assert mon_bin_drv.attributes['MonStatErr'].value == False assert mon_bin_drv.attributes['SafePosAct'].value == True - # safe position is clockwise rotation - assert mon_bin_drv.attributes['FwdFbk'].value == True + assert mon_bin_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation eval(f'mon_bin_drv.{stop_command}(True)') print(f'Scenario: mode {op_mode} {src_mode}, {stop_command}') @@ -184,8 +177,7 @@ def test_dynamic_error_rev_stop(): assert mon_bin_drv.attributes['MonDynErr'].value == True assert mon_bin_drv.attributes['MonStatErr'].value == False assert mon_bin_drv.attributes['SafePosAct'].value == True - # safe position is clockwise rotation - assert mon_bin_drv.attributes['FwdFbk'].value == True + assert mon_bin_drv.attributes['FwdFbk'].value == True # safe position is clockwise rotation mon_bin_drv.set_stop_monitor() mon_bin_drv.monitor_static_thread.join() diff --git a/tests/test_mon_bin_vlv.py b/tests/test_mon_bin_vlv.py index 7a8c1ea..225198a 100644 --- a/tests/test_mon_bin_vlv.py +++ b/tests/test_mon_bin_vlv.py @@ -1,5 +1,5 @@ import pytest -from MTPPy_Async.src.mtppy.active_elements import MonBinVlv +from mtppy.active_elements import MonBinVlv import time @@ -65,8 +65,7 @@ def test_static_error(): assert mon_bin_vlv.attributes['MonStatErr'].value == True assert mon_bin_vlv.attributes['MonDynErr'].value == False assert mon_bin_vlv.attributes['SafePosAct'].value == True - # safe position of valve is open - assert mon_bin_vlv.attributes['OpenFbk'].value == True + assert mon_bin_vlv.attributes['OpenFbk'].value == True # safe position of valve is open time.sleep(0.5) @@ -77,8 +76,7 @@ def test_static_error(): assert mon_bin_vlv.attributes['MonStatErr'].value == True assert mon_bin_vlv.attributes['MonDynErr'].value == False assert mon_bin_vlv.attributes['SafePosAct'].value == True - # safe position of valve is open - assert mon_bin_vlv.attributes['OpenFbk'].value == True + assert mon_bin_vlv.attributes['OpenFbk'].value == True # safe position of valve is open mon_bin_vlv.set_stop_monitor() mon_bin_vlv.monitor_static_thread.join() diff --git a/tests/test_mtp_generator_instance_hierarchy_services.py b/tests/test_mtp_generator_instance_hierarchy_services.py index 1c00b47..f52b1db 100644 --- a/tests/test_mtp_generator_instance_hierarchy_services.py +++ b/tests/test_mtp_generator_instance_hierarchy_services.py @@ -4,12 +4,12 @@ added to InstanceHierarchy services """ import pytest -from MTPPy_Async.src.mtppy.mtp_generator import MTPGenerator -from MTPPy_Async.src.mtppy.service import Service -from MTPPy_Async.src.mtppy.procedure import Procedure -from MTPPy_Async.src.mtppy.operation_elements import * -from MTPPy_Async.src.mtppy.indicator_elements import * -from MTPPy_Async.src.mtppy.active_elements import * +from mtppy.mtp_generator import MTPGenerator +from mtppy.service import Service +from mtppy.procedure import Procedure +from mtppy.operation_elements import * +from mtppy.indicator_elements import * +from mtppy.active_elements import * # basic infos needed to initiate mtp_generator writer_info_dict = {'WriterName': 'tud/plt', 'WriterID': 'tud/plt', 'WriterVendor': 'tud', @@ -29,8 +29,7 @@ procedure_process_value_out = BinView('process_value_out') # test obj: procedure parameter -procedure_param = DIntServParam('proc_param_dint', v_min=-10, v_max=10, - v_scl_min=0, v_scl_max=-10, v_unit=23) +procedure_param = DIntServParam('proc_param_dint', v_min=-10, v_max=10, v_scl_min=0, v_scl_max=-10, v_unit=23) # test obj: procedure procedure = Procedure(0, 'cont', is_self_completing=False, is_default=True) @@ -39,8 +38,7 @@ procedure.add_procedure_parameter(procedure_param) # test obj: service config parameter -serv_parameters = AnaServParam('serv_param_ana', v_min=0, v_max=50, - v_scl_min=0, v_scl_max=10, v_unit=23) +serv_parameters = AnaServParam('serv_param_ana', v_min=0, v_max=50, v_scl_min=0, v_scl_max=10, v_unit=23) # test obj: service @@ -104,8 +102,7 @@ def resetting(self): class TestInstanceHierarchyServices(object): # test some functions of mtp generator def setup_class(self): - self.mtp_generator = MTPGenerator( - writer_info_dict, export_manifest_path, manifest_template_path) + self.mtp_generator = MTPGenerator(writer_info_dict, export_manifest_path, manifest_template_path) def test_add_service_to_InstanceHierarchy(self): """ @@ -114,29 +111,24 @@ def test_add_service_to_InstanceHierarchy(self): self.mtp_generator.create_components_for_services(service1, 'services') # test, if two InternalElements are added to InstanceHierarchy Services # the name of these two InternalElements should correspond the service name - service_name = self.mtp_generator.instance_hierarchy_service.find( - 'InternalElement').get('Name') + service_name = self.mtp_generator.instance_hierarchy_service.find('InternalElement').get('Name') assert service_name == 'service_test1' def test_add_components_to_service(self): - self.mtp_generator.create_components_for_services( - serv_parameters, 'configuration_parameters') + self.mtp_generator.create_components_for_services(serv_parameters, 'configuration_parameters') self.mtp_generator.create_components_for_services(procedure, 'procedures') # InternalElement service should only have two sub-InternalElements config param and procedure - service_sub_elements = self.mtp_generator.instance_hierarchy_service.find( - 'InternalElement') + service_sub_elements = self.mtp_generator.instance_hierarchy_service.find('InternalElement') assert len(service_sub_elements.findall('InternalElement')) == 2 def test_add_components_to_procedure(self): self.mtp_generator.create_components_for_services(procedure_param, 'procedure_parameters') self.mtp_generator.create_components_for_services(procedure_report_value, 'report_values') - self.mtp_generator.create_components_for_services( - procedure_process_value_out, 'process_value_outs') + self.mtp_generator.create_components_for_services(procedure_process_value_out, 'process_value_outs') # in this test case, a procedure should have one report value, one value out and one procedure parameter - procedure_ie = self.mtp_generator.instance_hierarchy_service.findall( - 'InternalElement/InternalElement')[1] + procedure_ie = self.mtp_generator.instance_hierarchy_service.findall('InternalElement/InternalElement')[1] assert len(procedure_ie.findall('InternalElement')) == 3 pro_parameters = procedure_ie.findall('InternalElement')[0] diff --git a/tests/test_mtp_generator_instance_list.py b/tests/test_mtp_generator_instance_list.py index 5406cee..b208ec0 100644 --- a/tests/test_mtp_generator_instance_list.py +++ b/tests/test_mtp_generator_instance_list.py @@ -3,12 +3,12 @@ With these functions, data assembly and it´s attributes can be added under InstanceList and SourceList/OPCServer """ import pytest -from MTPPy_Async.src.mtppy.mtp_generator import MTPGenerator -from MTPPy_Async.src.mtppy.service import Service -from MTPPy_Async.src.mtppy.procedure import Procedure -from MTPPy_Async.src.mtppy.operation_elements import * -from MTPPy_Async.src.mtppy.indicator_elements import * -from MTPPy_Async.src.mtppy.active_elements import * +from mtppy.mtp_generator import MTPGenerator +from mtppy.service import Service +from mtppy.procedure import Procedure +from mtppy.operation_elements import * +from mtppy.indicator_elements import * +from mtppy.active_elements import * import xml.etree.ElementTree as ET @@ -102,8 +102,7 @@ def resetting(self): class TestMTPInstanceList(object): # test some functions of mtp generator def setup_class(self): - self.mtp_generator = MTPGenerator( - writer_info_dict, export_manifest_path, manifest_template_path) + self.mtp_generator = MTPGenerator(writer_info_dict, export_manifest_path, manifest_template_path) self.mtp_generator.add_module_type_package('1.0', 'mtp_test', '') self.module_type_package = self.mtp_generator.module_type_package endpoint = '127.0.0.0' @@ -117,8 +116,7 @@ def test_add_instance_report_value(self): report_value = self.mtp_generator.instance_list.findall('InternalElement')[0] # whether type and name of the report value obj are correct - assert report_value.get( - 'RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/IndicatorElement/AnaView' + assert report_value.get('RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/IndicatorElement/AnaView' assert report_value.get('Name') == 'services.rand_num_gen.procedures.cont.report_values' # initiate InstanceList for other tests @@ -132,8 +130,7 @@ def test_add_instance_process_value_out(self): value_out = self.mtp_generator.instance_list.findall('InternalElement')[0] # whether type and name of the value out obj are correct - assert value_out.get( - 'RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/IndicatorElement/BinView' + assert value_out.get('RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/IndicatorElement/BinView' assert value_out.get('Name') == 'services.rand_num_gen.procedures.cont.process_value_outs' # initiate InstanceList for other tests @@ -152,8 +149,7 @@ def test_add_instance_procedure(self): node_id = 'ns=3;s=services.rand_num_gen.procedures' self.mtp_generator.create_instance(procedure, node_id) proc = self.mtp_generator.instance_list.findall('InternalElement')[0] - assert proc.get( - 'RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/DiagnosticElement/HealthStateView' + assert proc.get('RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/DiagnosticElement/HealthStateView' assert proc.get('Name') == 'services.rand_num_gen.procedures.HealthStateView' # add report value @@ -161,8 +157,7 @@ def test_add_instance_procedure(self): for report_values in procedure.report_values.values(): self.mtp_generator.create_instance(report_values, node_id_rv) rv = self.mtp_generator.instance_list.findall('InternalElement')[1] - assert rv.get( - 'RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/IndicatorElement/AnaView' + assert rv.get('RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/IndicatorElement/AnaView' assert rv.get('Name') == 'services.rand_num_gen.procedures.cont.report_values' # initiate InstanceList for other tests @@ -175,8 +170,7 @@ def test_add_instance_service(self): node_id = 'ns=3;s=services' self.mtp_generator.create_instance(service, node_id) serv = self.mtp_generator.instance_list.findall('InternalElement')[0] - assert serv.get( - 'RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/ServiceControl' + assert serv.get('RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/ServiceControl' assert serv.get('Name') == 'services.ServiceControl' # add config para @@ -197,8 +191,7 @@ def test_add_instance_active_element_plt(self): node_id = 'ns=3;s=active_elements.pid_ctrl' self.mtp_generator.create_instance(pid_ctrl, node_id) active_element = self.mtp_generator.instance_list.findall('InternalElement')[0] - assert active_element.get( - 'RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/ActiveElement/PIDCtrl' + assert active_element.get('RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/ActiveElement/PIDCtrl' assert active_element.get('Name') == 'active_elements.pid_ctrl' # initiate InstanceList for other tests @@ -209,8 +202,7 @@ def test_add_instance_active_element_analogue_drive(self): node_id = 'ns=3;s=active_elements.analogue_drive' self.mtp_generator.create_instance(analogue_drive, node_id) act = self.mtp_generator.instance_list.findall('InternalElement')[0] - assert act.get( - 'RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/ActiveElement/AnaDrv' + assert act.get('RefBaseSystemUnitPath') == 'MTPDataObjectSUCLib/DataAssembly/ActiveElement/AnaDrv' assert act.get('Name') == 'active_elements.analogue_drive' # initiate InstanceList for other tests @@ -241,8 +233,7 @@ def test_add_attr_to_instance(self): assert len(self.mtp_generator.opcua_server.findall(".//ExternalInterface")) == 28 # each ExternalInterface has three attributes - assert len(self.mtp_generator.opcua_server.findall( - ".//ExternalInterface")[0].findall('Attribute')) == 3 + assert len(self.mtp_generator.opcua_server.findall(".//ExternalInterface")[0].findall('Attribute')) == 3 # check, if ExternalInterface is connected to attribute of Instance linked_id_ei = self.mtp_generator.opcua_server.findall(".//ExternalInterface")[2].get('ID') diff --git a/tests/test_mtp_generator_structure.py b/tests/test_mtp_generator_structure.py index 2093fd4..a700230 100644 --- a/tests/test_mtp_generator_structure.py +++ b/tests/test_mtp_generator_structure.py @@ -2,7 +2,7 @@ test the basic manifest structure """ import pytest -from MTPPy_Async.src.mtppy.mtp_generator import MTPGenerator +from mtppy.mtp_generator import MTPGenerator # basic infos needed to initiate mtp_generator writer_info_dict = {'WriterName': 'tud/plt/shk', 'WriterID': 'tud/plt', 'WriterVendor': 'tud', @@ -18,8 +18,7 @@ def setup_class(self): """ initiate mtp generator and ModuleTypePackage """ - self.mtp_generator = MTPGenerator( - writer_info_dict, export_manifest_path, manifest_template_path) + self.mtp_generator = MTPGenerator(writer_info_dict, export_manifest_path, manifest_template_path) self.mtp_generator.add_module_type_package('1.0', 'mtp_test', '') self.module_type_package = self.mtp_generator.module_type_package @@ -72,8 +71,7 @@ def test_edit_writer_infos_error(self): # a KeyError should be raised with pytest.raises(KeyError): - mtp_generator = MTPGenerator( - writer_info_dict1, export_manifest_path, manifest_template_path) + mtp_generator = MTPGenerator(writer_info_dict1, export_manifest_path, manifest_template_path) mtp_generator.edit_writer_information() def test_add_supported_role_class(self): @@ -81,8 +79,7 @@ def test_add_supported_role_class(self): self.mtp_generator.apply_add_supported_role_class() for internal_element in self.mtp_generator.root.iter('InternalElement'): supported_role_class = internal_element.find('SupportedRoleClass') - assert supported_role_class.get( - 'RefRoleClassPath') == 'AutomationMLBaseRoleClassLib/AutomationMLBaseRole' + assert supported_role_class.get('RefRoleClassPath') == 'AutomationMLBaseRoleClassLib/AutomationMLBaseRole' def test_random_id_generator(self): random_id = self.mtp_generator.random_id_generator() diff --git a/tests/test_pid_ctrl.py b/tests/test_pid_ctrl.py index eb291e0..5d510d2 100644 --- a/tests/test_pid_ctrl.py +++ b/tests/test_pid_ctrl.py @@ -1,4 +1,4 @@ -from MTPPy_Async.src.mtppy.active_elements import PIDCtrl +from mtppy.active_elements import PIDCtrl from time import sleep @@ -48,8 +48,7 @@ def test_set_sp(): for command in [-500, -10, 500, 1000, 10000]: pid_ctrl = init_pid_ctrl(op_mode=op_mode, src_mode=src_mode) eval(f'pid_ctrl.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, changes expected: {changes_expected}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, changes expected: {changes_expected}') if changes_expected: if -10 <= command <= 1000: assert pid_ctrl.get_sp() == command @@ -71,8 +70,7 @@ def test_set_mv_man(): for command in [-500, -10, 500, 1000, 10000]: pid_ctrl = init_pid_ctrl(op_mode=op_mode, src_mode=src_mode) eval(f'pid_ctrl.{set_command}({command})') - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, changes expected: {changes_expected}') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, changes expected: {changes_expected}') if changes_expected: if -10 <= command <= 1000: assert pid_ctrl.get_mv() == command diff --git a/tests/test_procedure_control.py b/tests/test_procedure_control.py index 197dcce..9227e29 100644 --- a/tests/test_procedure_control.py +++ b/tests/test_procedure_control.py @@ -1,5 +1,5 @@ -from MTPPy_Async.src.mtppy.procedure_control import ProcedureControl -from MTPPy_Async.src.mtppy.procedure import Procedure +from mtppy.procedure_control import ProcedureControl +from mtppy.procedure import Procedure from test_operation_source_mode import init_op_source_mode @@ -9,8 +9,7 @@ def init_procedure_control(op_mode='off', src_mode='int'): 1: Procedure(1, 'second', is_default=True), 2: Procedure(2, 'third'), 3: Procedure(3, 'forth')} - procedure_control = ProcedureControl( - procedures=dummy_procedures, service_op_src_mode=op_src_mode) + procedure_control = ProcedureControl(procedures=dummy_procedures, service_op_src_mode=op_src_mode) return procedure_control @@ -45,28 +44,23 @@ def test_set_procedure_op(): def test_set_procedure_int(): procedure_control = init_procedure_control(op_mode='off', src_mode='ext') - check_valid_states(procedure_control, procedure_control.set_procedure_int, - change_allowed=False) + check_valid_states(procedure_control, procedure_control.set_procedure_int, change_allowed=False) check_invalid_states(procedure_control, procedure_control.set_procedure_int) procedure_control = init_procedure_control(op_mode='off', src_mode='int') - check_valid_states(procedure_control, procedure_control.set_procedure_int, - change_allowed=False) + check_valid_states(procedure_control, procedure_control.set_procedure_int, change_allowed=False) check_invalid_states(procedure_control, procedure_control.set_procedure_int) procedure_control = init_procedure_control(op_mode='op', src_mode='ext') - check_valid_states(procedure_control, procedure_control.set_procedure_int, - change_allowed=False) + check_valid_states(procedure_control, procedure_control.set_procedure_int, change_allowed=False) check_invalid_states(procedure_control, procedure_control.set_procedure_int) procedure_control = init_procedure_control(op_mode='op', src_mode='int') - check_valid_states(procedure_control, procedure_control.set_procedure_int, - change_allowed=False) + check_valid_states(procedure_control, procedure_control.set_procedure_int, change_allowed=False) check_invalid_states(procedure_control, procedure_control.set_procedure_int) procedure_control = init_procedure_control(op_mode='aut', src_mode='ext') - check_valid_states(procedure_control, procedure_control.set_procedure_int, - change_allowed=False) + check_valid_states(procedure_control, procedure_control.set_procedure_int, change_allowed=False) check_invalid_states(procedure_control, procedure_control.set_procedure_int) procedure_control = init_procedure_control(op_mode='aut', src_mode='int') @@ -76,23 +70,19 @@ def test_set_procedure_int(): def test_set_procedure_ext(): procedure_control = init_procedure_control(op_mode='off', src_mode='ext') - check_valid_states(procedure_control, procedure_control.set_procedure_ext, - change_allowed=False) + check_valid_states(procedure_control, procedure_control.set_procedure_ext, change_allowed=False) check_invalid_states(procedure_control, procedure_control.set_procedure_ext) procedure_control = init_procedure_control(op_mode='off', src_mode='int') - check_valid_states(procedure_control, procedure_control.set_procedure_ext, - change_allowed=False) + check_valid_states(procedure_control, procedure_control.set_procedure_ext, change_allowed=False) check_invalid_states(procedure_control, procedure_control.set_procedure_ext) procedure_control = init_procedure_control(op_mode='op', src_mode='ext') - check_valid_states(procedure_control, procedure_control.set_procedure_ext, - change_allowed=False) + check_valid_states(procedure_control, procedure_control.set_procedure_ext, change_allowed=False) check_invalid_states(procedure_control, procedure_control.set_procedure_ext) procedure_control = init_procedure_control(op_mode='op', src_mode='int') - check_valid_states(procedure_control, procedure_control.set_procedure_ext, - change_allowed=False) + check_valid_states(procedure_control, procedure_control.set_procedure_ext, change_allowed=False) check_invalid_states(procedure_control, procedure_control.set_procedure_ext) procedure_control = init_procedure_control(op_mode='aut', src_mode='ext') @@ -100,8 +90,7 @@ def test_set_procedure_ext(): check_invalid_states(procedure_control, procedure_control.set_procedure_ext) procedure_control = init_procedure_control(op_mode='aut', src_mode='int') - check_valid_states(procedure_control, procedure_control.set_procedure_ext, - change_allowed=False) + check_valid_states(procedure_control, procedure_control.set_procedure_ext, change_allowed=False) check_invalid_states(procedure_control, procedure_control.set_procedure_ext) @@ -117,3 +106,4 @@ def test_set_procedure_cur(): procedure_control.set_procedure_op(10) procedure_control.set_procedure_cur() assert procedure_control.attributes['ProcedureCur'].value == 0 + diff --git a/tests/test_state_machine.py b/tests/test_state_machine.py index adc0094..c9c09dd 100644 --- a/tests/test_state_machine.py +++ b/tests/test_state_machine.py @@ -1,7 +1,7 @@ from test_procedure_control import init_procedure_control -from MTPPy_Async.src.mtppy.state_machine import StateMachine -from MTPPy_Async.src.mtppy.state_codes import StateCodes -from MTPPy_Async.src.mtppy.command_codes import CommandCodes +from mtppy.state_machine import StateMachine +from mtppy.state_codes import StateCodes +from mtppy.command_codes import CommandCodes StateCodes = StateCodes() CommandCodes = CommandCodes() @@ -175,8 +175,7 @@ def test_set_command(): for op_mode, src_mode, set_command, change_allowed in test_scenario: for state in StateCodes.get_list_str(): for command in CommandCodes.get_list_int(): - state_machine, callback_object = init_state_machine( - state=state, op_mode=op_mode, src_mode=src_mode) + state_machine, callback_object = init_state_machine(state=state, op_mode=op_mode, src_mode=src_mode) eval(f'state_machine.{set_command}({command})') command_str = CommandCodes.int_code[command] # eval(f'state_machine.{command_str}()') @@ -185,8 +184,6 @@ def test_set_command(): else: expected_state = state final_state = state_machine.get_current_state_str() - print( - f'Scenario: mode {op_mode} {src_mode}, {set_command}, validity is {change_allowed},', end=' ') - print( - f'transition {state}->({command_str})->{final_state} (expected {expected_state})') + print(f'Scenario: mode {op_mode} {src_mode}, {set_command}, validity is {change_allowed},', end=' ') + print(f'transition {state}->({command_str})->{final_state} (expected {expected_state})') assert final_state == expected_state From 65406be8d7ab11fd67477ffcc1915f13f30dea4e Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Sat, 2 Aug 2025 17:55:27 +0200 Subject: [PATCH 17/61] bugfix for disabling commands --- .gitignore | 3 ++- src/mtppy/service.py | 1 + src/mtppy/state_machine.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 9683059..b1113da 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /dist/ __pycache__/ -~$* \ No newline at end of file +~$* +MTPPy.egg-info/ \ No newline at end of file diff --git a/src/mtppy/service.py b/src/mtppy/service.py index de776ad..9ef321c 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -68,6 +68,7 @@ def __init__(self, tag_name: str, tag_description: str): state_change_function=self.state_change()) self.op_src_mode.add_enter_offline_callback(self.state_machine.command_en_ctrl.disable_all) + self.op_src_mode.add_enter_offline_callback(self.state_machine.update_command_en) self.op_src_mode.add_enter_offline_callback(self.thread_ctrl.stop_thread) self.op_src_mode.add_exit_offline_callback(self.state_machine.command_en_ctrl.set_default) diff --git a/src/mtppy/state_machine.py b/src/mtppy/state_machine.py index 858c508..144a313 100644 --- a/src/mtppy/state_machine.py +++ b/src/mtppy/state_machine.py @@ -50,7 +50,7 @@ def __init__(self, operation_source_mode: OperationSourceMode, ) self.op_src_mode.add_exit_operator_callback( # removes function to disables commands if no procedure is set - self.procedure_control.attributes['ProcedureReq'].remove_subscription_callback() + self.procedure_control.attributes['ProcedureReq'].remove_subscription_callback ) def set_command_op(self, value: int): From d108e5bceb79f2cd910da33b5ab730490fc34344 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Sat, 2 Aug 2025 19:53:15 +0200 Subject: [PATCH 18/61] disable restart if no procedure is selected --- src/mtppy/command_en_control.py | 60 ++++++++++++++++----------------- src/mtppy/service.py | 17 ++++++++++ src/mtppy/state_machine.py | 29 +++++++--------- 3 files changed, 59 insertions(+), 47 deletions(-) diff --git a/src/mtppy/command_en_control.py b/src/mtppy/command_en_control.py index fa553d6..6d680c2 100644 --- a/src/mtppy/command_en_control.py +++ b/src/mtppy/command_en_control.py @@ -24,6 +24,7 @@ def __init__(self): self.hold_enabled = True self.pause_enabled = True self.restart_enabled = True + self.restart_enabled_actual = True def set_default(self): """ @@ -104,6 +105,20 @@ def enable_restart(self, value: bool): value (bool): True if the restart command is to active and False if to disable. """ self.restart_enabled = value + self.restart_enabled_actual = value + + def disable_restart_temporarily(self): + """ + Disables restart command without overriding the actual setting. + """ + self.restart_enabled = False + self.set_command_en('restart', False) + + def restore_restart(self): + """ + Restores the restart command to the actual setting. + """ + self.restart_enabled = self.restart_enabled_actual def execute(self, state: str): """ @@ -132,8 +147,7 @@ def _execute_idle(self): self.set_command_en('pause', False) self.set_command_en('resume', False) self.set_command_en('abort', True) - if self.restart_enabled: - self.set_command_en('restart', False) + self.set_command_en('restart', False) self.set_command_en('complete', False) def _execute_starting(self): @@ -147,8 +161,7 @@ def _execute_starting(self): self.set_command_en('pause', False) self.set_command_en('resume', False) self.set_command_en('abort', True) - if self.restart_enabled: - self.set_command_en('restart', False) + self.set_command_en('restart', False) self.set_command_en('complete', True) def _execute_execute(self): @@ -177,8 +190,7 @@ def _execute_completing(self): self.set_command_en('pause', False) self.set_command_en('resume', False) self.set_command_en('abort', True) - if self.restart_enabled: - self.set_command_en('restart', False) + self.set_command_en('restart', False) self.set_command_en('complete', False) def _execute_completed(self): @@ -192,8 +204,7 @@ def _execute_completed(self): self.set_command_en('pause', False) self.set_command_en('resume', False) self.set_command_en('abort', True) - if self.restart_enabled: - self.set_command_en('restart', False) + self.set_command_en('restart', False) self.set_command_en('complete', False) def _execute_resuming(self): @@ -207,8 +218,7 @@ def _execute_resuming(self): self.set_command_en('pause', False) self.set_command_en('resume', False) self.set_command_en('abort', True) - if self.restart_enabled: - self.set_command_en('restart', False) + self.set_command_en('restart', False) self.set_command_en('complete', True) def _execute_paused(self): @@ -222,8 +232,7 @@ def _execute_paused(self): self.set_command_en('pause', False) self.set_command_en('resume', True) self.set_command_en('abort', True) - if self.restart_enabled: - self.set_command_en('restart', False) + self.set_command_en('restart', False) self.set_command_en('complete', True) def _execute_pausing(self): @@ -237,8 +246,7 @@ def _execute_pausing(self): self.set_command_en('pause', False) self.set_command_en('resume', False) self.set_command_en('abort', True) - if self.restart_enabled: - self.set_command_en('restart', False) + self.set_command_en('restart', False) self.set_command_en('complete', True) def _execute_holding(self): @@ -252,8 +260,7 @@ def _execute_holding(self): self.set_command_en('pause', False) self.set_command_en('resume', False) self.set_command_en('abort', True) - if self.restart_enabled: - self.set_command_en('restart', False) + self.set_command_en('restart', False) self.set_command_en('complete', False) def _execute_held(self): @@ -267,8 +274,7 @@ def _execute_held(self): self.set_command_en('pause', False) self.set_command_en('resume', False) self.set_command_en('abort', True) - if self.restart_enabled: - self.set_command_en('restart', False) + self.set_command_en('restart', False) self.set_command_en('complete', False) def _execute_unholding(self): @@ -282,8 +288,7 @@ def _execute_unholding(self): self.set_command_en('pause', False) self.set_command_en('resume', False) self.set_command_en('abort', True) - if self.restart_enabled: - self.set_command_en('restart', False) + self.set_command_en('restart', False) self.set_command_en('complete', True) def _execute_stopping(self): @@ -297,8 +302,7 @@ def _execute_stopping(self): self.set_command_en('pause', False) self.set_command_en('resume', False) self.set_command_en('abort', True) - if self.restart_enabled: - self.set_command_en('restart', False) + self.set_command_en('restart', False) self.set_command_en('complete', False) def _execute_stopped(self): @@ -312,8 +316,7 @@ def _execute_stopped(self): self.set_command_en('pause', False) self.set_command_en('resume', False) self.set_command_en('abort', True) - if self.restart_enabled: - self.set_command_en('restart', False) + self.set_command_en('restart', False) self.set_command_en('complete', False) def _execute_aborting(self): @@ -327,8 +330,7 @@ def _execute_aborting(self): self.set_command_en('pause', False) self.set_command_en('resume', False) self.set_command_en('abort', False) - if self.restart_enabled: - self.set_command_en('restart', False) + self.set_command_en('restart', False) self.set_command_en('complete', False) def _execute_aborted(self): @@ -342,8 +344,7 @@ def _execute_aborted(self): self.set_command_en('pause', False) self.set_command_en('resume', False) self.set_command_en('abort', False) - if self.restart_enabled: - self.set_command_en('restart', False) + self.set_command_en('restart', False) self.set_command_en('complete', False) def _execute_resetting(self): @@ -357,6 +358,5 @@ def _execute_resetting(self): self.set_command_en('pause', False) self.set_command_en('resume', False) self.set_command_en('abort', True) - if self.restart_enabled: - self.set_command_en('restart', False) + self.set_command_en('restart', False) self.set_command_en('complete', False) diff --git a/src/mtppy/service.py b/src/mtppy/service.py index 9ef321c..f1a2f71 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -76,6 +76,23 @@ def __init__(self, tag_name: str, tag_description: str): self.op_src_mode.add_exit_offline_callback(self.apply_configuration_parameters) self.op_src_mode.add_exit_offline_callback(self.init_idle_state) + self.op_src_mode.add_enter_operator_callback( + # adds function to disable commands if no procedure is set + # lambda allows adding function with argument + lambda: self.procedure_control.attributes['ProcedureReq'].attach_subscription_callback( + self.state_machine.disable_commands_if_no_procedure) + ) + self.op_src_mode.add_enter_operator_callback( + lambda: self.state_machine.disable_commands_if_no_procedure( + self.procedure_control.attributes['ProcedureReq'].value + ) + ) + + self.op_src_mode.add_exit_operator_callback( + # removes function to disables commands if no procedure is set + self.procedure_control.attributes['ProcedureReq'].remove_subscription_callback + ) + def init_idle_state(self): """ Initializes the idle state. diff --git a/src/mtppy/state_machine.py b/src/mtppy/state_machine.py index 144a313..a7a8804 100644 --- a/src/mtppy/state_machine.py +++ b/src/mtppy/state_machine.py @@ -42,16 +42,6 @@ def __init__(self, operation_source_mode: OperationSourceMode, self.act_state = StateCodes.idle self.prev_state = StateCodes.idle - self.op_src_mode.add_enter_operator_callback( - # adds function to disable commands if no procedure is set - # lambda allows adding function with argument - lambda: self.procedure_control.attributes['ProcedureReq'].attach_subscription_callback( - self.disable_commands_if_no_procedure) - ) - self.op_src_mode.add_exit_operator_callback( - # removes function to disables commands if no procedure is set - self.procedure_control.attributes['ProcedureReq'].remove_subscription_callback - ) def set_command_op(self, value: int): if self.op_src_mode.attributes['StateOpAct'].value: @@ -91,8 +81,6 @@ def command_execution(self, com_var: int): def start(self): if self.command_en_ctrl.is_enabled('start'): - # removes the function to disables commands if no procedure is set - self.procedure_control.attributes['ProcedureReq'].remove_subscription_callback() self.procedure_control.set_procedure_cur() self.procedure_control.attributes['ProcedureOp'].set_value(0) @@ -103,6 +91,11 @@ def start(self): def restart(self): if self.command_en_ctrl.is_enabled('restart'): + self.procedure_control.set_procedure_cur() + self.procedure_control.attributes['ProcedureOp'].set_value(0) + self.procedure_control.attributes['ProcedureInt'].set_value(0) + self.procedure_control.attributes['ProcedureExt'].set_value(0) + self.procedure_control.apply_procedure_parameters() self._change_state_to(StateCodes.starting) def complete(self): @@ -120,9 +113,6 @@ def resume(self): def reset(self): if self.command_en_ctrl.is_enabled('reset'): self._change_state_to(StateCodes.resetting) - # adds function to disable commands if no procedure is set - self.procedure_control.attributes['ProcedureReq'].attach_subscription_callback( - self.disable_commands_if_no_procedure) def hold(self): if self.command_en_ctrl.is_enabled('hold'): @@ -195,8 +185,13 @@ def disable_commands_if_no_procedure(self, value: int): Args: value (int): Value of the ProcedureReq attribute. """ - if self.act_state is StateCodes.idle and value == 0: - self.command_en_ctrl.disable_all() + if value == 0: + self.command_en_ctrl.disable_restart_temporarily() + + if self.act_state is StateCodes.idle: + self.command_en_ctrl.disable_all() else: + self.command_en_ctrl.restore_restart() + self.command_en_ctrl.execute(self.get_current_state_str()) self.update_command_en() From f8e39417c6b9b98009b37a484195965ab2e24748 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Sat, 9 Aug 2025 10:56:28 +0200 Subject: [PATCH 19/61] add pause and hold enable, set default to False --- src/mtppy/command_en_control.py | 10 +++++----- src/mtppy/service.py | 27 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/mtppy/command_en_control.py b/src/mtppy/command_en_control.py index 6d680c2..805e450 100644 --- a/src/mtppy/command_en_control.py +++ b/src/mtppy/command_en_control.py @@ -21,10 +21,10 @@ def __init__(self): 'restart': {'default': False, 'value': False, 'bit_no': 9, 'int': 512}, 'complete': {'default': False, 'value': False, 'bit_no': 10, 'int': 1024}, } - self.hold_enabled = True - self.pause_enabled = True + self.hold_enabled = False + self.pause_enabled = False self.restart_enabled = True - self.restart_enabled_actual = True + self.__restart_enabled_actual = True def set_default(self): """ @@ -105,7 +105,7 @@ def enable_restart(self, value: bool): value (bool): True if the restart command is to active and False if to disable. """ self.restart_enabled = value - self.restart_enabled_actual = value + self.__restart_enabled_actual = value def disable_restart_temporarily(self): """ @@ -118,7 +118,7 @@ def restore_restart(self): """ Restores the restart command to the actual setting. """ - self.restart_enabled = self.restart_enabled_actual + self.restart_enabled = self.__restart_enabled_actual def execute(self, state: str): """ diff --git a/src/mtppy/service.py b/src/mtppy/service.py index f1a2f71..93522c7 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -93,6 +93,33 @@ def __init__(self, tag_name: str, tag_description: str): self.procedure_control.attributes['ProcedureReq'].remove_subscription_callback ) + def enable_pause_loop(self, enable: bool = True): + """ + Enables or disables the pause loop. + + Args: + enable (bool): If True, enables the pause loop. If False, disables it. + """ + self.state_machine.command_en_ctrl.enable_pause_loop(enable) + + def enable_restart(self, enable: bool = True): + """ + Enables or disables the restart command. + + Args: + enable (bool): If True, enables the restart command. If False, disables it. + """ + self.state_machine.command_en_ctrl.enable_restart(enable) + + def enable_restart(self, enable: bool = True): + """ + Enables or disables the restart command. + + Args: + enable (bool): If True, enables the restart command. If False, disables it. + """ + self.state_machine.command_en_ctrl.enable_restart(enable) + def init_idle_state(self): """ Initializes the idle state. From e8e05f34e041abfbe17b4307173e375afdab1559 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Sun, 10 Aug 2025 12:10:32 +0200 Subject: [PATCH 20/61] moved from exception event to optional exception callback --- src/mtppy/service.py | 9 ++++++--- src/mtppy/thread_control.py | 14 ++++++++------ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/mtppy/service.py b/src/mtppy/service.py index 93522c7..a673a2f 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -1,6 +1,7 @@ import logging import time from threading import Event +from collections.abc import Callable from abc import abstractmethod @@ -43,13 +44,15 @@ class Service(SUCServiceControl): """ - def __init__(self, tag_name: str, tag_description: str): + def __init__(self, tag_name: str, tag_description: str, exception_callback: Callable[[Exception], None] = None): """ Represents a service of the PEA. Args: tag_name (str): Tag name of the service. tag_description (str): Tag description of the service. + exception_callback (Callable[[Exception], None]): Function to call + when an exception occurs in the thread. """ super().__init__(tag_name, tag_description) @@ -65,7 +68,8 @@ def __init__(self, tag_name: str, tag_description: str): execution_routine=self.state_change_callback) self.thread_ctrl = ThreadControl(service_name=tag_name, - state_change_function=self.state_change()) + state_change_function=self.state_change(), + exception_callback=exception_callback) self.op_src_mode.add_enter_offline_callback(self.state_machine.command_en_ctrl.disable_all) self.op_src_mode.add_enter_offline_callback(self.state_machine.update_command_en) @@ -368,5 +372,4 @@ def resetting(self): else: pass # Reset the state machine to idle - self.thread_ctrl.exception = None self.state_change() diff --git a/src/mtppy/thread_control.py b/src/mtppy/thread_control.py index 6957518..c2498fc 100644 --- a/src/mtppy/thread_control.py +++ b/src/mtppy/thread_control.py @@ -19,13 +19,16 @@ def stop(self): class ThreadControl: - def __init__(self, service_name: str = '', state_change_function: Callable = None): + def __init__(self, service_name: str = '', + state_change_function: Callable = None, + exception_callback: Callable[[Exception], None] = None): """ Represents a thread control to be able to run with multithreading. Args: service_name (str): Name of the service. state_change_function (Callable): Function to call after a state completes. + exception_callback (Callable): Function to call when an exception occurs in the thread. """ self.service_name = service_name self.state_change_function = state_change_function @@ -33,8 +36,7 @@ def __init__(self, service_name: str = '', state_change_function: Callable = Non self.running_state = '' self.requested_state = '' self.callback_function: Callable = None - self.exception_event = Event() - self.exception: Exception = None + self.exception_callback: Callable[[Exception], None] = exception_callback def request_state(self, state: str, cb_function: Callable): """ @@ -63,7 +65,7 @@ def reallocate_running_thread(self): def run_thread(self, target_function: Callable): """ - Runs the given target function. Sets the exception if it occurs. + Runs the given target function. If an exception occurs the exception callback is called. Args: target_function (Callable): The function to run in the thread. @@ -79,8 +81,8 @@ def run_thread(self, target_function: Callable): _logger.debug("Stop event was set, stopping thread execution.") except Exception as e: - self.exception = e - self.exception_event.set() + self.exception_callback(e) if self.exception_callback else _logger.error( + f"Exception in thread {self.thread.name}: {e}", exc_info=True) def stop_thread(self, stop_if_current_thread: bool = True): """ From 0fd73adbcfbb0d127d5588f1cb324a249d814c94 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Mon, 11 Aug 2025 11:14:42 +0200 Subject: [PATCH 21/61] fixed checking of attributes added .value where it was missing --- src/mtppy/operation_elements.py | 26 ++++++++++++++------------ src/mtppy/operation_source_mode.py | 12 ++++++------ 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/mtppy/operation_elements.py b/src/mtppy/operation_elements.py index a4dbadf..a7fbad0 100644 --- a/src/mtppy/operation_elements.py +++ b/src/mtppy/operation_elements.py @@ -38,17 +38,17 @@ def __init__(self, tag_name: str, tag_description: str = '', v_min: float = 0, v def set_v_op(self, value): _logger.debug('VOp set to %s' % value) - if self.op_src_mode.attributes['StateOpAct']: + if self.op_src_mode.attributes['StateOpAct'].value: self.set_v_req(value) def set_v_int(self, value): _logger.debug('VInt set to %s' % value) - if self.op_src_mode.attributes['StateAutAct'] and self.op_src_mode.attributes['SrcIntAct']: + if self.op_src_mode.attributes['StateAutAct'].value and self.op_src_mode.attributes['SrcIntAct'].value: self.set_v_req(value) def set_v_ext(self, value): _logger.debug('VExt set to %s' % value) - if self.op_src_mode.attributes['StateAutAct'] and self.op_src_mode.attributes['SrcExtAct']: + if self.op_src_mode.attributes['StateAutAct'].value and self.op_src_mode.attributes['SrcExtAct'].value: self.set_v_req(value) def valid_value(self, value): @@ -102,17 +102,17 @@ def __init__(self, tag_name: str, tag_description: str = '', v_state_0: str = 'f def set_v_op(self, value): _logger.debug('VOp set to %s' % value) - if self.op_src_mode.attributes['StateOpAct']: + if self.op_src_mode.attributes['StateOpAct'].value: self.set_v_req(value) def set_v_int(self, value): _logger.debug('VInt set to %s' % value) - if self.op_src_mode.attributes['StateAutAct'] and self.op_src_mode.attributes['SrcIntAct']: + if self.op_src_mode.attributes['StateAutAct'].value and self.op_src_mode.attributes['SrcIntAct'].value: self.set_v_req(value) def set_v_ext(self, value): _logger.debug('VExt set to %s' % value) - if self.op_src_mode.attributes['StateAutAct'] and self.op_src_mode.attributes['SrcExtAct']: + if self.op_src_mode.attributes['StateAutAct'].value and self.op_src_mode.attributes['SrcExtAct'].value: self.set_v_req(value) def set_v_req(self, value): @@ -164,17 +164,19 @@ def __init__(self, tag_name: str, tag_description: str = '', v_min: int = 0, v_m def set_v_op(self, value): _logger.debug('VOp set to %s' % value) - if self.op_src_mode.attributes['StateOpAct']: + if self.op_src_mode.attributes['StateOpAct'].value: self.set_v_req(value) def set_v_int(self, value): _logger.debug('VInt set to %s' % value) - if self.op_src_mode.attributes['StateAutAct'] and self.op_src_mode.attributes['SrcIntAct']: + if (self.op_src_mode.attributes['StateAutAct'].value + and self.op_src_mode.attributes['SrcIntAct'].value): self.set_v_req(value) def set_v_ext(self, value): _logger.debug('VExt set to %s' % value) - if self.op_src_mode.attributes['StateAutAct'] and self.op_src_mode.attributes['SrcExtAct']: + if (self.op_src_mode.attributes['StateAutAct'].value + and self.op_src_mode.attributes['SrcExtAct'].value): self.set_v_req(value) def valid_value(self, value): @@ -223,17 +225,17 @@ def __init__(self, tag_name: str, tag_description: str = ''): def set_v_op(self, value): _logger.debug('VOp set to %s' % value) - if self.op_src_mode.attributes['StateOpAct']: + if self.op_src_mode.attributes['StateOpAct'].value: self.set_v_req(value) def set_v_int(self, value): _logger.debug('VInt set to %s' % value) - if self.op_src_mode.attributes['StateAutAct'] and self.op_src_mode.attributes['SrcIntAct']: + if self.op_src_mode.attributes['StateAutAct'].value and self.op_src_mode.attributes['SrcIntAct'].value: self.set_v_req(value) def set_v_ext(self, value): _logger.debug('VExt set to %s' % value) - if self.op_src_mode.attributes['StateAutAct'] and self.op_src_mode.attributes['SrcExtAct']: + if self.op_src_mode.attributes['StateAutAct'].value and self.op_src_mode.attributes['SrcExtAct'].value: self.set_v_req(value) def valid_value(self, value): diff --git a/src/mtppy/operation_source_mode.py b/src/mtppy/operation_source_mode.py index aec133a..887eb60 100644 --- a/src/mtppy/operation_source_mode.py +++ b/src/mtppy/operation_source_mode.py @@ -151,39 +151,39 @@ def set_state_channel(self, value: bool): def set_state_aut_aut(self, value: bool): _logger.debug(f'StateAutAut set to {value}') if self.attributes['StateChannel'].value and value: - if self.attributes['StateOffAct'] or self.attributes['StateOpAct']: + if self.attributes['StateOffAct'].value or self.attributes['StateOpAct'].value: self._opmode_to_aut() def set_state_aut_op(self, value: bool): _logger.debug(f'StateAutOp set to {value}') if not self.attributes['StateChannel'].value and value: - if self.attributes['StateOffAct'] or self.attributes['StateOpAct']: + if self.attributes['StateOffAct'].value or self.attributes['StateOpAct'].value: self._opmode_to_aut() self.attributes['StateAutOp'].set_value(False) def set_state_off_aut(self, value: bool): _logger.debug(f'StateOffAut set to {value}') if self.attributes['StateChannel'].value and value and self.switch_to_offline_mode_allowed: - if self.attributes['StateAutAct'] or self.attributes['StateOpAct']: + if self.attributes['StateAutAct'].value or self.attributes['StateOpAct'].value: self._opmode_to_off() def set_state_off_op(self, value: bool): _logger.debug(f'StateOffOp set to {value}') if not self.attributes['StateChannel'].value and value and self.switch_to_offline_mode_allowed: - if self.attributes['StateAutAct'] or self.attributes['StateOpAct']: + if self.attributes['StateAutAct'].value or self.attributes['StateOpAct'].value: self._opmode_to_off() self.attributes['StateOffOp'].set_value(False) def set_state_op_aut(self, value: bool): _logger.debug(f'StateOpAut set to {value}') if self.attributes['StateChannel'].value and value: - if self.attributes['StateOffAct'] or self.attributes['StateOpAct']: + if self.attributes['StateOffAct'].value or self.attributes['StateOpAct'].value: self._opmode_to_op() def set_state_op_op(self, value: bool): _logger.debug(f'StateOpOp set to {value}') if not self.attributes['StateChannel'].value and value: - if self.attributes['StateOffAct'] or self.attributes['StateAutAct']: + if self.attributes['StateOffAct'].value or self.attributes['StateAutAct'].value: self._opmode_to_op() self.attributes['StateOpOp'].set_value(False) From 6f72e215d1dce370e43901805f7e74c564a5443e Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Mon, 11 Aug 2025 12:37:06 +0200 Subject: [PATCH 22/61] added type annotations, added abstract methods to parent classes --- src/mtppy/procedure.py | 6 ++-- src/mtppy/service.py | 2 +- src/mtppy/suc_data_assembly.py | 64 ++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/src/mtppy/procedure.py b/src/mtppy/procedure.py index e3c14f4..c97e5bf 100644 --- a/src/mtppy/procedure.py +++ b/src/mtppy/procedure.py @@ -21,10 +21,10 @@ def __init__(self, procedure_id: int, tag_name: str, tag_description: str = '', if procedure_id <= 0: raise ValueError(f"{tag_name}: Procedure ID can't be equal or less than 0.") super().__init__(procedure_id, tag_name, tag_description, is_self_completing, is_default) - self.procedure_parameters = {} + self.procedure_parameters: dict[str, SUCOperationElement] = {} self.process_value_ins = {} - self.report_values = {} - self.process_value_outs = {} + self.report_values: dict[str, SUCIndicatorElement] = {} + self.process_value_outs: dict[str, SUCIndicatorElement] = {} def add_procedure_parameter(self, procedure_parameter: SUCOperationElement): """ diff --git a/src/mtppy/service.py b/src/mtppy/service.py index a673a2f..d895af7 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -60,7 +60,7 @@ def __init__(self, tag_name: str, tag_description: str, exception_callback: Call self.configuration_parameters = {} - self.procedures = {} + self.procedures: dict[int, Procedure] = {} self.procedure_control = ProcedureControl(self.procedures, self.op_src_mode) self.state_machine = StateMachine(operation_source_mode=self.op_src_mode, diff --git a/src/mtppy/suc_data_assembly.py b/src/mtppy/suc_data_assembly.py index a077ed3..d5774fa 100644 --- a/src/mtppy/suc_data_assembly.py +++ b/src/mtppy/suc_data_assembly.py @@ -1,4 +1,5 @@ from mtppy.attribute import Attribute +from abc import abstractmethod class SUCDataAssembly: @@ -20,6 +21,13 @@ def __init__(self, tag_name: str, tag_description: str): self._add_attribute(Attribute('OSLevel', int, init_value=0)) self._add_attribute(Attribute('WQC', int, init_value=255)) + @abstractmethod + def set_v(self, value): + """ + Set the value of the 'V' attribute. + """ + pass + class SUCOperationElement(SUCDataAssembly): def __init__(self, tag_name: str, tag_description: str): @@ -27,6 +35,62 @@ def __init__(self, tag_name: str, tag_description: str): self._add_attribute(Attribute('OSLevel', int, init_value=0)) self._add_attribute(Attribute('WQC', int, init_value=255)) + @abstractmethod + def set_v_op(self, value): + """ + Set Parameter value if StateOpAct. + """ + pass + + @abstractmethod + def set_v_int(self, value): + """ + Set Parameter value if StateIntAct. + """ + pass + + @abstractmethod + def set_v_ext(self, value): + """ + Set Parameter value if StateExAct. + """ + pass + + @abstractmethod + def valid_value(self, value): + """ + Validate if the value is within the acceptable range. + """ + pass + + @abstractmethod + def set_v_req(self, value): + """ + Set VReq (requested parameter value) to the specified value if valid. + """ + pass + + @abstractmethod + def set_v_out(self): + """ + Set VOut (current parameter value) based on VReq and update feedback. + """ + pass + + @abstractmethod + def get_v_out(self): + """ + Get the current value of VOut (current parameter value). + """ + pass + + @abstractmethod + def set_v_fbk(self, value): + """ + Set VFbk (feedback value) to the specified value. + """ + pass + class SUCActiveElement(SUCDataAssembly): def __init__(self, tag_name: str, tag_description: str): From f2c89c9e967c598f0aab3748add2796d7f1a3ac0 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Mon, 11 Aug 2025 12:37:43 +0200 Subject: [PATCH 23/61] fix bug where idle thread would not restart after entering offline --- src/mtppy/thread_control.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mtppy/thread_control.py b/src/mtppy/thread_control.py index c2498fc..9ef2c34 100644 --- a/src/mtppy/thread_control.py +++ b/src/mtppy/thread_control.py @@ -54,8 +54,10 @@ def reallocate_running_thread(self): """ Reallocates the running thread to the requested state. """ - _logger.debug(f'Reallocate thread to state {self.requested_state}') - if self.requested_state is not self.running_state: + if (self.requested_state is not self.running_state + or (self.running_state == "idle" and + (self.thread is None or not self.thread.is_alive()))): + _logger.debug(f'Reallocate thread to state {self.requested_state}') self.stop_thread(stop_if_current_thread=False) self.thread = StoppableThread(target=self.run_thread, args=(self.callback_function,), From b85b41da25fab49ac793b6157c0a4c9148826557 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Tue, 12 Aug 2025 14:56:04 +0200 Subject: [PATCH 24/61] fix: ensure commands are disabled when no procedure is set --- src/mtppy/state_machine.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mtppy/state_machine.py b/src/mtppy/state_machine.py index a7a8804..b6a0a42 100644 --- a/src/mtppy/state_machine.py +++ b/src/mtppy/state_machine.py @@ -42,7 +42,6 @@ def __init__(self, operation_source_mode: OperationSourceMode, self.act_state = StateCodes.idle self.prev_state = StateCodes.idle - def set_command_op(self, value: int): if self.op_src_mode.attributes['StateOpAct'].value: self.command_execution(value) @@ -145,6 +144,9 @@ def state_change(self): self._change_state_to(StateCodes.execute) elif self.act_state == StateCodes.resetting: self._change_state_to(StateCodes.idle) + # fix for commands being enabled when procedure is not set + self.disable_commands_if_no_procedure( + self.procedure_control.attributes['ProcedureReq'].value) elif self.act_state == StateCodes.holding: self._change_state_to(StateCodes.held) elif self.act_state == StateCodes.unholding: From 91a626cac627bbe4eae37d25321c5d28aea6cfeb Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Tue, 12 Aug 2025 15:08:23 +0200 Subject: [PATCH 25/61] add init value --- src/mtppy/operation_elements.py | 44 ++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/mtppy/operation_elements.py b/src/mtppy/operation_elements.py index a7fbad0..48fc610 100644 --- a/src/mtppy/operation_elements.py +++ b/src/mtppy/operation_elements.py @@ -9,7 +9,7 @@ class AnaServParam(SUCOperationElement): def __init__(self, tag_name: str, tag_description: str = '', v_min: float = 0, v_max: float = 100, - v_scl_min: float = 0, v_scl_max: float = 100, v_unit: int = 0): + v_scl_min: float = 0, v_scl_max: float = 100, v_unit: int = 0, init_value: float = None): """ Analog Service Parameter (AnaServParam). Parameter names correspond attribute names in VDI/VDE/NAMUR 2658. """ @@ -22,11 +22,13 @@ def __init__(self, tag_name: str, tag_description: str = '', v_min: float = 0, v self.v_scl_min = v_scl_min self.v_scl_max = v_scl_max self.v_unit = v_unit + if init_value is None: + init_value = v_min - self._add_attribute(Attribute('VOp', float, init_value=v_min, sub_cb=self.set_v_op)) - self._add_attribute(Attribute('VInt', float, init_value=v_min, sub_cb=self.set_v_int)) - self._add_attribute(Attribute('VExt', float, init_value=v_min, sub_cb=self.set_v_ext)) - self._add_attribute(Attribute('VReq', float, init_value=v_min)) + self._add_attribute(Attribute('VOp', float, init_value=init_value, sub_cb=self.set_v_op)) + self._add_attribute(Attribute('VInt', float, init_value=init_value, sub_cb=self.set_v_int)) + self._add_attribute(Attribute('VExt', float, init_value=init_value, sub_cb=self.set_v_ext)) + self._add_attribute(Attribute('VReq', float, init_value=init_value)) self._add_attribute(Attribute('VOut', float, init_value=0)) self._add_attribute(Attribute('VFbk', float, init_value=0)) self._add_attribute(Attribute('VUnit', int, init_value=self.v_unit)) @@ -79,7 +81,7 @@ def set_v_fbk(self, value): class BinServParam(SUCOperationElement): - def __init__(self, tag_name: str, tag_description: str = '', v_state_0: str = 'false', v_state_1: str = 'true'): + def __init__(self, tag_name: str, tag_description: str = '', v_state_0: str = 'false', v_state_1: str = 'true', init_value: bool = False): """ Binary Service Parameter (BinServParam). Parameter names correspond attribute names in VDI/VDE/NAMUR 2658. """ @@ -90,10 +92,10 @@ def __init__(self, tag_name: str, tag_description: str = '', v_state_0: str = 'f self.v_state_0 = v_state_0 self.v_state_1 = v_state_1 - self._add_attribute(Attribute('VOp', bool, init_value=False, sub_cb=self.set_v_op)) - self._add_attribute(Attribute('VInt', bool, init_value=False, sub_cb=self.set_v_int)) - self._add_attribute(Attribute('VExt', bool, init_value=False, sub_cb=self.set_v_ext)) - self._add_attribute(Attribute('VReq', bool, init_value=False)) + self._add_attribute(Attribute('VOp', bool, init_value=init_value, sub_cb=self.set_v_op)) + self._add_attribute(Attribute('VInt', bool, init_value=init_value, sub_cb=self.set_v_int)) + self._add_attribute(Attribute('VExt', bool, init_value=init_value, sub_cb=self.set_v_ext)) + self._add_attribute(Attribute('VReq', bool, init_value=init_value)) self._add_attribute(Attribute('VOut', bool, init_value=False)) self._add_attribute(Attribute('VFbk', bool, init_value=False)) self._add_attribute(Attribute('VState0', str, init_value=self.v_state_0)) @@ -135,7 +137,7 @@ def set_v_fbk(self, value): class DIntServParam(SUCOperationElement): def __init__(self, tag_name: str, tag_description: str = '', v_min: int = 0, v_max: int = 100, v_scl_min: int = 0, - v_scl_max: int = 100, v_unit: int = 0): + v_scl_max: int = 100, v_unit: int = 0, init_value: int = None): """ Discrete Integer Service Parameter (DIntServParam). Parameter names correspond attribute names in VDI/VDE/NAMUR 2658. """ @@ -148,11 +150,13 @@ def __init__(self, tag_name: str, tag_description: str = '', v_min: int = 0, v_m self.v_scl_min = v_scl_min self.v_scl_max = v_scl_max self.v_unit = v_unit + if init_value is None: + init_value = v_min - self._add_attribute(Attribute('VOp', int, init_value=v_min, sub_cb=self.set_v_op)) - self._add_attribute(Attribute('VInt', int, init_value=v_min, sub_cb=self.set_v_int)) - self._add_attribute(Attribute('VExt', int, init_value=v_min, sub_cb=self.set_v_ext)) - self._add_attribute(Attribute('VReq', int, init_value=v_min)) + self._add_attribute(Attribute('VOp', int, init_value=init_value, sub_cb=self.set_v_op)) + self._add_attribute(Attribute('VInt', int, init_value=init_value, sub_cb=self.set_v_int)) + self._add_attribute(Attribute('VExt', int, init_value=init_value, sub_cb=self.set_v_ext)) + self._add_attribute(Attribute('VReq', int, init_value=init_value)) self._add_attribute(Attribute('VOut', int, init_value=0)) self._add_attribute(Attribute('VFbk', int, init_value=0)) self._add_attribute(Attribute('VUnit', int, init_value=self.v_unit)) @@ -207,7 +211,7 @@ def set_v_fbk(self, value): class StringServParam(SUCOperationElement): - def __init__(self, tag_name: str, tag_description: str = ''): + def __init__(self, tag_name: str, tag_description: str = '', init_value: str = ''): """ String Service Parameter (StringServParam). Parameter names correspond attribute names in VDI/VDE/NAMUR 2658. """ @@ -215,10 +219,10 @@ def __init__(self, tag_name: str, tag_description: str = ''): self.op_src_mode = OperationSourceMode() - self._add_attribute(Attribute('VOp', str, init_value='', sub_cb=self.set_v_op)) - self._add_attribute(Attribute('VInt', str, init_value='', sub_cb=self.set_v_int)) - self._add_attribute(Attribute('VExt', str, init_value='', sub_cb=self.set_v_ext)) - self._add_attribute(Attribute('VReq', str, init_value='')) + self._add_attribute(Attribute('VOp', str, init_value=init_value, sub_cb=self.set_v_op)) + self._add_attribute(Attribute('VInt', str, init_value=init_value, sub_cb=self.set_v_int)) + self._add_attribute(Attribute('VExt', str, init_value=init_value, sub_cb=self.set_v_ext)) + self._add_attribute(Attribute('VReq', str, init_value=init_value)) self._add_attribute(Attribute('VOut', str, init_value='')) self._add_attribute(Attribute('VFbk', str, init_value='')) self._add_attribute(Attribute('Sync', bool, False)) From 6ff29579d235e276f1a0fb27a4f84e5d9b8bee07 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Wed, 13 Aug 2025 15:40:08 +0200 Subject: [PATCH 26/61] moved state change call into run_thread function --- src/mtppy/service.py | 18 +++++++++--------- src/mtppy/thread_control.py | 7 ++++--- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/mtppy/service.py b/src/mtppy/service.py index d895af7..74b69e3 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -22,14 +22,14 @@ class Service(SUCServiceControl): """Represents a service of the PEA. - To create a new service, inherit from this class and implement at least the abstract methods. + To create a new service, inherit from this class and implement at least the abstract methods. These include: - starting - execute - completing - Other methods can be overridden as needed. - By default the higher level methods (on the right side) will call the lower level methods. + Other methods can be overridden as needed. + By default the higher level methods (on the right side) will call the lower level methods. See the following diagram for how the methods are called: +------------+ +------------+ +------------+ +------------+ +------------+ @@ -38,9 +38,9 @@ class Service(SUCServiceControl): +------------+ +------------+ +------------+ +------------+ || | paused | <- | held | <- | stopped | <- | aborted | +------------+ +------------+ +------------+ +------------+ - +------------+ +------------+ +------------+ - | starting | <- | resuming | <- | unholding | - +------------+ +------------+ +------------+ + +------------+ +------------+ +------------+ + | starting | <- | resuming | <- | unholding | + +------------+ +------------+ +------------+ """ @@ -51,10 +51,11 @@ def __init__(self, tag_name: str, tag_description: str, exception_callback: Call Args: tag_name (str): Tag name of the service. tag_description (str): Tag description of the service. - exception_callback (Callable[[Exception], None]): Function to call + exception_callback (Callable[[Exception], None]): Function to call when an exception occurs in the thread. """ super().__init__(tag_name, tag_description) + self.exception = None self.op_src_mode = OperationSourceMode() @@ -68,7 +69,7 @@ def __init__(self, tag_name: str, tag_description: str, exception_callback: Call execution_routine=self.state_change_callback) self.thread_ctrl = ThreadControl(service_name=tag_name, - state_change_function=self.state_change(), + state_change_function=self.state_change, exception_callback=exception_callback) self.op_src_mode.add_enter_offline_callback(self.state_machine.command_en_ctrl.disable_all) @@ -372,4 +373,3 @@ def resetting(self): else: pass # Reset the state machine to idle - self.state_change() diff --git a/src/mtppy/thread_control.py b/src/mtppy/thread_control.py index 9ef2c34..3fe32d2 100644 --- a/src/mtppy/thread_control.py +++ b/src/mtppy/thread_control.py @@ -75,9 +75,10 @@ def run_thread(self, target_function: Callable): try: try: target_function() - # changes state for transitional states - # if self.state_change_function: - # self.state_change_function() + # changes state for transitional states, only if the state is the current state. + if self.running_state is target_function.__name__: + if self.state_change_function: + self.state_change_function() # Catch InterruptedError thrown by stop events. Should not cause an error. except InterruptedError: _logger.debug("Stop event was set, stopping thread execution.") From 9f073b778764f909d84e134e3d51dc8b932eff74 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Thu, 14 Aug 2025 18:14:11 +0200 Subject: [PATCH 27/61] added type notation --- src/mtppy/attribute.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/mtppy/attribute.py b/src/mtppy/attribute.py index 82ee288..80e2b92 100644 --- a/src/mtppy/attribute.py +++ b/src/mtppy/attribute.py @@ -1,19 +1,24 @@ import logging +from collections.abc import Callable +from typing import Any + + _logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") class Attribute: - def __init__(self, name: str, data_type, init_value, sub_cb=None): + def __init__(self, name: str, data_type, init_value, sub_cb: Callable[[Any], None] = None): """ - Atttribute represents an elementary object (attribute or parameter) of a data assembly. Depending on whether + Attribute represents an elementary object (attribute or parameter) of a data assembly. Depending on whether subscription callback is defined or not, it might be an monitored object or a static OPC UA node. Args: name (str): Attribute name. data_type (type): Attribute type. init_value (type): Initial value of the attribute. - sub_cb (callable, optional): Subscription callback if applied. + sub_cb (Callable[[Any], None], optional): Subscription callback. + If defined, the attribute will be monitored. """ self.name = name self.type = data_type @@ -21,7 +26,7 @@ def __init__(self, name: str, data_type, init_value, sub_cb=None): self.init_value = corrected_value self.value = corrected_value self.comm_obj = None - self.sub_cb = sub_cb + self.sub_cb: Callable[[Any], None] = sub_cb def set_value(self, value): """ @@ -73,12 +78,13 @@ def attach_communication_object(self, communication_object): """ self.comm_obj = communication_object - def attach_subscription_callback(self, sub_cb): + def attach_subscription_callback(self, sub_cb: Callable[[Any], None]): """ Attach a subscription callback to the attribute. Args: - sub_cb (callable): Subscription callback. + sub_cb (Callable[[Any], None]): + Subscription callback function that will be called when the value changes. """ self.sub_cb = sub_cb From 6a23727b5974dc2e726f0753aa706d54700f784e Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Fri, 15 Aug 2025 11:37:28 +0200 Subject: [PATCH 28/61] fix error for start_subscriptions if no subscriptions are added --- src/mtppy/opcua_server_pea.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/mtppy/opcua_server_pea.py b/src/mtppy/opcua_server_pea.py index 5a730c2..91d36f7 100644 --- a/src/mtppy/opcua_server_pea.py +++ b/src/mtppy/opcua_server_pea.py @@ -253,7 +253,11 @@ def _start_subscription(self): handler = Marshalling() handler.import_subscription_list(self.subscription_list) sub = self.opcua_server.create_subscription(500, handler) - handle = sub.subscribe_data_change(self.subscription_list.get_nodeid_list()) + nodeid_list = self.subscription_list.get_nodeid_list() + if nodeid_list is None: + _logger.warning('No subscriptions to OPC UA nodes defined.') + return + handle = sub.subscribe_data_change(nodeid_list) class SubscriptionList: From 09660cadc5f4353158eb2ff711906d95529c0726 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Fri, 15 Aug 2025 16:26:57 +0200 Subject: [PATCH 29/61] add ability to add custom data assembly sets to the server --- src/mtppy/opcua_server_pea.py | 152 +++++++++++++++++++++++++++------- 1 file changed, 122 insertions(+), 30 deletions(-) diff --git a/src/mtppy/opcua_server_pea.py b/src/mtppy/opcua_server_pea.py index 91d36f7..acb9642 100644 --- a/src/mtppy/opcua_server_pea.py +++ b/src/mtppy/opcua_server_pea.py @@ -9,7 +9,7 @@ class OPCUAServerPEA: - def __init__(self, mtp_generator: MTPGenerator = None, endpoint: str ='opc.tcp://127.0.0.1:4840/'): + def __init__(self, mtp_generator: MTPGenerator = None, endpoint: str = 'opc.tcp://127.0.0.1:4840/'): """ Defines an OPC UA server for PEA. @@ -19,6 +19,7 @@ def __init__(self, mtp_generator: MTPGenerator = None, endpoint: str ='opc.tcp:/ """ self.service_set = {} self.active_elements = {} + self.custom_data_assembly_sets: dict[str, dict[str, SUCDataAssembly]] = {} self.endpoint = endpoint self.opcua_server = None self.opcua_ns = 3 @@ -26,6 +27,16 @@ def __init__(self, mtp_generator: MTPGenerator = None, endpoint: str ='opc.tcp:/ self._init_opcua_server() self.mtp = mtp_generator + self._folders = ['configuration_parameters', 'procedures', 'procedure_parameters', + 'process_value_ins', 'report_values', 'process_value_outs'] + """Folders that are created in the OPC UA server if found in a data assembly + even if they are not of type SUCDataAssembly.""" + self._leaves = ['op_src_mode', 'state_machine', 'procedure_control'] + """Folders that are created in the OPC UA server if found in a data assembly + even if they are not of type SUCDataAssembly. + + No subfolders are created for them.""" + def add_service(self, service: Service): """ Add a service to the PEA. @@ -44,6 +55,52 @@ def add_active_element(self, active_element: SUCActiveElement): """ self.active_elements[active_element.tag_name] = active_element + def add_custom_data_assembly_set(self, root_folder_name: str, data_assembly_set: dict[str, SUCDataAssembly]): + """ + Add a custom data assembly to the PEA. + + Args: + root_folder_name (str): Root folder name for the custom data assembly. + data_assembly (dict[str, SUCDataAssembly]): Custom data assembly instance. + Has to be a dictionary with folder names as keys and SUCDataAssembly instances as values. + If name is '', the data assembly is added to the root of the PEA. + """ + + if root_folder_name in self.custom_data_assembly_sets: + raise ValueError(f"Data assembly with tag name '{root_folder_name}' already exists.") + if not isinstance(data_assembly_set, dict) or data_assembly_set.__len__() == 0: + raise ValueError("Data assembly set must be a non-empty dictionary.") + if not isinstance(next(iter(data_assembly_set)), str): + raise TypeError("Data assembly set keys must be strings.") + if not isinstance(next(iter(data_assembly_set.values())), SUCDataAssembly): + raise TypeError("Data assembly set values must be instances of SUCDataAssembly.") + + root_folder_name = (next(iter(data_assembly_set)) if root_folder_name == '' + else root_folder_name) + self.custom_data_assembly_sets[root_folder_name] = data_assembly_set + + def add_folders(self, folders: list[str]): + """ + Folders that are created in the OPC UA server if found in a opcua object. + even if they are not of type SUCDataAssembly. + + Args: + folders (list[str]): List of folder names. + """ + self._folders.extend(folders) + + def add_leaves(self, leaves: list[str]): + """ + Leaves that are created in the OPC UA server if found in a opcua object. + even if they are not of type SUCDataAssembly. + + No Subfolders are created for them. + + Args: + leaves (list[str]): List of leaf names. + """ + self._leaves.extend(leaves) + def _init_opcua_server(self): """ Initializes an OPC UA server and sets the endpoint. @@ -109,13 +166,29 @@ def _build_opcua_server(self): _logger.info(f'- service {service.tag_name}') self._create_opcua_objects_for_folders(service, services_node_id, services_node) - act_elem_node_id = f'ns={ns};s=active_elements' - act_elem_node = server.add_folder(act_elem_node_id, "active_elements") - for active_element in self.active_elements.values(): - _logger.info(f'- active element {active_element.tag_name}') - self._create_opcua_objects_for_folders(active_element, act_elem_node_id, act_elem_node) - - # add SupportedRoleClass to all InternalElements + if self.active_elements.__len__() > 0: + act_elem_node_id = f'ns={ns};s=active_elements' + act_elem_node = server.add_folder(act_elem_node_id, "active_elements") + for active_element in self.active_elements.values(): + _logger.info(f'- active element {active_element.tag_name}') + self._create_opcua_objects_for_folders( + active_element, act_elem_node_id, act_elem_node) + + # add custom data assemblies + for root_folder_name, data_assembly_set in self.custom_data_assembly_sets.items(): + _logger.info(f'- custom data assembly {root_folder_name}') + if root_folder_name == next(iter(data_assembly_set)): + self._create_opcua_objects_for_folders( + data_assembly_set[root_folder_name], f"{ns};s={root_folder_name}", server, root_folder_name) + else: + custom_da_node_id = f'ns={ns};s={root_folder_name}' + custom_da_node = server.add_folder(custom_da_node_id, root_folder_name) + for data_assembly in data_assembly_set.values(): + _logger.info(f'-- data assembly {data_assembly.tag_name}') + self._create_opcua_objects_for_folders( + data_assembly, custom_da_node_id, custom_da_node) + + # add SupportedRoleClass to all InternalElements if self.mtp: self.mtp.apply_add_supported_role_class() @@ -124,7 +197,9 @@ def _build_opcua_server(self): _logger.info(f'MTP manifest export to {self.mtp.export_path}') self.mtp.export_manifest() - def _create_opcua_objects_for_folders(self, data_assembly: SUCDataAssembly, parent_opcua_prefix: str, parent_opcua_object): + def _create_opcua_objects_for_folders(self, + data_assembly: SUCDataAssembly, parent_opcua_prefix: str, + parent_opcua_object, name: str = None): """ Iterates over data assemblies to create OPC UA folders. @@ -132,17 +207,17 @@ def _create_opcua_objects_for_folders(self, data_assembly: SUCDataAssembly, pare data_assembly (SUCDataAssembly): Data assembly. parent_opcua_prefix (str): Prefix to add in front of the data assembly tag. parent_opcua_object: Parent OPC UA node where the data assembly is added. + name (str): Name of the data assembly. If None, the tag name of the data assembly is used. """ - da_node_id = f'{parent_opcua_prefix}.{data_assembly.tag_name}' - da_node = parent_opcua_object.add_folder(da_node_id, data_assembly.tag_name) + if name is None: + name = data_assembly.tag_name + da_node_id = f'{parent_opcua_prefix}.{name}' + da_node = parent_opcua_object.add_folder(da_node_id, name) + _logger.debug(f'OPCUA Folder: {da_node_id}, Name: {name}') # type of data assembly (e.g. services, active_elements, procedures etc.) da_type = parent_opcua_prefix.split('=')[-1].split('.')[-1] - folders = ['configuration_parameters', 'procedures','procedure_parameters', - 'process_value_ins', 'report_values', 'process_value_outs'] - leaves = ['op_src_mode', 'state_machine', 'procedure_control'] - # create instance of ServiceControl, HealthStateView, DIntServParam etc. if self.mtp: instance = self.mtp.create_instance(data_assembly, da_node_id) @@ -151,25 +226,24 @@ def _create_opcua_objects_for_folders(self, data_assembly: SUCDataAssembly, pare if self.mtp: link_id = self.mtp.random_id_generator() - if da_type == 'services' or da_type in folders: + if da_type == 'services' or da_type in self._folders: link_id = self.mtp.create_components_for_services(data_assembly, da_type) else: link_id = None + # Add attributes of data assembly if hasattr(data_assembly, 'attributes'): self._create_opcua_objects_for_leaves(data_assembly, da_node_id, da_node, instance) - for section_name in folders + leaves: - if not hasattr(data_assembly, section_name): + # Find any variable of data_assembly that is of type SUCDataAssembly + # and create corresponding folders and leaves for it + for variable_name, value in self.__get_objects_attributes(data_assembly).items(): + if not isinstance(value, SUCDataAssembly) and variable_name not in self._folders + self._leaves: continue - section = eval(f'data_assembly.{section_name}') - section_node_id = f'{da_node_id}.{section_name}' - section_node = da_node.add_folder(section_node_id, section_name) - if section_name in folders: - for parameter in eval(f'data_assembly.{section_name}.values()'): - self._create_opcua_objects_for_folders(parameter, section_node_id, section_node) - if section_name in leaves: - self._create_opcua_objects_for_leaves(section, section_node_id, section_node, instance) + if name in self._leaves: + continue # do not create folders for leaves + folder_name = value.tag_name if hasattr(value, 'tag_name') else variable_name + self._create_opcua_objects_for_folders(value, da_node_id, da_node, folder_name) # create linked obj between instance and service component if self.mtp: @@ -206,18 +280,19 @@ def _create_opcua_objects_for_leaves(self, opcua_object, parent_opcua_prefix: st linked_id = None """ - add attributes of data assembly to corresponding instance under InstanceList + add attributes of data assembly to corresponding instance under InstanceList e.g.: attributes of services belong to InstanceList/ServiceControl - exception: some attributes of procedure ('ProcedureId', 'IsSelfCompleting', 'IsDefault') should be - added to InstanceHierarchy_Service/service/procedure. The other attributes of procedure should belong to + exception: some attributes of procedure ('ProcedureId', 'IsSelfCompleting', 'IsDefault') should be + added to InstanceHierarchy_Service/service/procedure. The other attributes of procedure should belong to InstanceList/HeathStateView """ if type(opcua_object).__name__ == 'Procedure' and attr.name in ['ProcedureId', 'IsSelfCompleting', 'IsDefault']: pass else: if self.mtp: - self.mtp.add_attr_to_instance(par_instance, attr.name, attr.init_value, linked_id) + self.mtp.add_attr_to_instance( + par_instance, attr.name, attr.init_value, linked_id) # We subscribe to nodes that are writable attributes if attr.sub_cb is not None: @@ -259,6 +334,23 @@ def _start_subscription(self): return handle = sub.subscribe_data_change(nodeid_list) + def __get_objects_attributes(self, object) -> dict: + """ + Get all attributes of the given object. + + Args: + object: any python object. + """ + if isinstance(object, dict): + return object + if isinstance(object, list): + # return a dictionary with index as key + return {f'{i}': item for i, item in enumerate(object)} + try: + return vars(object) + except TypeError: + return {} + class SubscriptionList: def __init__(self): From 8e0d8f12aa46ec8030475280e0f6e639f73b0cf0 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Wed, 20 Aug 2025 19:58:59 +0200 Subject: [PATCH 30/61] bugfix for custom data assembly --- src/mtppy/opcua_server_pea.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/mtppy/opcua_server_pea.py b/src/mtppy/opcua_server_pea.py index acb9642..188c084 100644 --- a/src/mtppy/opcua_server_pea.py +++ b/src/mtppy/opcua_server_pea.py @@ -1,5 +1,5 @@ import logging -from opcua import Server, ua +from opcua import Server, ua, Node from mtppy.communication_object import OPCUACommunicationObject from mtppy.service import Service from mtppy.suc_data_assembly import SUCDataAssembly, SUCActiveElement @@ -177,9 +177,10 @@ def _build_opcua_server(self): # add custom data assemblies for root_folder_name, data_assembly_set in self.custom_data_assembly_sets.items(): _logger.info(f'- custom data assembly {root_folder_name}') + # if root_folder_name is the same as the first key add to root if root_folder_name == next(iter(data_assembly_set)): self._create_opcua_objects_for_folders( - data_assembly_set[root_folder_name], f"{ns};s={root_folder_name}", server, root_folder_name) + data_assembly_set[root_folder_name], f"ns={ns};s={root_folder_name}", server, root_folder_name) else: custom_da_node_id = f'ns={ns};s={root_folder_name}' custom_da_node = server.add_folder(custom_da_node_id, root_folder_name) @@ -197,9 +198,9 @@ def _build_opcua_server(self): _logger.info(f'MTP manifest export to {self.mtp.export_path}') self.mtp.export_manifest() - def _create_opcua_objects_for_folders(self, - data_assembly: SUCDataAssembly, parent_opcua_prefix: str, - parent_opcua_object, name: str = None): + def _create_opcua_objects_for_folders(self, data_assembly: SUCDataAssembly, + parent_opcua_prefix: str, parent_opcua_object: Node, + name: str = None): """ Iterates over data assemblies to create OPC UA folders. From dc1cba55e08dd3cd16138a883faf3f3d8eb1420a Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Sun, 24 Aug 2025 12:44:15 +0200 Subject: [PATCH 31/61] added ability to link param op_src_mode to service op_src_mode for easier control --- src/mtppy/operation_elements.py | 2 +- src/mtppy/operation_source_mode.py | 89 ++++++++++++++++++++++-------- src/mtppy/service.py | 2 +- 3 files changed, 69 insertions(+), 24 deletions(-) diff --git a/src/mtppy/operation_elements.py b/src/mtppy/operation_elements.py index 48fc610..7f7a4ce 100644 --- a/src/mtppy/operation_elements.py +++ b/src/mtppy/operation_elements.py @@ -143,7 +143,7 @@ def __init__(self, tag_name: str, tag_description: str = '', v_min: int = 0, v_m """ super().__init__(tag_name, tag_description) - self.op_src_mode = OperationSourceMode() + self.op_src_mode = OperationSourceMode(tag_name) self.v_min = v_min self.v_max = v_max diff --git a/src/mtppy/operation_source_mode.py b/src/mtppy/operation_source_mode.py index 887eb60..981aebe 100644 --- a/src/mtppy/operation_source_mode.py +++ b/src/mtppy/operation_source_mode.py @@ -6,7 +6,7 @@ class OperationSourceMode: - def __init__(self): + def __init__(self, name_of_parent: str = ''): """ Represents the operation and source modes control. """ @@ -30,7 +30,7 @@ def __init__(self): 'SrcIntAct': Attribute('SrcIntAct', bool, init_value=False), 'SrcExtAct': Attribute('SrcExtAct', bool, init_value=False) } - self.switch_to_offline_mode_allowed = False + self.switch_to_offline_mode_allowed = True self.enter_offline_callbacks = [] self.exit_offline_callbacks = [] @@ -41,6 +41,9 @@ def __init__(self): self.enter_automatic_callbacks = [] self.exit_automatic_callbacks = [] + self.linked_op_src_modes = [] + self._name_of_parent = f"{name_of_parent}: " if name_of_parent != '' else '' + def allow_switch_to_offline_mode(self, allow_flag: bool): self.switch_to_offline_mode_allowed = allow_flag @@ -64,32 +67,32 @@ def add_exit_automatic_callback(self, callback: callable): def _enter_off(self): if len(self.enter_offline_callbacks): - _logger.debug('Applying enter offline mode callbacks') + _logger.debug(f'{self._name_of_parent}Applying enter offline mode callbacks') [cb() for cb in self.enter_offline_callbacks] def _exit_off(self): if len(self.exit_offline_callbacks): - _logger.debug('Applying exit offline mode callbacks') + _logger.debug(f'{self._name_of_parent}Applying exit offline mode callbacks') [cb() for cb in self.exit_offline_callbacks] def _enter_op(self): if len(self.enter_operator_callbacks): - _logger.debug('Applying enter operator mode callbacks') + _logger.debug(f'{self._name_of_parent}Applying enter operator mode callbacks') [cb() for cb in self.enter_operator_callbacks] def _exit_op(self): if len(self.exit_operator_callbacks): - _logger.debug('Applying exit operator mode callbacks') + _logger.debug(f'{self._name_of_parent}Applying exit operator mode callbacks') [cb() for cb in self.exit_operator_callbacks] def _enter_aut(self): if len(self.enter_automatic_callbacks): - _logger.debug('Applying enter automatic mode callbacks') + _logger.debug(f'{self._name_of_parent}Applying enter automatic mode callbacks') [cb() for cb in self.enter_automatic_callbacks] def _exit_aut(self): if len(self.exit_automatic_callbacks): - _logger.debug('Applying exit automatic mode callbacks') + _logger.debug(f'{self._name_of_parent}Applying exit automatic mode callbacks') [cb() for cb in self.exit_automatic_callbacks] def _opmode_to_off(self): @@ -107,7 +110,7 @@ def _opmode_to_off(self): self._exit_aut() self._enter_off() - _logger.debug('Operation mode is now off') + _logger.debug(f'{self._name_of_parent}Operation mode is now off') self._src_to_off() def _opmode_to_aut(self): @@ -125,7 +128,7 @@ def _opmode_to_aut(self): self._exit_op() self._enter_aut() - _logger.debug('Operation mode is now aut') + _logger.debug(f'{self._name_of_parent}Operation mode is now aut') self._src_to_int() def _opmode_to_op(self): @@ -142,90 +145,132 @@ def _opmode_to_op(self): self._exit_aut() self._enter_op() - _logger.debug('Operation mode is now op') + _logger.debug(f'{self._name_of_parent}Operation mode is now op') self._src_to_off() + def add_linked_op_src_mode(self, linked_op_src_mode): + """ + Adds a linked operation source mode. + + Args: + linked_op_src_mode (OperationSourceMode): The linked operation source mode to add. + """ + if isinstance(linked_op_src_mode, OperationSourceMode): + self.linked_op_src_modes.append(linked_op_src_mode) + else: + raise TypeError("linked_op_src_mode must be an instance of OperationSourceMode") + + def _update_linked_op_src_modes(self, attribute_name: str, value: bool): + """ + Updates the linked operation source modes based on the attribute change. + + Args: + attribute_name (str): The name of the attribute that changed. + value (bool): The new value of the attribute. + """ + if self.linked_op_src_modes == []: + return + # if value is False: + # return + _logger.debug( + f'{self._name_of_parent}Updating linked op_src_modes for attribute {attribute_name} to {value}') + linked_op_src_mode: OperationSourceMode + for linked_op_src_mode in self.linked_op_src_modes: + linked_op_src_mode.attributes[attribute_name].set_value(value) + def set_state_channel(self, value: bool): - _logger.debug('Operation mode channel is now %s' % value) + _logger.debug(f'{self._name_of_parent}Operation mode channel is now %s' % value) + self._update_linked_op_src_modes('StateChannel', value) def set_state_aut_aut(self, value: bool): - _logger.debug(f'StateAutAut set to {value}') + _logger.debug(f'{self._name_of_parent}StateAutAut set to {value}') if self.attributes['StateChannel'].value and value: if self.attributes['StateOffAct'].value or self.attributes['StateOpAct'].value: self._opmode_to_aut() + self._update_linked_op_src_modes('StateAutAut', value) def set_state_aut_op(self, value: bool): - _logger.debug(f'StateAutOp set to {value}') + _logger.debug(f'{self._name_of_parent}StateAutOp set to {value}') if not self.attributes['StateChannel'].value and value: if self.attributes['StateOffAct'].value or self.attributes['StateOpAct'].value: self._opmode_to_aut() self.attributes['StateAutOp'].set_value(False) + self._update_linked_op_src_modes('StateAutOp', value) def set_state_off_aut(self, value: bool): - _logger.debug(f'StateOffAut set to {value}') + _logger.debug(f'{self._name_of_parent}StateOffAut set to {value}') if self.attributes['StateChannel'].value and value and self.switch_to_offline_mode_allowed: if self.attributes['StateAutAct'].value or self.attributes['StateOpAct'].value: self._opmode_to_off() + self._update_linked_op_src_modes('StateOffAut', value) def set_state_off_op(self, value: bool): - _logger.debug(f'StateOffOp set to {value}') + _logger.debug(f'{self._name_of_parent}StateOffOp set to {value}') if not self.attributes['StateChannel'].value and value and self.switch_to_offline_mode_allowed: if self.attributes['StateAutAct'].value or self.attributes['StateOpAct'].value: self._opmode_to_off() self.attributes['StateOffOp'].set_value(False) + self._update_linked_op_src_modes('StateOffOp', value) def set_state_op_aut(self, value: bool): - _logger.debug(f'StateOpAut set to {value}') + _logger.debug(f'{self._name_of_parent}StateOpAut set to {value}') if self.attributes['StateChannel'].value and value: if self.attributes['StateOffAct'].value or self.attributes['StateOpAct'].value: self._opmode_to_op() + self._update_linked_op_src_modes('StateOpAut', value) def set_state_op_op(self, value: bool): - _logger.debug(f'StateOpOp set to {value}') + _logger.debug(f'{self._name_of_parent}StateOpOp set to {value}') if not self.attributes['StateChannel'].value and value: if self.attributes['StateOffAct'].value or self.attributes['StateAutAct'].value: self._opmode_to_op() self.attributes['StateOpOp'].set_value(False) + self._update_linked_op_src_modes('StateOpOp', value) def _src_to_off(self): self.attributes['SrcIntAct'].set_value(False) self.attributes['SrcExtAct'].set_value(False) - _logger.debug('Source mode is now off') + _logger.debug(f'{self._name_of_parent}Source mode is now off') def _src_to_int(self): self.attributes['SrcIntAct'].set_value(True) self.attributes['SrcExtAct'].set_value(False) - _logger.debug('Source mode is now int') + _logger.debug(f'{self._name_of_parent}Source mode is now int') def _src_to_ext(self): self.attributes['SrcIntAct'].set_value(False) self.attributes['SrcExtAct'].set_value(True) - _logger.debug('Source mode is now ext') + _logger.debug(f'{self._name_of_parent}Source mode is now ext') def set_src_channel(self, value: bool): - _logger.debug('Source mode channel is now %s' % value) + _logger.debug(f'{self._name_of_parent}Source mode channel is now %s' % value) + self._update_linked_op_src_modes('SrcChannel', value) def set_src_ext_aut(self, value: bool): if not self.attributes['StateOffAct'].value and value: if self.attributes['SrcChannel'].value: self._src_to_ext() + self._update_linked_op_src_modes('SrcExtAut', value) def set_src_ext_op(self, value: bool): if not self.attributes['StateOffAct'].value and value: if not self.attributes['SrcChannel'].value: self._src_to_ext() self.attributes['SrcExtOp'].set_value(False) + self._update_linked_op_src_modes('SrcExtOp', value) def set_src_int_aut(self, value: bool): if not self.attributes['StateOffAct'].value and value: if self.attributes['SrcChannel'].value: self._src_to_int() + self._update_linked_op_src_modes('SrcIntAut', value) def set_src_int_op(self, value: bool): if not self.attributes['StateOffAct'].value and value: if not self.attributes['SrcChannel'].value: self._src_to_int() self.attributes['SrcIntOp'].set_value(False) + self._update_linked_op_src_modes('SrcIntOp', value) class OperationSourceModeActiveElements(OperationSourceMode): diff --git a/src/mtppy/service.py b/src/mtppy/service.py index 74b69e3..d52ae8d 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -57,7 +57,7 @@ def __init__(self, tag_name: str, tag_description: str, exception_callback: Call super().__init__(tag_name, tag_description) self.exception = None - self.op_src_mode = OperationSourceMode() + self.op_src_mode = OperationSourceMode(tag_name) self.configuration_parameters = {} From a0a0d16bef605db130756246f004218945c3e729 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Wed, 27 Aug 2025 16:50:53 +0200 Subject: [PATCH 32/61] BinVlv changed locks to adhere to standard. Added proper handshake for op commands --- src/mtppy/active_elements.py | 180 ++++++++++++++++++++++++----------- 1 file changed, 124 insertions(+), 56 deletions(-) diff --git a/src/mtppy/active_elements.py b/src/mtppy/active_elements.py index bdb610a..8e5d64e 100644 --- a/src/mtppy/active_elements.py +++ b/src/mtppy/active_elements.py @@ -41,7 +41,8 @@ def __init__(self, tag_name, tag_description='', self._add_attribute(Attribute('SafePos', bool, init_value=safe_pos)) self._add_attribute(Attribute('SafePosEn', bool, init_value=self.safe_pos_en)) - self._add_attribute(Attribute('SafePosAct', bool, init_value=False)) # default value should be true? + self._add_attribute(Attribute('SafePosAct', bool, init_value=False) + ) # default value should be true? self._add_attribute(Attribute('OpenAut', bool, init_value=0, sub_cb=self.set_open_aut)) self._add_attribute(Attribute('CloseAut', bool, init_value=0, sub_cb=self.set_close_aut)) self._add_attribute(Attribute('OpenOp', bool, init_value=0, sub_cb=self.set_open_op)) @@ -119,14 +120,14 @@ def set_open_op(self, value: bool): if self.op_src_mode.attributes['StateOpAct'].value: if value and self._run_allowed(): self._run_open_vlv() - self.attributes['OpenOp'].value = False + self.attributes['OpenOp'].set_value(False) def set_close_op(self, value: bool): _logger.debug(f'CloseOp set to {value}') if self.op_src_mode.attributes['StateOpAct'].value: if value and self._run_allowed(): self._run_close_vlv() - self.attributes['CloseOp'].value = False + self.attributes['CloseOp'].set_value(False) def set_reset_op(self, value: bool): _logger.debug(f'ResetOp set to {value}') @@ -397,7 +398,8 @@ def monitor_static_error(self): if self.monitored_values.stop_event_lock.is_set(): _logger.debug('static monitoring stopped') break - states, control_signals = self.compare_states_control_signals(self.attributes['MonStatTi'].value) + states, control_signals = self.compare_states_control_signals( + self.attributes['MonStatTi'].value) if not all(states): # if the states of valve changed if all(control_signals): # but the control signals did not change @@ -413,7 +415,8 @@ def monitor_dynamic_error(self): if self.monitored_values.stop_event_lock.is_set(): _logger.debug('dynamic monitoring stopped') break - states, control_signals = self.compare_states_control_signals(self.attributes['MonDynTi'].value) + states, control_signals = self.compare_states_control_signals( + self.attributes['MonDynTi'].value) if all(states): # if the states of valve did not changed if not all(control_signals): # but a control command is executed @@ -490,7 +493,7 @@ def set_open_op(self, value: bool): self.monitored_values.open_op = value self.monitored_values.lock.release() self._run_open_vlv() - self.attributes['OpenOp'].value = False + self.attributes['OpenOp'].set_value(False) def set_close_op(self, value: bool): _logger.debug('CloseOp set to %s' % value) @@ -501,7 +504,7 @@ def set_close_op(self, value: bool): self.monitored_values.close_op = value self.monitored_values.lock.release() self._run_close_vlv() - self.attributes['CloseOp'].value = False + self.attributes['CloseOp'].set_value(False) def set_reset_op(self, value: bool): _logger.debug('ResetOp set to %s' % value) @@ -572,10 +575,12 @@ def __init__(self, tag_name: str, tag_description: str = '', open_fbk_calc: bool self.perm_en = perm_en self.intl_en = intl_en self.prot_en = prot_en + self.__reset_protect = False self._add_attribute(Attribute('SafePos', bool, init_value=safe_pos)) self._add_attribute(Attribute('SafePosEn', bool, init_value=self.safe_pos_en)) - self._add_attribute(Attribute('SafePosAct', bool, init_value=False)) # default value should be true? + self._add_attribute(Attribute('SafePosAct', bool, init_value=False) + ) # default value should be true? self._add_attribute(Attribute('OpenOp', bool, init_value=0, sub_cb=self.set_open_op)) self._add_attribute(Attribute('CloseOp', bool, init_value=0, sub_cb=self.set_close_op)) self._add_attribute(Attribute('OpenAut', bool, init_value=0, sub_cb=self.set_open_aut)) @@ -586,11 +591,12 @@ def __init__(self, tag_name: str, tag_description: str = '', open_fbk_calc: bool self._add_attribute(Attribute('CloseFbkCalc', bool, init_value=close_fbk_calc)) self._add_attribute(Attribute('CloseFbk', bool, init_value=False)) self._add_attribute(Attribute('PermEn', bool, init_value=perm_en)) - self._add_attribute(Attribute('Permit', bool, init_value=0)) + self._add_attribute(Attribute('Permit', bool, init_value=1, sub_cb=self.__set_permit)) self._add_attribute(Attribute('IntlEn', bool, init_value=intl_en)) - self._add_attribute(Attribute('Interlock', bool, init_value=0)) + self._add_attribute(Attribute('Interlock', bool, init_value=1, + sub_cb=self.__set_interlock)) self._add_attribute(Attribute('ProtEn', bool, init_value=prot_en)) - self._add_attribute(Attribute('Protect', bool, init_value=0)) + self._add_attribute(Attribute('Protect', bool, init_value=1, sub_cb=self.__set_protect)) self._add_attribute(Attribute('ResetOp', bool, init_value=0, sub_cb=self.set_reset_op)) self._add_attribute(Attribute('ResetAut', bool, init_value=0, sub_cb=self.set_reset_aut)) @@ -619,35 +625,52 @@ def set_open_aut(self, value: bool): def set_close_aut(self, value: bool): _logger.debug('CloseAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value: - if value and self._run_allowed(): + if value and self._run_allowed(for_close=True): self._run_close_vlv() def set_reset_aut(self, value: bool): _logger.debug('ResetAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and value: - self._reset_vlv() + self.reset_vlv() def set_open_op(self, value: bool): _logger.debug('OpenOp set to %s' % value) + if self.attributes['OpenOp'].value == value: + return if self.op_src_mode.attributes['StateOpAct'].value: if value and self._run_allowed(): self._run_open_vlv() - self.attributes['OpenOp'].value = False + self.attributes['OpenOp'].set_value(False) def set_close_op(self, value: bool): _logger.debug('CloseOp set to %s' % value) + if self.attributes['CloseOp'].value == value: + return if self.op_src_mode.attributes['StateOpAct'].value: - if value and self._run_allowed(): + if value and self._run_allowed(for_close=True): self._run_close_vlv() - self.attributes['CloseOp'].value = False + self.attributes['CloseOp'].set_value(False) def set_reset_op(self, value: bool): _logger.debug('ResetOp set to %s' % value) + if self.attributes['ResetOp'].value == value: + return if self.op_src_mode.attributes['StateOpAct'].value and value: - self._reset_vlv() - self.attributes['ResetOp'].set_value(False) + self.reset_vlv() + self.attributes['ResetOp'].set_value(False) - def _run_allowed(self): + def _run_allowed(self, for_close: bool = False): + """ + Check if running the valve is allowed + + Args: + for_close (bool): If True, check if closing the valve is allowed. Default is False. + Returns: + bool: True if running is allowed, False otherwise. + """ + # Closing the valve is always allowed + if for_close: + return True if self.attributes['PermEn'].value and self.attributes['Permit'].value == 0: _logger.debug('Permission is not given') return False @@ -675,8 +698,9 @@ def _run_close_vlv(self): if self.attributes['CloseFbkCalc']: self.attributes['CloseFbk'].set_value(True) - def _reset_vlv(self): + def reset_vlv(self): if self.attributes['ProtEn'].value and self.attributes['Protect'].value == 0: + self.__reset_protect = True self.attributes['Protect'].set_value(True) if self.attributes['SafePosEn'].value: @@ -699,27 +723,60 @@ def set_close_fbk(self, value: bool): self.attributes['CloseFbk'].set_value(value) _logger.debug('CloseFbk set to %s' % value) + def __set_permit(self, value: bool): + """Callback function for Permit attribute.""" + _logger.debug(f'Permit set to {value}') + if self.attributes['Permit'].value == value: + return + self.set_permit(value) + def set_permit(self, value: bool): + """Set Permit attribute with safety checks.""" + # don't set Permit to False if PermEn is False if not self.attributes['PermEn'].value: - value = True - self.attributes['Permit'].set_value(value) - _logger.debug('Permit set to %s' % value) - self.attributes['SafePosAct'].set_value(False) + self.attributes['Permit'].set_value(True) + return + # only set value if it is different from the current one + if self.attributes['Permit'].value != value: + self.attributes['Permit'].set_value(value) + return + + def __set_interlock(self, value: bool): + """Callback function for Interlock attribute.""" + _logger.debug(f'Interlock set to {value}') + if self.attributes['Interlock'].value == value: + return + self.set_interlock(value) def set_interlock(self, value: bool): + """Set Interlock attribute with safety checks.""" + # don't set Interlock to False if IntlEn is False if not self.attributes['IntlEn'].value: - value = True - self.attributes['Interlock'].set_value(value) - _logger.debug('Interlock set to %s' % value) - self._expect_save_pos() + self.attributes['Interlock'].set_value(True) + return + if self.attributes['Interlock'].value != value: + self.attributes['Interlock'].set_value(value) + self._expect_save_pos() + + def __set_protect(self, value: bool): + """Callback function for Protect attribute.""" + _logger.debug('Protect set to %s' % value) + if self.attributes['Protect'].value == value: + return + self.set_protect(value) def set_protect(self, value: bool): + """Set Protect attribute with safety checks. + Does not allow resetting Protect by default.""" if not self.attributes['ProtEn'].value: - value = True - if value: - self._reset_vlv() - self.attributes['Protect'].set_value(value) - _logger.debug('Protect set to %s' % value) + self.attributes['Protect'].set_value(True) + return + + if self.__reset_protect: + self.attributes['Protect'].set_value(True) + self.__reset_protect = False + else: + self.attributes['Protect'].set_value(False) self._expect_save_pos() def get_open_fbk(self): @@ -808,7 +865,8 @@ def monitor_static_error(self): if self.monitored_values.stop_event_lock.is_set(): _logger.debug('static monitoring stopped') break - states, control_signals = self.compare_states_control_signals(self.attributes['MonStatTi'].value) + states, control_signals = self.compare_states_control_signals( + self.attributes['MonStatTi'].value) if not all(states): # if the states of valve changed if all(control_signals): # but the control signals did not change @@ -824,7 +882,8 @@ def monitor_dynamic_error(self): if self.monitored_values.stop_event_lock.is_set(): _logger.debug('dynamic monitoring stopped') break - states, control_signals = self.compare_states_control_signals(self.attributes['MonDynTi'].value) + states, control_signals = self.compare_states_control_signals( + self.attributes['MonDynTi'].value) if all(states): # if the states of valve did not changed if not all(control_signals): # but a control command is executed @@ -880,7 +939,7 @@ def set_reset_aut(self, value: bool): self.monitored_values.lock.acquire() self.monitored_values.reset_aut = value self.monitored_values.lock.release() - self._reset_vlv() + self.reset_vlv() def set_open_op(self, value: bool): _logger.debug('OpenOp set to %s' % value) @@ -891,7 +950,7 @@ def set_open_op(self, value: bool): self.monitored_values.open_op = value self.monitored_values.lock.release() self._run_open_vlv() - self.attributes['OpenOp'].value = False + self.attributes['OpenOp'].set_value(False) def set_close_op(self, value: bool): _logger.debug('CloseOp set to %s' % value) @@ -902,7 +961,7 @@ def set_close_op(self, value: bool): self.monitored_values.close_op = value self.monitored_values.lock.release() self._run_close_vlv() - self.attributes['CloseOp'].value = False + self.attributes['CloseOp'].set_value(False) def set_reset_op(self, value: bool): _logger.debug('ResetOp set to %s' % value) @@ -911,7 +970,7 @@ def set_reset_op(self, value: bool): self.monitored_values.lock.acquire() self.monitored_values.reset_op = value self.monitored_values.lock.release() - self._reset_vlv() + self.reset_vlv() self.attributes['ResetOp'].set_value(False) def set_stop_monitor(self): @@ -936,7 +995,8 @@ def __init__(self, tag_name, tag_description='', rev_fbk_calc=True, fwd_fbk_calc self.prot_en = prot_en self._add_attribute(Attribute('SafePos', bool, init_value=safe_pos)) - self._add_attribute(Attribute('SafePosAct', bool, init_value=False)) # default value should be true? + self._add_attribute(Attribute('SafePosAct', bool, init_value=False) + ) # default value should be true? self._add_attribute(Attribute('FwdEn', bool, init_value=fwd_en)) self._add_attribute(Attribute('RevEn', bool, init_value=rev_en)) self._add_attribute(Attribute('StopOp', bool, init_value=0, sub_cb=self.set_stop_op)) @@ -994,14 +1054,14 @@ def set_fwd_op(self, value: bool): if self.op_src_mode.attributes['StateOpAct'].value and self.fwd_en: if value and self._run_allowed(): self._run_fwd_drv() - self.attributes['FwdOp'].value = False + self.attributes['FwdOp'].set_value(False) def set_rev_op(self, value: bool): _logger.debug('RevOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and self.rev_en: if value and self._run_allowed(): self._run_rev_drv() - self.attributes['RevOp'].value = False + self.attributes['RevOp'].set_value(False) def set_stop_op(self, value: bool): _logger.debug('StopOp set to %s' % value) @@ -1192,7 +1252,8 @@ def monitor_static_error(self): if self.monitored_values.stop_event_lock.is_set(): _logger.debug('static monitoring stopped') break - states, control_signals = self.compare_states_control_signals(self.attributes['MonStatTi'].value) + states, control_signals = self.compare_states_control_signals( + self.attributes['MonStatTi'].value) if not all(states): if all(control_signals): @@ -1205,7 +1266,8 @@ def monitor_dynamic_error(self): if self.monitored_values.stop_event_lock.is_set(): _logger.debug('dynamic monitoring stopped') break - states, control_signals = self.compare_states_control_signals(self.attributes['MonDynTi'].value) + states, control_signals = self.compare_states_control_signals( + self.attributes['MonDynTi'].value) if all(states): if not all(control_signals): @@ -1236,7 +1298,7 @@ def set_fwd_op(self, value: bool): self.monitored_values.lock.acquire() self.monitored_values.fwd_op = value self.monitored_values.lock.release() - self.attributes['FwdOp'].value = False + self.attributes['FwdOp'].set_value(False) def set_rev_op(self, value: bool): _logger.debug('RevOp set to %s' % value) @@ -1248,7 +1310,7 @@ def set_rev_op(self, value: bool): self.monitored_values.rev_op = value self.monitored_values.lock.release() self._run_rev_drv() - self.attributes['RevOp'].value = False + self.attributes['RevOp'].set_value(False) def set_stop_op(self, value: bool): _logger.debug('StopOp set to %s' % value) @@ -1412,14 +1474,14 @@ def set_fwd_op(self, value: bool): if self.op_src_mode.attributes['StateOpAct'].value and self.fwd_en: if value and self._run_allowed(): self._run_fwd_drv() - self.attributes['FwdOp'].value = False + self.attributes['FwdOp'].set_value(False) def set_rev_op(self, value: bool): _logger.debug('RevOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and self.rev_en: if value and self._run_allowed(): self._run_rev_drv() - self.attributes['RevOp'].value = False + self.attributes['RevOp'].set_value(False) def set_stop_op(self, value: bool): _logger.debug('StopOp set to %s' % value) @@ -1680,7 +1742,8 @@ def monitor_static_error(self): if self.monitored_values.stop_event_lock.is_set(): _logger.debug('static monitoring stopped') break - states, control_signals = self.compare_states_control_signals(self.attributes['MonStatTi'].value) + states, control_signals = self.compare_states_control_signals( + self.attributes['MonStatTi'].value) if not all(states): if all(control_signals): @@ -1693,7 +1756,8 @@ def monitor_dynamic_error(self): if self.monitored_values.stop_event_lock.is_set(): _logger.debug('dynamic monitoring stopped') break - states, control_signals = self.compare_states_control_signals(self.attributes['MonDynTi'].value) + states, control_signals = self.compare_states_control_signals( + self.attributes['MonDynTi'].value) if all(states): if not all(control_signals): @@ -1760,7 +1824,8 @@ def start_monitor(self): _logger.debug('rpm error monitoring start') if self.attributes['RpmAHEn'].value: - self.monitor_rpm_limit_high_thread = threading.Thread(target=self.monitor_rpm_high_limit) + self.monitor_rpm_limit_high_thread = threading.Thread( + target=self.monitor_rpm_high_limit) self.monitor_rpm_limit_high_thread.start() _logger.debug('rpm high limit monitoring start') @@ -1778,7 +1843,7 @@ def set_fwd_op(self, value: bool): self.monitored_values.lock.acquire() self.monitored_values.fwd_op = value self.monitored_values.lock.release() - self.attributes['FwdOp'].value = False + self.attributes['FwdOp'].set_value(False) def set_rev_op(self, value: bool): _logger.debug('RevOp set to %s' % value) @@ -1790,7 +1855,7 @@ def set_rev_op(self, value: bool): self.monitored_values.rev_op = value self.monitored_values.lock.release() self._run_rev_drv() - self.attributes['RevOp'].value = False + self.attributes['RevOp'].set_value(False) def set_stop_op(self, value: bool): _logger.debug('StopOp set to %s' % value) @@ -1886,7 +1951,8 @@ def __init__(self, mv_init_value=0, kp=100, ki=10, kd=1, mv_min=0, mv_max=100, s self.mv = mv_init_value self.sample_time = sample_time - self.ctrl = PID(Kp=self.kp, Ki=self.ki, Kd=self.kd, output_limits=(mv_min, mv_max), sample_time=sample_time) + self.ctrl = PID(Kp=self.kp, Ki=self.ki, Kd=self.kd, + output_limits=(mv_min, mv_max), sample_time=sample_time) self.thread = threading.Thread(target=self.loop) self.stop_flag = True @@ -1975,8 +2041,10 @@ def __init__(self, tag_name, tag_description='', self._add_attribute(Attribute('PVSclMin', float, init_value=pv_scl_min)) self._add_attribute(Attribute('PVSclMax', float, init_value=pv_scl_max)) self._add_attribute(Attribute('PVUnit', int, init_value=pv_unit)) - self._add_attribute(Attribute('SPMan', float, init_value=sp_man_min, sub_cb=self.set_sp_man)) - self._add_attribute(Attribute('SPInt', float, init_value=sp_int_min, sub_cb=self.set_sp_int)) + self._add_attribute( + Attribute('SPMan', float, init_value=sp_man_min, sub_cb=self.set_sp_man)) + self._add_attribute( + Attribute('SPInt', float, init_value=sp_int_min, sub_cb=self.set_sp_int)) self._add_attribute(Attribute('SPSclMin', float, init_value=sp_scl_min)) self._add_attribute(Attribute('SPSclMax', float, init_value=sp_scl_max)) self._add_attribute(Attribute('SPUnit', int, init_value=sp_unit)) From a898f0b0cbe98f6e56f2a2becbd370a3766aeb07 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Fri, 29 Aug 2025 16:38:47 +0200 Subject: [PATCH 33/61] Fix for StateChannel and SrcChannel not being set. Also insured correct handshake for Op commands --- src/mtppy/operation_source_mode.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/mtppy/operation_source_mode.py b/src/mtppy/operation_source_mode.py index 981aebe..6bf1578 100644 --- a/src/mtppy/operation_source_mode.py +++ b/src/mtppy/operation_source_mode.py @@ -180,6 +180,9 @@ def _update_linked_op_src_modes(self, attribute_name: str, value: bool): def set_state_channel(self, value: bool): _logger.debug(f'{self._name_of_parent}Operation mode channel is now %s' % value) + if self.attributes['StateChannel'].value == value: + return + self.attributes['StateChannel'].set_value(value) self._update_linked_op_src_modes('StateChannel', value) def set_state_aut_aut(self, value: bool): @@ -194,7 +197,8 @@ def set_state_aut_op(self, value: bool): if not self.attributes['StateChannel'].value and value: if self.attributes['StateOffAct'].value or self.attributes['StateOpAct'].value: self._opmode_to_aut() - self.attributes['StateAutOp'].set_value(False) + if value: + self.attributes['StateAutOp'].set_value(False) self._update_linked_op_src_modes('StateAutOp', value) def set_state_off_aut(self, value: bool): @@ -209,7 +213,8 @@ def set_state_off_op(self, value: bool): if not self.attributes['StateChannel'].value and value and self.switch_to_offline_mode_allowed: if self.attributes['StateAutAct'].value or self.attributes['StateOpAct'].value: self._opmode_to_off() - self.attributes['StateOffOp'].set_value(False) + if value: + self.attributes['StateOffOp'].set_value(False) self._update_linked_op_src_modes('StateOffOp', value) def set_state_op_aut(self, value: bool): @@ -224,7 +229,8 @@ def set_state_op_op(self, value: bool): if not self.attributes['StateChannel'].value and value: if self.attributes['StateOffAct'].value or self.attributes['StateAutAct'].value: self._opmode_to_op() - self.attributes['StateOpOp'].set_value(False) + if value: + self.attributes['StateOpOp'].set_value(False) self._update_linked_op_src_modes('StateOpOp', value) def _src_to_off(self): @@ -244,6 +250,9 @@ def _src_to_ext(self): def set_src_channel(self, value: bool): _logger.debug(f'{self._name_of_parent}Source mode channel is now %s' % value) + if self.attributes['SrcChannel'].value == value: + return + self.attributes['SrcChannel'].set_value(value) self._update_linked_op_src_modes('SrcChannel', value) def set_src_ext_aut(self, value: bool): @@ -256,7 +265,8 @@ def set_src_ext_op(self, value: bool): if not self.attributes['StateOffAct'].value and value: if not self.attributes['SrcChannel'].value: self._src_to_ext() - self.attributes['SrcExtOp'].set_value(False) + if value: + self.attributes['SrcExtOp'].set_value(False) self._update_linked_op_src_modes('SrcExtOp', value) def set_src_int_aut(self, value: bool): @@ -269,7 +279,8 @@ def set_src_int_op(self, value: bool): if not self.attributes['StateOffAct'].value and value: if not self.attributes['SrcChannel'].value: self._src_to_int() - self.attributes['SrcIntOp'].set_value(False) + if value: + self.attributes['SrcIntOp'].set_value(False) self._update_linked_op_src_modes('SrcIntOp', value) @@ -327,4 +338,5 @@ def set_src_man_op(self, value: bool): if not self.attributes['StateOffAct'].value and value: if not self.attributes['SrcChannel'].value: self._src_to_man() - self.attributes['SrcManOp'].set_value(False) + if value: + self.attributes['SrcManOp'].set_value(False) From 9198b57476685b40b641b2d64a461adaf28d7d79 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Fri, 29 Aug 2025 16:56:58 +0200 Subject: [PATCH 34/61] don't updated linked op_src_modes for False for Op Commands --- src/mtppy/operation_source_mode.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/mtppy/operation_source_mode.py b/src/mtppy/operation_source_mode.py index 6bf1578..6e87a97 100644 --- a/src/mtppy/operation_source_mode.py +++ b/src/mtppy/operation_source_mode.py @@ -170,8 +170,6 @@ def _update_linked_op_src_modes(self, attribute_name: str, value: bool): """ if self.linked_op_src_modes == []: return - # if value is False: - # return _logger.debug( f'{self._name_of_parent}Updating linked op_src_modes for attribute {attribute_name} to {value}') linked_op_src_mode: OperationSourceMode @@ -199,7 +197,8 @@ def set_state_aut_op(self, value: bool): self._opmode_to_aut() if value: self.attributes['StateAutOp'].set_value(False) - self._update_linked_op_src_modes('StateAutOp', value) + else: + self._update_linked_op_src_modes('StateAutOp', value) def set_state_off_aut(self, value: bool): _logger.debug(f'{self._name_of_parent}StateOffAut set to {value}') @@ -215,7 +214,8 @@ def set_state_off_op(self, value: bool): self._opmode_to_off() if value: self.attributes['StateOffOp'].set_value(False) - self._update_linked_op_src_modes('StateOffOp', value) + else: + self._update_linked_op_src_modes('StateOffOp', value) def set_state_op_aut(self, value: bool): _logger.debug(f'{self._name_of_parent}StateOpAut set to {value}') @@ -231,7 +231,8 @@ def set_state_op_op(self, value: bool): self._opmode_to_op() if value: self.attributes['StateOpOp'].set_value(False) - self._update_linked_op_src_modes('StateOpOp', value) + else: + self._update_linked_op_src_modes('StateOpOp', value) def _src_to_off(self): self.attributes['SrcIntAct'].set_value(False) @@ -267,7 +268,8 @@ def set_src_ext_op(self, value: bool): self._src_to_ext() if value: self.attributes['SrcExtOp'].set_value(False) - self._update_linked_op_src_modes('SrcExtOp', value) + else: + self._update_linked_op_src_modes('SrcExtOp', value) def set_src_int_aut(self, value: bool): if not self.attributes['StateOffAct'].value and value: @@ -281,7 +283,8 @@ def set_src_int_op(self, value: bool): self._src_to_int() if value: self.attributes['SrcIntOp'].set_value(False) - self._update_linked_op_src_modes('SrcIntOp', value) + else: + self._update_linked_op_src_modes('SrcIntOp', value) class OperationSourceModeActiveElements(OperationSourceMode): From 935f2318817f38535b83ffcba9a2c1fea7791150 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Fri, 29 Aug 2025 17:04:03 +0200 Subject: [PATCH 35/61] fix for previous commit --- src/mtppy/operation_source_mode.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/mtppy/operation_source_mode.py b/src/mtppy/operation_source_mode.py index 6e87a97..89bcff1 100644 --- a/src/mtppy/operation_source_mode.py +++ b/src/mtppy/operation_source_mode.py @@ -197,7 +197,6 @@ def set_state_aut_op(self, value: bool): self._opmode_to_aut() if value: self.attributes['StateAutOp'].set_value(False) - else: self._update_linked_op_src_modes('StateAutOp', value) def set_state_off_aut(self, value: bool): @@ -214,7 +213,6 @@ def set_state_off_op(self, value: bool): self._opmode_to_off() if value: self.attributes['StateOffOp'].set_value(False) - else: self._update_linked_op_src_modes('StateOffOp', value) def set_state_op_aut(self, value: bool): @@ -231,7 +229,6 @@ def set_state_op_op(self, value: bool): self._opmode_to_op() if value: self.attributes['StateOpOp'].set_value(False) - else: self._update_linked_op_src_modes('StateOpOp', value) def _src_to_off(self): @@ -268,7 +265,6 @@ def set_src_ext_op(self, value: bool): self._src_to_ext() if value: self.attributes['SrcExtOp'].set_value(False) - else: self._update_linked_op_src_modes('SrcExtOp', value) def set_src_int_aut(self, value: bool): @@ -283,7 +279,6 @@ def set_src_int_op(self, value: bool): self._src_to_int() if value: self.attributes['SrcIntOp'].set_value(False) - else: self._update_linked_op_src_modes('SrcIntOp', value) From 050e866123f7f20f55151f8ef9783efefbfc49d8 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Fri, 29 Aug 2025 17:17:41 +0200 Subject: [PATCH 36/61] Renamed operation_elements to parameter_elements --- examples/virtual_pea_minimal.py | 2 +- examples/virtual_pea_with_recipe.py | 2 +- src/mtppy/mtp_generator.py | 3 +++ .../{operation_elements.py => parameter_elements.py} | 10 +++++----- src/mtppy/procedure.py | 10 +++++----- src/mtppy/service.py | 6 +++--- src/mtppy/suc_data_assembly.py | 2 +- .../test_mtp_generator_instance_hierarchy_services.py | 2 +- tests/test_mtp_generator_instance_list.py | 2 +- tests/test_operation_source_mode.py | 2 +- tests/test_procedure.py | 2 +- 11 files changed, 23 insertions(+), 20 deletions(-) rename src/mtppy/{operation_elements.py => parameter_elements.py} (97%) diff --git a/examples/virtual_pea_minimal.py b/examples/virtual_pea_minimal.py index 8e9f797..3ff8b41 100644 --- a/examples/virtual_pea_minimal.py +++ b/examples/virtual_pea_minimal.py @@ -2,7 +2,7 @@ from mtppy.mtp_generator import MTPGenerator from mtppy.service import Service from mtppy.procedure import Procedure -from mtppy.operation_elements import * +from mtppy.parameter_elements import * from mtppy.indicator_elements import * from mtppy.active_elements import * diff --git a/examples/virtual_pea_with_recipe.py b/examples/virtual_pea_with_recipe.py index 903fcd0..4527dcf 100644 --- a/examples/virtual_pea_with_recipe.py +++ b/examples/virtual_pea_with_recipe.py @@ -2,7 +2,7 @@ from mtppy.mtp_generator import MTPGenerator from mtppy.service import Service from mtppy.procedure import Procedure -from mtppy.operation_elements import * +from mtppy.parameter_elements import * from mtppy.indicator_elements import * import time diff --git a/src/mtppy/mtp_generator.py b/src/mtppy/mtp_generator.py index 838a383..22bbcf0 100644 --- a/src/mtppy/mtp_generator.py +++ b/src/mtppy/mtp_generator.py @@ -157,6 +157,9 @@ def add_data_assembly_to_instance_list(self, instance_name: str, instance_id: st elif instance_basic_type == 'SUCActiveElement': name = instance_name reference_path = 'MTPDataObjectSUCLib/DataAssembly/ActiveElement/' + instance_type_name + elif instance_basic_type == 'SUCParameterElement': + name = instance_name + reference_path = 'MTPDataObjectSUCLib/DataAssembly/ParameterElement/' + instance_type_name else: raise TypeError('data assembly type error') diff --git a/src/mtppy/operation_elements.py b/src/mtppy/parameter_elements.py similarity index 97% rename from src/mtppy/operation_elements.py rename to src/mtppy/parameter_elements.py index 7f7a4ce..6cca751 100644 --- a/src/mtppy/operation_elements.py +++ b/src/mtppy/parameter_elements.py @@ -2,12 +2,12 @@ from mtppy.attribute import Attribute from mtppy.operation_source_mode import OperationSourceMode -from mtppy.suc_data_assembly import SUCOperationElement +from mtppy.suc_data_assembly import SUCParameterElement _logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") -class AnaServParam(SUCOperationElement): +class AnaServParam(SUCParameterElement): def __init__(self, tag_name: str, tag_description: str = '', v_min: float = 0, v_max: float = 100, v_scl_min: float = 0, v_scl_max: float = 100, v_unit: int = 0, init_value: float = None): """ @@ -80,7 +80,7 @@ def set_v_fbk(self, value): _logger.debug('VFbk set to %s' % value) -class BinServParam(SUCOperationElement): +class BinServParam(SUCParameterElement): def __init__(self, tag_name: str, tag_description: str = '', v_state_0: str = 'false', v_state_1: str = 'true', init_value: bool = False): """ Binary Service Parameter (BinServParam). Parameter names correspond attribute names in VDI/VDE/NAMUR 2658. @@ -135,7 +135,7 @@ def set_v_fbk(self, value): _logger.debug('VFbk set to %s' % value) -class DIntServParam(SUCOperationElement): +class DIntServParam(SUCParameterElement): def __init__(self, tag_name: str, tag_description: str = '', v_min: int = 0, v_max: int = 100, v_scl_min: int = 0, v_scl_max: int = 100, v_unit: int = 0, init_value: int = None): """ @@ -210,7 +210,7 @@ def set_v_fbk(self, value): _logger.debug('VFbk set to %s' % value) -class StringServParam(SUCOperationElement): +class StringServParam(SUCParameterElement): def __init__(self, tag_name: str, tag_description: str = '', init_value: str = ''): """ String Service Parameter (StringServParam). Parameter names correspond attribute names in VDI/VDE/NAMUR 2658. diff --git a/src/mtppy/procedure.py b/src/mtppy/procedure.py index c97e5bf..16d3c9d 100644 --- a/src/mtppy/procedure.py +++ b/src/mtppy/procedure.py @@ -21,22 +21,22 @@ def __init__(self, procedure_id: int, tag_name: str, tag_description: str = '', if procedure_id <= 0: raise ValueError(f"{tag_name}: Procedure ID can't be equal or less than 0.") super().__init__(procedure_id, tag_name, tag_description, is_self_completing, is_default) - self.procedure_parameters: dict[str, SUCOperationElement] = {} + self.procedure_parameters: dict[str, SUCParameterElement] = {} self.process_value_ins = {} self.report_values: dict[str, SUCIndicatorElement] = {} self.process_value_outs: dict[str, SUCIndicatorElement] = {} - def add_procedure_parameter(self, procedure_parameter: SUCOperationElement): + def add_procedure_parameter(self, procedure_parameter: SUCParameterElement): """ Adds a procedure parameter to the procedure. Args: - procedure_parameter (SUCOperationElement): Procedure parameter. + procedure_parameter (SUCParameterElement): Procedure parameter. Raises: - TypeError: If procedure_parameter is not an instance of SUCOperationElement. + TypeError: If procedure_parameter is not an instance of SUCParameterElement. """ - if isinstance(procedure_parameter, SUCOperationElement): + if isinstance(procedure_parameter, SUCParameterElement): self.procedure_parameters[procedure_parameter.tag_name] = procedure_parameter else: raise TypeError() diff --git a/src/mtppy/service.py b/src/mtppy/service.py index d52ae8d..96e6a21 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -12,7 +12,7 @@ from mtppy.procedure_control import ProcedureControl from mtppy.state_codes import StateCodes from mtppy.procedure import Procedure -from mtppy.suc_data_assembly import SUCOperationElement +from mtppy.suc_data_assembly import SUCParameterElement _logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") @@ -182,12 +182,12 @@ def state_change(self): """ self.state_machine.state_change() - def add_configuration_parameter(self, configuration_parameter: SUCOperationElement): + def add_configuration_parameter(self, configuration_parameter: SUCParameterElement): """ Adds a configuration parameter to the service. Args: - configuration_parameter (SUCOperationElement): Configuration parameter to add. + configuration_parameter (SUCParameterElement): Configuration parameter to add. """ self.configuration_parameters[configuration_parameter.tag_name] = configuration_parameter diff --git a/src/mtppy/suc_data_assembly.py b/src/mtppy/suc_data_assembly.py index d5774fa..da1d1e9 100644 --- a/src/mtppy/suc_data_assembly.py +++ b/src/mtppy/suc_data_assembly.py @@ -29,7 +29,7 @@ def set_v(self, value): pass -class SUCOperationElement(SUCDataAssembly): +class SUCParameterElement(SUCDataAssembly): def __init__(self, tag_name: str, tag_description: str): super().__init__(tag_name, tag_description) self._add_attribute(Attribute('OSLevel', int, init_value=0)) diff --git a/tests/test_mtp_generator_instance_hierarchy_services.py b/tests/test_mtp_generator_instance_hierarchy_services.py index f52b1db..97d2413 100644 --- a/tests/test_mtp_generator_instance_hierarchy_services.py +++ b/tests/test_mtp_generator_instance_hierarchy_services.py @@ -7,7 +7,7 @@ from mtppy.mtp_generator import MTPGenerator from mtppy.service import Service from mtppy.procedure import Procedure -from mtppy.operation_elements import * +from mtppy.parameter_elements import * from mtppy.indicator_elements import * from mtppy.active_elements import * diff --git a/tests/test_mtp_generator_instance_list.py b/tests/test_mtp_generator_instance_list.py index b208ec0..62ed396 100644 --- a/tests/test_mtp_generator_instance_list.py +++ b/tests/test_mtp_generator_instance_list.py @@ -6,7 +6,7 @@ from mtppy.mtp_generator import MTPGenerator from mtppy.service import Service from mtppy.procedure import Procedure -from mtppy.operation_elements import * +from mtppy.parameter_elements import * from mtppy.indicator_elements import * from mtppy.active_elements import * import xml.etree.ElementTree as ET diff --git a/tests/test_operation_source_mode.py b/tests/test_operation_source_mode.py index 5babc2e..b29b011 100644 --- a/tests/test_operation_source_mode.py +++ b/tests/test_operation_source_mode.py @@ -1,4 +1,4 @@ -from src.mtppy.operation_elements import OperationSourceMode +from mtppy.parameter_elements import OperationSourceMode def init_op_source_mode(op_mode='off', diff --git a/tests/test_procedure.py b/tests/test_procedure.py index 7719e12..c720364 100644 --- a/tests/test_procedure.py +++ b/tests/test_procedure.py @@ -1,5 +1,5 @@ from src.mtppy.procedure import Procedure -from src.mtppy.operation_elements import * +from mtppy.parameter_elements import * from src.mtppy.indicator_elements import * operation_elements = [AnaServParam('serv_param_ana', v_min=0, v_max=50, v_scl_min=0, v_scl_max=10, v_unit=23), From 3a0eec36a0a35119df1fbf6de0a7a3d7ec8d45f3 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Fri, 29 Aug 2025 19:01:50 +0200 Subject: [PATCH 37/61] added real operation_elements --- src/mtppy/operation_elements.py | 258 +++++++++++++++++++++++++++++ src/mtppy/operation_source_mode.py | 63 +++++++ src/mtppy/suc_data_assembly.py | 41 +---- 3 files changed, 327 insertions(+), 35 deletions(-) create mode 100644 src/mtppy/operation_elements.py diff --git a/src/mtppy/operation_elements.py b/src/mtppy/operation_elements.py new file mode 100644 index 0000000..316683e --- /dev/null +++ b/src/mtppy/operation_elements.py @@ -0,0 +1,258 @@ +import logging + +from mtppy.attribute import Attribute +from mtppy.operation_source_mode import OperationSourceModeOperationElements +from mtppy.suc_data_assembly import SUCOperationElement + + +_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") + + +class AnaMan(SUCOperationElement): + """ + Analog Operation Element (AnaMan). + Table 32 from VDI/VDE/NAMUR 2658-3. + """ + + def __init__(self, tag_name: str, tag_description: str = '', + v_min: float = 0.0, v_max: float = 100.0, + v_scl_min: float = 0.0, v_scl_max: float = 100.0, + v_unit: int = 0, init_value: float = 0.0): + super().__init__(tag_name, tag_description) + + self.v_min = v_min + self.v_max = v_max + self.v_scl_min = v_scl_min + self.v_scl_max = v_scl_max + self.v_unit = v_unit + + # Attributes with callbacks + self._add_attribute(Attribute('VOut', float, init_value=init_value)) + self._add_attribute(Attribute('VSclMin', float, init_value=v_scl_min)) + self._add_attribute(Attribute('VSclMax', float, init_value=v_scl_max)) + self._add_attribute(Attribute('VUnit', int, init_value=v_unit)) + self._add_attribute(Attribute('VMan', float, init_value=init_value, sub_cb=self.set_v_man)) + self._add_attribute(Attribute('VMin', float, init_value=v_min)) + self._add_attribute(Attribute('VMax', float, init_value=v_max)) + self._add_attribute(Attribute('VRbk', float, init_value=init_value)) + self._add_attribute(Attribute('VFbk', float, init_value=init_value)) + + def valid_value(self, value: float) -> bool: + return self.v_min <= value <= self.v_max + + def set_v_man(self, value: float): + _logger.debug(f"VMan set to {value}") + if self.valid_value(value): + self.set_v_out(value) + else: + _logger.warning(f"VMan {value} out of range ({self.v_min} - {self.v_max})") + + def set_v_out(self, value: float): + self.attributes['VOut'].set_value(value) + self.set_v_rbk(value) + self.set_v_fbk(value) + _logger.debug(f"VOut set to {value}") + + def set_v_rbk(self, value: float): + self.attributes['VRbk'].set_value(value) + _logger.debug(f"VRbk set to {value}") + + def set_v_fbk(self, value: float): + self.attributes['VFbk'].set_value(value) + _logger.debug(f"VFbk set to {value}") + + def get_v_out(self) -> float: + return self.attributes['VOut'].value + + +class AnaManInt(AnaMan): + """ + Analog Operation Element with Internal Setpoint and SourceMode (AnaManInt). + Table 33 from VDI/VDE/NAMUR 2658-3. + """ + + def __init__(self, tag_name: str, tag_description: str = '', + v_min: float = 0.0, v_max: float = 100.0, + v_scl_min: float = 0.0, v_scl_max: float = 100.0, + v_unit: int = 0, init_value: float = 0.0): + super().__init__(tag_name, tag_description, + v_min=v_min, v_max=v_max, + v_scl_min=v_scl_min, v_scl_max=v_scl_max, + v_unit=v_unit, init_value=init_value) + + self.op_src_mode = OperationSourceModeOperationElements() + + # Extensions (Table 33) + self._add_attribute(Attribute('WQC', int, init_value=0)) + self._add_attribute(Attribute('VInt', float, init_value=init_value, sub_cb=self.set_v_int)) + + def set_v_man(self, value: float): + _logger.debug(f"VMan set to {value}") + if self.op_src_mode.attributes['SrcManAct'].value and self.valid_value(value): + self.set_v_out(value) + + def set_v_int(self, value: float): + _logger.debug(f"VInt set to {value}") + if self.op_src_mode.attributes['SrcIntAct'].value and self.valid_value(value): + self.set_v_out(value) + + +class DIntMan(SUCOperationElement): + """ + Discrete Integer Operation Element (DIntMan). + Table 34 from VDI/VDE/NAMUR 2658-3. + """ + + def __init__(self, tag_name: str, tag_description: str = '', + v_min: int = 0, v_max: int = 100, + v_scl_min: int = 0, v_scl_max: int = 100, + v_unit: int = 0, init_value: int = 0): + super().__init__(tag_name, tag_description) + + self.v_min = v_min + self.v_max = v_max + self.v_scl_min = v_scl_min + self.v_scl_max = v_scl_max + self.v_unit = v_unit + + self._add_attribute(Attribute('VOut', int, init_value=init_value)) + self._add_attribute(Attribute('VSclMin', int, init_value=v_scl_min)) + self._add_attribute(Attribute('VSclMax', int, init_value=v_scl_max)) + self._add_attribute(Attribute('VUnit', int, init_value=v_unit)) + self._add_attribute(Attribute('VMan', int, init_value=init_value, sub_cb=self.set_v_man)) + self._add_attribute(Attribute('VMin', int, init_value=v_min)) + self._add_attribute(Attribute('VMax', int, init_value=v_max)) + self._add_attribute(Attribute('VRbk', int, init_value=init_value)) + self._add_attribute(Attribute('VFbk', int, init_value=init_value)) + + def valid_value(self, value: int) -> bool: + return self.v_min <= value <= self.v_max + + def set_v_man(self, value: int): + _logger.debug(f"VMan set to {value}") + if self.valid_value(value): + self.set_v_out(value) + else: + _logger.warning(f"VMan {value} out of range ({self.v_min} - {self.v_max})") + + def set_v_out(self, value: int): + self.attributes['VOut'].set_value(value) + self.set_v_rbk(value) + self.set_v_fbk(value) + _logger.debug(f"VOut set to {value}") + + def set_v_rbk(self, value: int): + self.attributes['VRbk'].set_value(value) + _logger.debug(f"VRbk set to {value}") + + def set_v_fbk(self, value: int): + self.attributes['VFbk'].set_value(value) + _logger.debug(f"VFbk set to {value}") + + def get_v_out(self) -> int: + return self.attributes['VOut'].value + + +class DIntManInt(DIntMan): + """ + Integer Operation Element with Internal Setpoint and SourceMode (DIntManInt). + Table 35 from VDI/VDE/NAMUR 2658-3. + """ + + def __init__(self, tag_name: str, tag_description: str = '', + v_min: int = 0, v_max: int = 100, + v_scl_min: int = 0, v_scl_max: int = 100, + v_unit: int = 0, init_value: int = 0): + super().__init__(tag_name, tag_description, + v_min=v_min, v_max=v_max, + v_scl_min=v_scl_min, v_scl_max=v_scl_max, + v_unit=v_unit, init_value=init_value) + + self.op_src_mode = OperationSourceModeOperationElements() + + # Extensions (Table 35) + self._add_attribute(Attribute('WQC', int, init_value=0)) + self._add_attribute(Attribute('VInt', int, init_value=init_value, sub_cb=self.set_v_int)) + + def set_v_man(self, value: int): + _logger.debug(f"VMan set to {value}") + if self.op_src_mode.attributes['SrcManAct'].value and self.valid_value(value): + self.set_v_out(value) + + def set_v_int(self, value: int): + _logger.debug(f"VInt set to {value}") + if self.op_src_mode.attributes['SrcIntAct'].value and self.valid_value(value): + self.set_v_out(value) + + +class BinMan(SUCOperationElement): + """ + Binary Operation Element (BinMan). + Table 36 from VDI/VDE/NAMUR 2658-3. + """ + + def __init__(self, tag_name: str, tag_description: str = '', + v_state0: str = 'Off', v_state1: str = 'On', + init_value: bool = False): + super().__init__(tag_name, tag_description) + + self.v_state0 = v_state0 + self.v_state1 = v_state1 + + self._add_attribute(Attribute('VOut', bool, init_value=init_value)) + self._add_attribute(Attribute('VState0', str, init_value=v_state0)) + self._add_attribute(Attribute('VState1', str, init_value=v_state1)) + self._add_attribute(Attribute('VMan', bool, init_value=init_value, sub_cb=self.set_v_man)) + self._add_attribute(Attribute('VRbk', bool, init_value=init_value)) + self._add_attribute(Attribute('VFbk', bool, init_value=init_value)) + + def set_v_man(self, value: bool): + _logger.debug(f"VMan set to {value}") + self.set_v_out(value) + + def set_v_out(self, value: bool): + self.attributes['VOut'].set_value(value) + self.set_v_rbk(value) + self.set_v_fbk(value) + _logger.debug(f"VOut set to {value}") + + def set_v_rbk(self, value: bool): + self.attributes['VRbk'].set_value(value) + _logger.debug(f"VRbk set to {value}") + + def set_v_fbk(self, value: bool): + self.attributes['VFbk'].set_value(value) + _logger.debug(f"VFbk set to {value}") + + def get_v_out(self) -> bool: + return self.attributes['VOut'].value + + +class BinManInt(BinMan): + """ + Binary Operation Element with Internal Setpoint and SourceMode (BinManInt). + Table 37 from VDI/VDE/NAMUR 2658-3. + """ + + def __init__(self, tag_name: str, tag_description: str = '', + v_state0: str = 'Off', v_state1: str = 'On', + init_value: bool = False): + super().__init__(tag_name, tag_description, + v_state0=v_state0, v_state1=v_state1, + init_value=init_value) + + self.op_src_mode = OperationSourceModeOperationElements() + + # Extensions (Table 37) + self._add_attribute(Attribute('WQC', int, init_value=0)) + self._add_attribute(Attribute('VInt', bool, init_value=init_value, sub_cb=self.set_v_int)) + + def set_v_man(self, value: bool): + _logger.debug(f"VMan set to {value}") + if self.op_src_mode.attributes['SrcManAct'].value: + self.set_v_out(value) + + def set_v_int(self, value: bool): + _logger.debug(f"VInt set to {value}") + if self.op_src_mode.attributes['SrcIntAct'].value: + self.set_v_out(value) diff --git a/src/mtppy/operation_source_mode.py b/src/mtppy/operation_source_mode.py index 89bcff1..2aa5449 100644 --- a/src/mtppy/operation_source_mode.py +++ b/src/mtppy/operation_source_mode.py @@ -338,3 +338,66 @@ def set_src_man_op(self, value: bool): self._src_to_man() if value: self.attributes['SrcManOp'].set_value(False) + + +class OperationSourceModeOperationElements(): + def __init__(self, name_of_parent: str = ''): + """ + Represents the operation and source mode control for operation elements + (Table 35 VDI/VDE/NAMUR 2658-3). + """ + self.attributes = { + # --- Source mode control --- + 'SrcChannel': Attribute('SrcChannel', bool, init_value=False, sub_cb=self.set_src_channel), + 'SrcManAut': Attribute('SrcManAut', bool, init_value=False, sub_cb=self.set_src_man_aut), + 'SrcIntAut': Attribute('SrcIntAut', bool, init_value=False, sub_cb=self.set_src_int_aut), + 'SrcIntOp': Attribute('SrcIntOp', bool, init_value=False, sub_cb=self.set_src_int_op), + 'SrcManOp': Attribute('SrcManOp', bool, init_value=False, sub_cb=self.set_src_man_op), + + 'SrcIntAct': Attribute('SrcIntAct', bool, init_value=False), + 'SrcManAct': Attribute('SrcManAct', bool, init_value=False) + } + + self._name_of_parent = f"{name_of_parent}: " if name_of_parent != '' else '' + + # --- Source mode transitions --- + def _src_to_int(self): + self.attributes['SrcIntAct'].set_value(True) + self.attributes['SrcManAct'].set_value(False) + _logger.debug('Source mode is now int') + + def _src_to_man(self): + self.attributes['SrcIntAct'].set_value(False) + self.attributes['SrcManAct'].set_value(True) + _logger.debug('Source mode is now man') + + # --- Callbacks for source control --- + def set_src_channel(self, value: bool): + _logger.debug('Source mode channel is now %s' % value) + if self.attributes['SrcChannel'].value == value: + return + self.attributes['SrcChannel'].set_value(value) + + def set_src_man_aut(self, value: bool): + if not self.attributes['StateOffAct'].value and value: + if self.attributes['SrcChannel'].value: + self._src_to_man() + + def set_src_int_aut(self, value: bool): + if not self.attributes['StateOffAct'].value and value: + if self.attributes['SrcChannel'].value: + self._src_to_int() + + def set_src_int_op(self, value: bool): + if not self.attributes['StateOffAct'].value and value: + if not self.attributes['SrcChannel'].value: + self._src_to_int() + if value: + self.attributes['SrcIntOp'].set_value(False) + + def set_src_man_op(self, value: bool): + if not self.attributes['StateOffAct'].value and value: + if not self.attributes['SrcChannel'].value: + self._src_to_man() + if value: + self.attributes['SrcManOp'].set_value(False) diff --git a/src/mtppy/suc_data_assembly.py b/src/mtppy/suc_data_assembly.py index da1d1e9..2a851f7 100644 --- a/src/mtppy/suc_data_assembly.py +++ b/src/mtppy/suc_data_assembly.py @@ -29,19 +29,18 @@ def set_v(self, value): pass +class SUCOperationElement(SUCDataAssembly): + def __init__(self, tag_name: str, tag_description: str): + super().__init__(tag_name, tag_description) + self._add_attribute(Attribute('OSLevel', int, init_value=0)) + + class SUCParameterElement(SUCDataAssembly): def __init__(self, tag_name: str, tag_description: str): super().__init__(tag_name, tag_description) self._add_attribute(Attribute('OSLevel', int, init_value=0)) self._add_attribute(Attribute('WQC', int, init_value=255)) - @abstractmethod - def set_v_op(self, value): - """ - Set Parameter value if StateOpAct. - """ - pass - @abstractmethod def set_v_int(self, value): """ @@ -49,34 +48,6 @@ def set_v_int(self, value): """ pass - @abstractmethod - def set_v_ext(self, value): - """ - Set Parameter value if StateExAct. - """ - pass - - @abstractmethod - def valid_value(self, value): - """ - Validate if the value is within the acceptable range. - """ - pass - - @abstractmethod - def set_v_req(self, value): - """ - Set VReq (requested parameter value) to the specified value if valid. - """ - pass - - @abstractmethod - def set_v_out(self): - """ - Set VOut (current parameter value) based on VReq and update feedback. - """ - pass - @abstractmethod def get_v_out(self): """ From 7cf048830a700221335d217f772542e9db9104d1 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Mon, 1 Sep 2025 12:09:11 +0200 Subject: [PATCH 38/61] bugfix to OperationSourceModeOperationElements --- src/mtppy/operation_source_mode.py | 32 +++++++++++++----------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/mtppy/operation_source_mode.py b/src/mtppy/operation_source_mode.py index 2aa5449..b8e09a4 100644 --- a/src/mtppy/operation_source_mode.py +++ b/src/mtppy/operation_source_mode.py @@ -348,9 +348,9 @@ def __init__(self, name_of_parent: str = ''): """ self.attributes = { # --- Source mode control --- - 'SrcChannel': Attribute('SrcChannel', bool, init_value=False, sub_cb=self.set_src_channel), + 'SrcChannel': Attribute('SrcChannel', bool, init_value=True, sub_cb=self.set_src_channel), 'SrcManAut': Attribute('SrcManAut', bool, init_value=False, sub_cb=self.set_src_man_aut), - 'SrcIntAut': Attribute('SrcIntAut', bool, init_value=False, sub_cb=self.set_src_int_aut), + 'SrcIntAut': Attribute('SrcIntAut', bool, init_value=True, sub_cb=self.set_src_int_aut), 'SrcIntOp': Attribute('SrcIntOp', bool, init_value=False, sub_cb=self.set_src_int_op), 'SrcManOp': Attribute('SrcManOp', bool, init_value=False, sub_cb=self.set_src_man_op), @@ -379,25 +379,21 @@ def set_src_channel(self, value: bool): self.attributes['SrcChannel'].set_value(value) def set_src_man_aut(self, value: bool): - if not self.attributes['StateOffAct'].value and value: - if self.attributes['SrcChannel'].value: - self._src_to_man() + if self.attributes['SrcChannel'].value and value: + self._src_to_man() + + def set_src_man_op(self, value: bool): + if not self.attributes['SrcChannel'].value and value: + self._src_to_man() + if value: + self.attributes['SrcManOp'].set_value(False) def set_src_int_aut(self, value: bool): - if not self.attributes['StateOffAct'].value and value: - if self.attributes['SrcChannel'].value: - self._src_to_int() + if self.attributes['SrcChannel'].value and value: + self._src_to_int() def set_src_int_op(self, value: bool): - if not self.attributes['StateOffAct'].value and value: - if not self.attributes['SrcChannel'].value: - self._src_to_int() + if not self.attributes['SrcChannel'].value and value: + self._src_to_int() if value: self.attributes['SrcIntOp'].set_value(False) - - def set_src_man_op(self, value: bool): - if not self.attributes['StateOffAct'].value and value: - if not self.attributes['SrcChannel'].value: - self._src_to_man() - if value: - self.attributes['SrcManOp'].set_value(False) From 83b39d13c8d3f5d65fcd5c4786065ef210dcdaa6 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Mon, 1 Sep 2025 12:13:06 +0200 Subject: [PATCH 39/61] added indicator and operation elements, some refactoring --- src/mtppy/opcua_server_pea.py | 89 +++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 30 deletions(-) diff --git a/src/mtppy/opcua_server_pea.py b/src/mtppy/opcua_server_pea.py index 188c084..02c196f 100644 --- a/src/mtppy/opcua_server_pea.py +++ b/src/mtppy/opcua_server_pea.py @@ -2,7 +2,7 @@ from opcua import Server, ua, Node from mtppy.communication_object import OPCUACommunicationObject from mtppy.service import Service -from mtppy.suc_data_assembly import SUCDataAssembly, SUCActiveElement +from mtppy.suc_data_assembly import SUCDataAssembly, SUCActiveElement, SUCIndicatorElement, SUCOperationElement from mtppy.mtp_generator import MTPGenerator _logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") @@ -17,20 +17,23 @@ def __init__(self, mtp_generator: MTPGenerator = None, endpoint: str = 'opc.tcp: mtp_generator (MTPGenerator): Instance of an MTP generator. endpoint (str): Endpoint of the OPC UA server. """ - self.service_set = {} - self.active_elements = {} + self.service_set: dict[str, Service] = {} + self.active_elements: dict[str, SUCActiveElement] = {} + self.indicator_elements: dict[str, SUCIndicatorElement] = {} + self.operation_elements: dict[str, SUCOperationElement] = {} self.custom_data_assembly_sets: dict[str, dict[str, SUCDataAssembly]] = {} - self.endpoint = endpoint - self.opcua_server = None - self.opcua_ns = 3 + self.endpoint: str = endpoint + self.opcua_server: Server = None + self.opcua_ns: int = 3 self.subscription_list = SubscriptionList() self._init_opcua_server() - self.mtp = mtp_generator + self.mtp: MTPGenerator = mtp_generator self._folders = ['configuration_parameters', 'procedures', 'procedure_parameters', 'process_value_ins', 'report_values', 'process_value_outs'] """Folders that are created in the OPC UA server if found in a data assembly even if they are not of type SUCDataAssembly.""" + self._leaves = ['op_src_mode', 'state_machine', 'procedure_control'] """Folders that are created in the OPC UA server if found in a data assembly even if they are not of type SUCDataAssembly. @@ -55,6 +58,24 @@ def add_active_element(self, active_element: SUCActiveElement): """ self.active_elements[active_element.tag_name] = active_element + def add_indicator_element(self, indicator_element: SUCIndicatorElement): + """ + Add an indicator element to the PEA. + + Args: + indicator_element (SUCIndicatorElement): Indicator element. + """ + self.indicator_elements[indicator_element.tag_name] = indicator_element + + def add_operation_element(self, operation_element: SUCOperationElement): + """ + Add an operation element to the PEA. + + Args: + operation_element (SUCOperationElement): Operation element. + """ + self.operation_elements[operation_element.tag_name] = operation_element + def add_custom_data_assembly_set(self, root_folder_name: str, data_assembly_set: dict[str, SUCDataAssembly]): """ Add a custom data assembly to the PEA. @@ -148,11 +169,6 @@ def _build_opcua_server(self): Creates an OPC UA server instance including required nodes according to defined data assemblies. """ _logger.info(f'Adding OPC UA nodes to the server structure according to the PEA structure:') - ns = self.opcua_ns - server = self.opcua_server.get_objects_node() - - services_node_id = f'ns={ns};s=services' - services_node = server.add_folder(services_node_id, "services") # initiate a new MTP that will be added to InstanceHierarchy: ModuleTypePackage if self.mtp: @@ -162,32 +178,28 @@ def _build_opcua_server(self): if self.mtp: self.mtp.add_opcua_server(self.endpoint) - for service in self.service_set.values(): - _logger.info(f'- service {service.tag_name}') - self._create_opcua_objects_for_folders(service, services_node_id, services_node) + # add service elements + self._create_opcua_element(self.service_set, "services") + # add active, indicator and operation elements if self.active_elements.__len__() > 0: - act_elem_node_id = f'ns={ns};s=active_elements' - act_elem_node = server.add_folder(act_elem_node_id, "active_elements") - for active_element in self.active_elements.values(): - _logger.info(f'- active element {active_element.tag_name}') - self._create_opcua_objects_for_folders( - active_element, act_elem_node_id, act_elem_node) + self._create_opcua_element(self.active_elements, "active_elements") + if self.indicator_elements.__len__() > 0: + self._create_opcua_element(self.indicator_elements, "indicator_elements") + if self.operation_elements.__len__() > 0: + self._create_opcua_element(self.operation_elements, "operation_elements") # add custom data assemblies for root_folder_name, data_assembly_set in self.custom_data_assembly_sets.items(): _logger.info(f'- custom data assembly {root_folder_name}') # if root_folder_name is the same as the first key add to root if root_folder_name == next(iter(data_assembly_set)): + ns = self.opcua_ns + server = self.opcua_server.get_objects_node() self._create_opcua_objects_for_folders( data_assembly_set[root_folder_name], f"ns={ns};s={root_folder_name}", server, root_folder_name) else: - custom_da_node_id = f'ns={ns};s={root_folder_name}' - custom_da_node = server.add_folder(custom_da_node_id, root_folder_name) - for data_assembly in data_assembly_set.values(): - _logger.info(f'-- data assembly {data_assembly.tag_name}') - self._create_opcua_objects_for_folders( - data_assembly, custom_da_node_id, custom_da_node) + self._create_opcua_element(data_assembly_set, root_folder_name) # add SupportedRoleClass to all InternalElements if self.mtp: @@ -198,6 +210,23 @@ def _build_opcua_server(self): _logger.info(f'MTP manifest export to {self.mtp.export_path}') self.mtp.export_manifest() + def _create_opcua_element(self, elements: dict[str, SUCDataAssembly], folder_name: str): + """ + Create OPC UA nodes for a specific element type (active elements, indicator elements, operation elements). + + Args: + elements (dict[str, SUCDataAssembly]): Dictionary of elements. + folder_name (str): Name of the folder to create in the OPC UA server. + """ + ns = self.opcua_ns + server = self.opcua_server.get_objects_node() + element_node_id = f'ns={ns};s={folder_name}' + element_node = server.add_folder(element_node_id, folder_name) + _logger.info(f'- {folder_name}') + for element in elements.values(): + _logger.info(f'-- element {element.tag_name}') + self._create_opcua_objects_for_folders(element, element_node_id, element_node) + def _create_opcua_objects_for_folders(self, data_assembly: SUCDataAssembly, parent_opcua_prefix: str, parent_opcua_object: Node, name: str = None): @@ -250,7 +279,7 @@ def _create_opcua_objects_for_folders(self, data_assembly: SUCDataAssembly, if self.mtp: self.mtp.add_linked_attr(instance, link_id) - def _create_opcua_objects_for_leaves(self, opcua_object, parent_opcua_prefix: str, parent_opcua_object, par_instance): + def _create_opcua_objects_for_leaves(self, opcua_object: SUCDataAssembly, parent_opcua_prefix: str, parent_opcua_object: Node, par_instance): """ Iterates over end objects (leaves) of data assemblies to create corresponding OPC UA nodes. @@ -265,8 +294,8 @@ def _create_opcua_objects_for_leaves(self, opcua_object, parent_opcua_prefix: st # We attach communication objects to be able to write values on opcua server on attributes change opcua_type = self._infer_data_type(attr.type) - opcua_node_obj = parent_opcua_object.add_variable(attribute_node_id, attr.name, attr.init_value, - varianttype=opcua_type) + opcua_node_obj: Node = parent_opcua_object.add_variable(attribute_node_id, attr.name, attr.init_value, + varianttype=opcua_type) _logger.debug( f'OPCUA Node: {attribute_node_id}, Name: {attr.name}, Value: {attr.init_value}') opcua_node_obj.set_writable(False) From b38f2c360bd21579988172e08e05e6cdb183a238 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Mon, 1 Sep 2025 14:19:34 +0200 Subject: [PATCH 40/61] stopped non self completing procedures from self completing --- src/mtppy/service.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/mtppy/service.py b/src/mtppy/service.py index 96e6a21..54f051b 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -125,6 +125,15 @@ def enable_restart(self, enable: bool = True): """ self.state_machine.command_en_ctrl.enable_restart(enable) + def get_current_procedure(self) -> Procedure: + """ + Returns the current procedure. + + Returns: + Procedure: The current procedure. + """ + return self.procedures[self.procedure_control.get_procedure_cur()] + def init_idle_state(self): """ Initializes the idle state. @@ -180,8 +189,21 @@ def state_change(self): """ Changes the state. Has to be called by each transitional state method. """ + # don't automatically change state for non self-completing procedures + if self.state_machine.get_current_state_str() == "execute": + if not self.is_self_completing(): + return self.state_machine.state_change() + def is_self_completing(self) -> bool: + """ + Checks if the current Procedure is self-completing. + + Returns: + bool: True if the current Procedure is self-completing, False otherwise. + """ + return self.get_current_procedure().attributes['IsSelfCompleting'].value + def add_configuration_parameter(self, configuration_parameter: SUCParameterElement): """ Adds a configuration parameter to the service. From 9bf89f12e2037d904a287e244f2bc979a2e583e0 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Mon, 1 Sep 2025 19:28:45 +0200 Subject: [PATCH 41/61] added operation mode for BinVlv and BinDrv. Refactor OperationSourceModeActiveElements --- src/mtppy/active_elements.py | 12 +-- src/mtppy/operation_elements.py | 8 +- src/mtppy/operation_source_mode.py | 141 +++++++++++++++++++---------- 3 files changed, 103 insertions(+), 58 deletions(-) diff --git a/src/mtppy/active_elements.py b/src/mtppy/active_elements.py index 8e5d64e..71ccdae 100644 --- a/src/mtppy/active_elements.py +++ b/src/mtppy/active_elements.py @@ -3,7 +3,7 @@ from mtppy.attribute import Attribute -from mtppy.operation_source_mode import OperationSourceModeActiveElements +from mtppy.operation_source_mode import OperationSourceModeElement, OperationMode from mtppy.suc_data_assembly import SUCActiveElement from time import sleep @@ -22,7 +22,7 @@ def __init__(self, tag_name, tag_description='', """ super().__init__(tag_name, tag_description) - self.op_src_mode = OperationSourceModeActiveElements() + self.op_src_mode = OperationSourceModeElement() self.pos_min = pos_min self.pos_max = pos_max @@ -565,7 +565,7 @@ def __init__(self, tag_name: str, tag_description: str = '', open_fbk_calc: bool super().__init__(tag_name, tag_description) - self.op_src_mode = OperationSourceModeActiveElements() + self.op_src_mode = OperationMode() self.open_fbk_calc = open_fbk_calc self.close_fbk_calc = close_fbk_calc @@ -982,7 +982,7 @@ def __init__(self, tag_name, tag_description='', rev_fbk_calc=True, fwd_fbk_calc rev_en=False, perm_en=False, intl_en=False, prot_en=False): super().__init__(tag_name, tag_description) - self.op_src_mode = OperationSourceModeActiveElements() + self.op_src_mode = OperationMode() self.rev_fbk_calc = rev_fbk_calc self.fwd_fbk_calc = fwd_fbk_calc @@ -1386,7 +1386,7 @@ def __init__(self, tag_name: str, tag_description: str = '', """ super().__init__(tag_name, tag_description) - self.op_src_mode = OperationSourceModeActiveElements() + self.op_src_mode = OperationSourceModeElement() self.rpm_min = rpm_min self.rpm_max = rpm_max @@ -2013,7 +2013,7 @@ def __init__(self, tag_name, tag_description='', P=100, Ti=10, Td=1): super().__init__(tag_name, tag_description) - self.op_src_mode = OperationSourceModeActiveElements() + self.op_src_mode = OperationSourceModeElement() self.pv_scl_min = pv_scl_min self.pv_scl_max = pv_scl_max diff --git a/src/mtppy/operation_elements.py b/src/mtppy/operation_elements.py index 316683e..b25817c 100644 --- a/src/mtppy/operation_elements.py +++ b/src/mtppy/operation_elements.py @@ -1,7 +1,7 @@ import logging from mtppy.attribute import Attribute -from mtppy.operation_source_mode import OperationSourceModeOperationElements +from mtppy.operation_source_mode import SourceMode from mtppy.suc_data_assembly import SUCOperationElement @@ -80,7 +80,7 @@ def __init__(self, tag_name: str, tag_description: str = '', v_scl_min=v_scl_min, v_scl_max=v_scl_max, v_unit=v_unit, init_value=init_value) - self.op_src_mode = OperationSourceModeOperationElements() + self.op_src_mode = SourceMode() # Extensions (Table 33) self._add_attribute(Attribute('WQC', int, init_value=0)) @@ -168,7 +168,7 @@ def __init__(self, tag_name: str, tag_description: str = '', v_scl_min=v_scl_min, v_scl_max=v_scl_max, v_unit=v_unit, init_value=init_value) - self.op_src_mode = OperationSourceModeOperationElements() + self.op_src_mode = SourceMode() # Extensions (Table 35) self._add_attribute(Attribute('WQC', int, init_value=0)) @@ -241,7 +241,7 @@ def __init__(self, tag_name: str, tag_description: str = '', v_state0=v_state0, v_state1=v_state1, init_value=init_value) - self.op_src_mode = OperationSourceModeOperationElements() + self.op_src_mode = SourceMode() # Extensions (Table 37) self._add_attribute(Attribute('WQC', int, init_value=0)) diff --git a/src/mtppy/operation_source_mode.py b/src/mtppy/operation_source_mode.py index b8e09a4..ba1946e 100644 --- a/src/mtppy/operation_source_mode.py +++ b/src/mtppy/operation_source_mode.py @@ -282,71 +282,108 @@ def set_src_int_op(self, value: bool): self._update_linked_op_src_modes('SrcIntOp', value) -class OperationSourceModeActiveElements(OperationSourceMode): - def __init__(self): +class OperationMode(): + def __init__(self, name_of_parent: str = ''): """ - Represents the operation and source model control for active elements. + Represents the operation mode control. Used by active elements. + (Table 15 VDI/VDE/NAMUR 2658-3). """ - super().__init__() - self.attributes = { - 'StateChannel': Attribute('StateChannel', bool, init_value=False, sub_cb=self.set_state_channel), + if not hasattr(self, 'attributes'): + self.attributes: dict['str', Attribute] = {} + self.attributes.update({ + # --- Operation mode control --- + 'StateChannel': Attribute('StateChannel', bool, init_value=True, sub_cb=self.set_state_channel), 'StateOffAut': Attribute('StateOffAut', bool, init_value=False, sub_cb=self.set_state_off_aut), - 'StateOpAut': Attribute('StateOpAut', bool, init_value=False, sub_cb=self.set_state_op_aut), + 'StateOpAut': Attribute('StateOpAut', bool, init_value=True, sub_cb=self.set_state_op_aut), 'StateAutAut': Attribute('StateAutAut', bool, init_value=False, sub_cb=self.set_state_aut_aut), 'StateOffOp': Attribute('StateOffOp', bool, init_value=False, sub_cb=self.set_state_off_op), 'StateOpOp': Attribute('StateOpOp', bool, init_value=False, sub_cb=self.set_state_op_op), 'StateAutOp': Attribute('StateAutOp', bool, init_value=False, sub_cb=self.set_state_aut_op), - 'StateOpAct': Attribute('StateOpAct', bool, init_value=False), + + 'StateOpAct': Attribute('StateOpAct', bool, init_value=True), 'StateAutAct': Attribute('StateAutAct', bool, init_value=False), - 'StateOffAct': Attribute('StateOffAct', bool, init_value=True), + 'StateOffAct': Attribute('StateOffAct', bool, init_value=False) + }) - 'SrcChannel': Attribute('SrcChannel', bool, init_value=False, sub_cb=self.set_src_channel), - 'SrcManAut': Attribute('SrcManAut', bool, init_value=False, sub_cb=self.set_src_man_aut), - 'SrcIntOp': Attribute('SrcIntOp', bool, init_value=False, sub_cb=self.set_src_int_op), - 'SrcIntAut': Attribute('SrcIntAut', bool, init_value=False, sub_cb=self.set_src_int_aut), - 'SrcManOp': Attribute('SrcManOp', bool, init_value=False, sub_cb=self.set_src_man_op), - 'SrcIntAct': Attribute('SrcIntAct', bool, init_value=False), - 'SrcManAct': Attribute('SrcManAct', bool, init_value=False) - } + self._name_of_parent = f"{name_of_parent}: " if name_of_parent != '' else '' - def _src_to_off(self): - self.attributes['SrcIntAct'].set_value(False) - self.attributes['SrcManAct'].set_value(False) - _logger.debug('Source mode is now off') + # --- Operation mode transitions --- + def _opmode_to_off(self): + self.attributes['StateOpAct'].set_value(False) + self.attributes['StateAutAct'].set_value(False) + self.attributes['StateOffAct'].set_value(True) + _logger.debug(f'{self._name_of_parent}Operation mode is now off') - def _src_to_int(self): - self.attributes['SrcIntAct'].set_value(True) - self.attributes['SrcManAct'].set_value(False) - _logger.debug('Source mode is now int') + def _opmode_to_aut(self): + self.attributes['StateOpAct'].set_value(False) + self.attributes['StateAutAct'].set_value(True) + self.attributes['StateOffAct'].set_value(False) + _logger.debug(f'{self._name_of_parent}Operation mode is now aut') - def _src_to_man(self): - self.attributes['SrcIntAct'].set_value(False) - self.attributes['SrcManAct'].set_value(True) - _logger.debug('Source mode is now man') + def _opmode_to_op(self): + self.attributes['StateOpAct'].set_value(True) + self.attributes['StateAutAct'].set_value(False) + self.attributes['StateOffAct'].set_value(False) + _logger.debug(f'{self._name_of_parent}Operation mode is now op') - def set_src_channel(self, value: bool): - _logger.debug('Source mode channel is now %s' % value) + # --- Callbacks for operation control --- + def set_state_channel(self, value: bool): + _logger.debug(f'{self._name_of_parent}Operation mode channel is now %s' % value) + if self.attributes['StateChannel'].value != value: + self.attributes['StateChannel'].set_value(value) - def set_src_man_aut(self, value: bool): - if not self.attributes['StateOffAct'].value and value: - if self.attributes['SrcChannel'].value: - self._src_to_man() + def set_state_off_aut(self, value: bool): + _logger.debug(f'{self._name_of_parent}StateOffAut set to {value}') + if self.attributes['StateChannel'].value and value: + if self.attributes['StateOpAct'].value or self.attributes['StateAutAct'].value: + self._opmode_to_off() - def set_src_man_op(self, value: bool): - if not self.attributes['StateOffAct'].value and value: - if not self.attributes['SrcChannel'].value: - self._src_to_man() + def set_state_op_aut(self, value: bool): + _logger.debug(f'{self._name_of_parent}StateOpAut set to {value}') + if self.attributes['StateChannel'].value and value: + if self.attributes['StateOffAct'].value or self.attributes['StateAutAct'].value: + self._opmode_to_op() + + def set_state_aut_aut(self, value: bool): + _logger.debug(f'{self._name_of_parent}StateAutAut set to {value}') + if self.attributes['StateChannel'].value and value: + if self.attributes['StateOffAct'].value or self.attributes['StateOpAct'].value: + self._opmode_to_aut() + + def set_state_off_op(self, value: bool): + _logger.debug(f'{self._name_of_parent}StateOffOp set to {value}') + if not self.attributes['StateChannel'].value and value: + if self.attributes['StateOpAct'].value or self.attributes['StateAutAct'].value: + self._opmode_to_off() if value: - self.attributes['SrcManOp'].set_value(False) + self.attributes['StateOffOp'].set_value(False) + + def set_state_op_op(self, value: bool): + _logger.debug(f'{self._name_of_parent}StateOpOp set to {value}') + if not self.attributes['StateChannel'].value and value: + if self.attributes['StateOffAct'].value or self.attributes['StateAutAct'].value: + self._opmode_to_op() + if value: + self.attributes['StateOffOp'].set_value(False) + + def set_state_aut_op(self, value: bool): + _logger.debug(f'{self._name_of_parent}StateAutOp set to {value}') + if not self.attributes['StateChannel'].value and value: + if self.attributes['StateOffAct'].value or self.attributes['StateOpAct'].value: + self._opmode_to_aut() + if value: + self.attributes['StateOffOp'].set_value(False) -class OperationSourceModeOperationElements(): +class SourceMode(): def __init__(self, name_of_parent: str = ''): """ - Represents the operation and source mode control for operation elements - (Table 35 VDI/VDE/NAMUR 2658-3). + Represents the source mode control. Used by the operation elements. + (Table 16 VDI/VDE/NAMUR 2658-3). """ - self.attributes = { + if not hasattr(self, 'attributes'): + self.attributes: dict['str', Attribute] = {} + self.attributes.update({ # --- Source mode control --- 'SrcChannel': Attribute('SrcChannel', bool, init_value=True, sub_cb=self.set_src_channel), 'SrcManAut': Attribute('SrcManAut', bool, init_value=False, sub_cb=self.set_src_man_aut), @@ -356,7 +393,7 @@ def __init__(self, name_of_parent: str = ''): 'SrcIntAct': Attribute('SrcIntAct', bool, init_value=False), 'SrcManAct': Attribute('SrcManAct', bool, init_value=False) - } + }) self._name_of_parent = f"{name_of_parent}: " if name_of_parent != '' else '' @@ -374,9 +411,8 @@ def _src_to_man(self): # --- Callbacks for source control --- def set_src_channel(self, value: bool): _logger.debug('Source mode channel is now %s' % value) - if self.attributes['SrcChannel'].value == value: - return - self.attributes['SrcChannel'].set_value(value) + if self.attributes['SrcChannel'].value != value: + self.attributes['SrcChannel'].set_value(value) def set_src_man_aut(self, value: bool): if self.attributes['SrcChannel'].value and value: @@ -397,3 +433,12 @@ def set_src_int_op(self, value: bool): self._src_to_int() if value: self.attributes['SrcIntOp'].set_value(False) + + +class OperationSourceModeElement(OperationMode, SourceMode): + def __init__(self, name_of_parent: str = ''): + """ + Represents the operation and source mode control for operation elements. + """ + OperationMode.__init__(self, name_of_parent) + SourceMode.__init__(self, name_of_parent) From 8cb31a244078e969528b4411a6ab7d3e6647218d Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Sat, 6 Sep 2025 12:04:52 +0200 Subject: [PATCH 42/61] Moved lock functionality to new class. Some refactoring --- src/mtppy/active_elements.py | 398 ++++++++++++++-------------------- src/mtppy/opcua_server_pea.py | 5 +- 2 files changed, 165 insertions(+), 238 deletions(-) diff --git a/src/mtppy/active_elements.py b/src/mtppy/active_elements.py index 71ccdae..974c87a 100644 --- a/src/mtppy/active_elements.py +++ b/src/mtppy/active_elements.py @@ -1,5 +1,7 @@ import logging import threading +from abc import abstractmethod +from collections.abc import Callable from mtppy.attribute import Attribute @@ -12,6 +14,117 @@ _logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") +class Locks(): + """Class that implements the locks for the active elements. Implements the locks for permission, interlock and protect.""" + + def __init__(self, perm_en=False, intl_en=False, prot_en=False, expext_save_pos_func: Callable[[], None] = None): + + self.perm_en = perm_en + self.intl_en = intl_en + self.prot_en = prot_en + + self._expect_save_pos = expext_save_pos_func + + self.permit = threading.Lock() + self.interlock = threading.Lock() + self.protect = threading.Lock() + + self.attributes = { + 'PermEn': Attribute('PermEn', bool, init_value=perm_en), + 'Permit': Attribute('Permit', bool, init_value=1, sub_cb=self.__set_permit), + 'IntlEn': Attribute('IntlEn', bool, init_value=intl_en), + 'Interlock': Attribute('Interlock', bool, init_value=1, sub_cb=self.__set_interlock), + 'ProtEn': Attribute('ProtEn', bool, init_value=prot_en), + 'Protect': Attribute('Protect', bool, init_value=1, sub_cb=self.__set_protect), + } + + def _run_allowed(self): + if self.attributes['PermEn'].value and self.attributes['Permit'].value == 0: + _logger.debug('Permission is not given') + return False + if self.attributes['IntlEn'].value and self.attributes['Interlock'].value == 0: + _logger.debug('Interlock is active') + return False + if self.attributes['ProtEn'].value and self.attributes['Protect'].value == 0: + _logger.debug('Protect is active') + return False + return True + + def __set_permit(self, value: bool): + """Callback function for Permit attribute.""" + _logger.debug(f'Permit set to {value}') + if self.attributes['Permit'].value == value: + return + self.set_permit(value) + + def set_permit(self, value: bool): + """Set Permit attribute with safety checks.""" + # don't set Permit to False if PermEn is False + if not self.attributes['PermEn'].value: + self.attributes['Permit'].set_value(True) + return + # only set value if it is different from the current one + if self.attributes['Permit'].value != value: + if not value: + self.permit.acquire(blocking=False) + else: + self.permit.release() + self.attributes['Permit'].set_value(value) + return + + def permit_status(self) -> bool: + return self.attributes['Permit'].value + + def __set_interlock(self, value: bool): + """Callback function for Interlock attribute.""" + _logger.debug(f'Interlock set to {value}') + if self.attributes['Interlock'].value == value: + return + self.set_interlock(value) + + def set_interlock(self, value: bool): + """Set Interlock attribute with safety checks.""" + # don't set Interlock to False if IntlEn is False + if not self.attributes['IntlEn'].value: + self.attributes['Interlock'].set_value(True) + return + if self.attributes['Interlock'].value != value: + if not value: + self.interlock.acquire(blocking=False) + else: + self.interlock.release() + self.attributes['Interlock'].set_value(value) + self._expect_save_pos() + + def interlock_status(self) -> bool: + return self.attributes['Interlock'].value + + def __set_protect(self, value: bool): + """Callback function for Protect attribute.""" + _logger.debug('Protect set to %s' % value) + if self.attributes['Protect'].value == value: + return + self.set_protect(value) + + def set_protect(self, value: bool, reset=False): + """Set Protect attribute with safety checks. + Reset should be True if called from reset_vlv function.""" + if not self.attributes['ProtEn'].value: + self.attributes['Protect'].set_value(True) + return + + if reset: + self.protect.release() + self.attributes['Protect'].set_value(True) + else: + self.protect.acquire(blocking=False) + self.attributes['Protect'].set_value(False) + self._expect_save_pos() + + def protect_status(self) -> bool: + return self.attributes['Protect'].value + + class AnaVlv(SUCActiveElement): def __init__(self, tag_name, tag_description='', pos_min=0, pos_max=1000, pos_scl_min=0, pos_scl_max=1000, pos_unit=0, @@ -24,6 +137,8 @@ def __init__(self, tag_name, tag_description='', self.op_src_mode = OperationSourceModeElement() + self.locks = Locks(perm_en, intl_en, prot_en, self._expect_save_pos) + self.pos_min = pos_min self.pos_max = pos_max self.pos_scl_min = pos_scl_min @@ -35,9 +150,6 @@ def __init__(self, tag_name, tag_description='', self.safe_pos = safe_pos self.safe_pos_en = safe_pos_en - self.perm_en = perm_en - self.intl_en = intl_en - self.prot_en = prot_en self._add_attribute(Attribute('SafePos', bool, init_value=safe_pos)) self._add_attribute(Attribute('SafePosEn', bool, init_value=self.safe_pos_en)) @@ -64,13 +176,7 @@ def __init__(self, tag_name, tag_description='', self._add_attribute(Attribute('CloseFbk', bool, init_value=False)) self._add_attribute(Attribute('PosFbkCalc', bool, init_value=pos_fbk_calc)) self._add_attribute(Attribute('PosFbk', float, init_value=pos_min)) - self._add_attribute(Attribute('PermEn', bool, init_value=perm_en)) - self._add_attribute(Attribute('Permit', bool, init_value=0)) - self._add_attribute(Attribute('IntlEn', bool, init_value=intl_en)) - self._add_attribute(Attribute('Interlock', bool, init_value=0)) - self._add_attribute(Attribute('ProtEn', bool, init_value=prot_en)) - self._add_attribute(Attribute('Protect', bool, init_value=0)) - self._add_attribute(Attribute('ResetOp', bool, init_value=0, sub_cb=self.set_reset_op)) + self._add_attribute(Attribute('ResetOp', bool, init_value=0, sub_cb=self._set_reset_op)) self._add_attribute(Attribute('ResetAut', bool, init_value=0, sub_cb=self.set_reset_aut)) def _expect_save_pos(self): @@ -79,9 +185,9 @@ def _expect_save_pos(self): else: if self.attributes['SafePosEn'].value: self._go_save_pos() + self.attributes['SafePosAct'].set_value(True) else: # hold position _logger.debug('Device has no safe position') - self.attributes['SafePosAct'].set_value(True) def _go_save_pos(self): if self.attributes['SafePos'].value == 1: @@ -129,23 +235,14 @@ def set_close_op(self, value: bool): self._run_close_vlv() self.attributes['CloseOp'].set_value(False) - def set_reset_op(self, value: bool): + def _set_reset_op(self, value: bool): _logger.debug(f'ResetOp set to {value}') if self.op_src_mode.attributes['StateOpAct'].value and value: self._reset_vlv() self.attributes['ResetOp'].set_value(False) def _run_allowed(self): - if self.attributes['PermEn'].value and self.attributes['Permit'].value == 0: - _logger.debug(f'Permission is not given') - return False - if self.attributes['IntlEn'].value and self.attributes['Interlock'].value == 0: - _logger.debug(f'Interlock is active') - return False - if self.attributes['ProtEn'].value and self.attributes['Protect'].value == 0: - _logger.debug(f'Protect is active') - return False - return True + return self.locks._run_allowed() def _run_open_vlv(self): self.attributes['OpenAct'].set_value(True) @@ -164,8 +261,8 @@ def _run_close_vlv(self): self.attributes['CloseFbk'].set_value(True) def _reset_vlv(self): - if self.attributes['ProtEn'].value and self.attributes['Protect'].value == 0: - self.attributes['Protect'].set_value(True) + if self.locks.attributes['ProtEn'].value and self.locks.attributes['Protect'].value == 0: + self.locks.set_protect(True, reset=True) self.attributes['SafePosAct'].set_value(False) self.attributes['OpenAct'].set_value(False) if self.attributes['OpenFbkCalc']: @@ -212,7 +309,7 @@ def _set_pos(self, value: float): # if SafePosAct is active, safety setting for the position is accepted elif self.attributes['SafePosAct'].value or \ - (self.attributes['PermEn'].value and self.attributes['Permit'].value == 0): + (self.locks.attributes['PermEn'].value and self.locks.attributes['Permit'].value == 0): _logger.debug('manual or internal position specification inactive') return @@ -244,30 +341,6 @@ def set_close_fbk(self, value: bool): self.attributes['CloseFbk'].set_value(value) _logger.debug(f'CloseFbk set to {value}') - def set_permit(self, value: bool): - if not self.attributes['PermEn'].value: - value = True - self.attributes['Permit'].set_value(value) - _logger.debug('Permit set to %s' % value) - # safety position should not be activated for permit mode - self.attributes['SafePosAct'].set_value(False) - - def set_interlock(self, value: bool): - if not self.attributes['IntlEn'].value: - value = True - self.attributes['Interlock'].set_value(value) - _logger.debug('Interlock set to %s' % value) - self._expect_save_pos() - - def set_protect(self, value: bool): - if not self.attributes['ProtEn'].value: - value = True - if value: - self._reset_vlv() - self.attributes['Protect'].set_value(value) - _logger.debug('Protect set to %s' % value) - self._expect_save_pos() - def get_pos(self): return self.attributes['Pos'].value @@ -506,7 +579,7 @@ def set_close_op(self, value: bool): self._run_close_vlv() self.attributes['CloseOp'].set_value(False) - def set_reset_op(self, value: bool): + def _set_reset_op(self, value: bool): _logger.debug('ResetOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and value: if self.attributes['MonEn'].value: @@ -555,27 +628,38 @@ def set_stop_monitor(self): class BinVlv(SUCActiveElement): + """Binary Valve (BinVlv). Table 40 from VDI/VDE/NAMUR 2658-3.""" + def __init__(self, tag_name: str, tag_description: str = '', open_fbk_calc: bool = True, close_fbk_calc: bool = True, safe_pos: int = 0, safe_pos_en: bool = False, perm_en: bool = False, intl_en: bool = False, prot_en: bool = False): """ - Binary Valve (BinVlv). Parameter names correspond attribute names in VDI/VDE/NAMUR 2658. + Binary Valve (BinVlv). Initializes the attributes according to VDI/VDE/NAMUR 2658-3 Table 40. + + Args: + tag_name (str): Name of the Element. + tag_description (str): Description of the Element. + open_fbk_calc (bool): Open Feedback Source. 1: calculated, 0: sensor detection. + close_fbk_calc (bool): Close Feedback Source. 1: calculated, 0: sensor detection. + safe_pos (int): Safe Position. 1: open, 0: close. + safe_pos_en (bool): Hardware Safe Position enabled. 1: Device has a safe position, 0: Device has no safe position. + perm_en (bool): Enables the Permission Lock. 1: enabled, 0: disabled. + intl_en (bool): Enables the Interlock Lock. 1: enabled, 0: disabled. + prot_en (bool): Enables the Protection Lock. 1: enabled, 0: disabled. """ super().__init__(tag_name, tag_description) self.op_src_mode = OperationMode() + self.locks = Locks(perm_en, intl_en, prot_en, self._expect_save_pos) + self.open_fbk_calc = open_fbk_calc self.close_fbk_calc = close_fbk_calc self.safe_pos = safe_pos self.safe_pos_en = safe_pos_en - self.perm_en = perm_en - self.intl_en = intl_en - self.prot_en = prot_en - self.__reset_protect = False self._add_attribute(Attribute('SafePos', bool, init_value=safe_pos)) self._add_attribute(Attribute('SafePosEn', bool, init_value=self.safe_pos_en)) @@ -590,14 +674,7 @@ def __init__(self, tag_name: str, tag_description: str = '', open_fbk_calc: bool self._add_attribute(Attribute('OpenFbk', bool, init_value=False)) self._add_attribute(Attribute('CloseFbkCalc', bool, init_value=close_fbk_calc)) self._add_attribute(Attribute('CloseFbk', bool, init_value=False)) - self._add_attribute(Attribute('PermEn', bool, init_value=perm_en)) - self._add_attribute(Attribute('Permit', bool, init_value=1, sub_cb=self.__set_permit)) - self._add_attribute(Attribute('IntlEn', bool, init_value=intl_en)) - self._add_attribute(Attribute('Interlock', bool, init_value=1, - sub_cb=self.__set_interlock)) - self._add_attribute(Attribute('ProtEn', bool, init_value=prot_en)) - self._add_attribute(Attribute('Protect', bool, init_value=1, sub_cb=self.__set_protect)) - self._add_attribute(Attribute('ResetOp', bool, init_value=0, sub_cb=self.set_reset_op)) + self._add_attribute(Attribute('ResetOp', bool, init_value=0, sub_cb=self._set_reset_op)) self._add_attribute(Attribute('ResetAut', bool, init_value=0, sub_cb=self.set_reset_aut)) def _expect_save_pos(self): @@ -606,9 +683,9 @@ def _expect_save_pos(self): else: if self.attributes['SafePosEn'].value: self._go_safe_pos() + self.attributes['SafePosAct'].set_value(True) else: _logger.debug('Device has no safe position') - self.attributes['SafePosAct'].set_value(True) def _go_safe_pos(self): if self.attributes['SafePos'].value: @@ -651,7 +728,7 @@ def set_close_op(self, value: bool): self._run_close_vlv() self.attributes['CloseOp'].set_value(False) - def set_reset_op(self, value: bool): + def _set_reset_op(self, value: bool): _logger.debug('ResetOp set to %s' % value) if self.attributes['ResetOp'].value == value: return @@ -671,16 +748,7 @@ def _run_allowed(self, for_close: bool = False): # Closing the valve is always allowed if for_close: return True - if self.attributes['PermEn'].value and self.attributes['Permit'].value == 0: - _logger.debug('Permission is not given') - return False - if self.attributes['IntlEn'].value and self.attributes['Interlock'].value == 0: - _logger.debug('Interlock is active') - return False - if self.attributes['ProtEn'].value and self.attributes['Protect'].value == 0: - _logger.debug('Protect is active') - return False - return True + return self.locks._run_allowed() def _run_open_vlv(self): self.attributes['Ctrl'].set_value(True) @@ -699,9 +767,8 @@ def _run_close_vlv(self): self.attributes['CloseFbk'].set_value(True) def reset_vlv(self): - if self.attributes['ProtEn'].value and self.attributes['Protect'].value == 0: - self.__reset_protect = True - self.attributes['Protect'].set_value(True) + if self.locks.attributes['ProtEn'].value and self.locks.attributes['Protect'].value == 0: + self.locks.set_protect(True, reset=True) if self.attributes['SafePosEn'].value: self.attributes['SafePosAct'].set_value(False) @@ -723,62 +790,6 @@ def set_close_fbk(self, value: bool): self.attributes['CloseFbk'].set_value(value) _logger.debug('CloseFbk set to %s' % value) - def __set_permit(self, value: bool): - """Callback function for Permit attribute.""" - _logger.debug(f'Permit set to {value}') - if self.attributes['Permit'].value == value: - return - self.set_permit(value) - - def set_permit(self, value: bool): - """Set Permit attribute with safety checks.""" - # don't set Permit to False if PermEn is False - if not self.attributes['PermEn'].value: - self.attributes['Permit'].set_value(True) - return - # only set value if it is different from the current one - if self.attributes['Permit'].value != value: - self.attributes['Permit'].set_value(value) - return - - def __set_interlock(self, value: bool): - """Callback function for Interlock attribute.""" - _logger.debug(f'Interlock set to {value}') - if self.attributes['Interlock'].value == value: - return - self.set_interlock(value) - - def set_interlock(self, value: bool): - """Set Interlock attribute with safety checks.""" - # don't set Interlock to False if IntlEn is False - if not self.attributes['IntlEn'].value: - self.attributes['Interlock'].set_value(True) - return - if self.attributes['Interlock'].value != value: - self.attributes['Interlock'].set_value(value) - self._expect_save_pos() - - def __set_protect(self, value: bool): - """Callback function for Protect attribute.""" - _logger.debug('Protect set to %s' % value) - if self.attributes['Protect'].value == value: - return - self.set_protect(value) - - def set_protect(self, value: bool): - """Set Protect attribute with safety checks. - Does not allow resetting Protect by default.""" - if not self.attributes['ProtEn'].value: - self.attributes['Protect'].set_value(True) - return - - if self.__reset_protect: - self.attributes['Protect'].set_value(True) - self.__reset_protect = False - else: - self.attributes['Protect'].set_value(False) - self._expect_save_pos() - def get_open_fbk(self): return self.attributes['OpenFbk'].value @@ -963,7 +974,7 @@ def set_close_op(self, value: bool): self._run_close_vlv() self.attributes['CloseOp'].set_value(False) - def set_reset_op(self, value: bool): + def _set_reset_op(self, value: bool): _logger.debug('ResetOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and value: if self.attributes['MonEn'].value: @@ -984,15 +995,14 @@ def __init__(self, tag_name, tag_description='', rev_fbk_calc=True, fwd_fbk_calc self.op_src_mode = OperationMode() + self.locks = Locks(perm_en, intl_en, prot_en, self._expect_save_pos) + self.rev_fbk_calc = rev_fbk_calc self.fwd_fbk_calc = fwd_fbk_calc self.safe_pos = safe_pos self.fwd_en = fwd_en self.rev_en = rev_en - self.perm_en = perm_en - self.intl_en = intl_en - self.prot_en = prot_en self._add_attribute(Attribute('SafePos', bool, init_value=safe_pos)) self._add_attribute(Attribute('SafePosAct', bool, init_value=False) @@ -1012,13 +1022,7 @@ def __init__(self, tag_name, tag_description='', rev_fbk_calc=True, fwd_fbk_calc self._add_attribute(Attribute('FwdFbkCalc', bool, init_value=fwd_fbk_calc)) self._add_attribute(Attribute('FwdFbk', bool, init_value=False)) self._add_attribute(Attribute('Trip', bool, init_value=True)) - self._add_attribute(Attribute('PermEn', bool, init_value=perm_en)) - self._add_attribute(Attribute('Permit', bool, init_value=0)) - self._add_attribute(Attribute('IntlEn', bool, init_value=intl_en)) - self._add_attribute(Attribute('Interlock', bool, init_value=0)) - self._add_attribute(Attribute('ProtEn', bool, init_value=prot_en)) - self._add_attribute(Attribute('Protect', bool, init_value=0)) - self._add_attribute(Attribute('ResetOp', bool, init_value=0, sub_cb=self.set_reset_op)) + self._add_attribute(Attribute('ResetOp', bool, init_value=0, sub_cb=self._set_reset_op)) self._add_attribute(Attribute('ResetAut', bool, init_value=0, sub_cb=self.set_reset_aut)) def _expect_save_pos(self): @@ -1035,14 +1039,7 @@ def _go_safe_pos(self): self._stop_drv() def _run_allowed(self): - if self.attributes['PermEn'].value and self.attributes['Permit'].value == 0: - _logger.debug('Permission is not given') - return False - if self.attributes['IntlEn'].value and self.attributes['Interlock'].value == 0: - _logger.debug('Interlock is active') - return False - if self.attributes['ProtEn'].value and self.attributes['Protect'].value == 0: - _logger.debug('Protect is active') + if not self.locks._run_allowed(): return False if not self.attributes['Trip'].value: _logger.debug('tripped') @@ -1069,12 +1066,6 @@ def set_stop_op(self, value: bool): self._stop_drv() self.attributes['StopOp'].set_value(False) - def set_reset_op(self, value: bool): - _logger.debug('ResetOp set to %s' % value) - if self.op_src_mode.attributes['StateOpAct'].value and value: - self._reset_drv() - self.attributes['ResetOp'].set_value(False) - def set_fwd_aut(self, value: bool): _logger.debug('FwdAut set to %s' % value) if self.op_src_mode.attributes['StateAutAct'].value and self.fwd_en: @@ -1092,11 +1083,6 @@ def set_stop_aut(self, value: bool): if self.op_src_mode.attributes['StateAutAct'].value and value: self._stop_drv() - def set_reset_aut(self, value: bool): - _logger.debug('ResetAut set to %s' % value) - if self.op_src_mode.attributes['StateAutAct'].value and value: - self._reset_drv() - def _run_fwd_drv(self): self.attributes['FwdCtrl'].set_value(True) if self.attributes['FwdFbkCalc']: @@ -1122,8 +1108,8 @@ def _stop_drv(self): self.attributes['RevFbk'].set_value(False) def _reset_drv(self): - if self.attributes['ProtEn'].value and self.attributes['Protect'].value == 0: - self.attributes['Protect'].set_value(True) + if self.locks.attributes['ProtEn'].value and self.locks.attributes['Protect'].value == 0: + self.locks.set_protect(True, reset=True) self.attributes['SafePosAct'].set_value(False) self._stop_drv() @@ -1142,29 +1128,6 @@ def set_trip(self, value: bool): _logger.debug('Trip set to %s' % value) self._expect_save_pos() - def set_permit(self, value: bool): - if not self.attributes['PermEn'].value: - value = True - self.attributes['Permit'].set_value(value) - _logger.debug('Permit set to %s' % value) - self.attributes['SafePosAct'].set_value(False) - - def set_interlock(self, value: bool): - if not self.attributes['IntlEn'].value: - value = True - self.attributes['Interlock'].set_value(value) - _logger.debug('Interlock set to %s' % value) - self._expect_save_pos() - - def set_protect(self, value: bool): - if not self.attributes['ProtEn'].value: - value = True - if value: - self._reset_drv() - self.attributes['Protect'].set_value(value) - _logger.debug('Protect set to %s' % value) - self._expect_save_pos() - def get_fwd_fbk(self): return self.attributes['FwdFbk'].value @@ -1322,7 +1285,7 @@ def set_stop_op(self, value: bool): self._stop_drv() self.attributes['StopOp'].set_value(False) - def set_reset_op(self, value: bool): + def _set_reset_op(self, value: bool): _logger.debug('ResetOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and value: if self.attributes['MonEn'].value: @@ -1384,10 +1347,12 @@ def __init__(self, tag_name: str, tag_description: str = '', """ Analog Drive (AnaDrv). Parameter names correspond attribute names in VDI/VDE/NAMUR 2658. """ - super().__init__(tag_name, tag_description) + super().__init__(tag_name, tag_description, perm_en, intl_en, prot_en) self.op_src_mode = OperationSourceModeElement() + self.locks = Locks(perm_en, intl_en, prot_en, self._expect_save_pos) + self.rpm_min = rpm_min self.rpm_max = rpm_max self.rpm_scl_min = rpm_scl_min @@ -1400,9 +1365,6 @@ def __init__(self, tag_name: str, tag_description: str = '', self.safe_pos = safe_pos self.fwd_en = fwd_en self.rev_en = rev_en - self.perm_en = perm_en - self.intl_en = intl_en - self.prot_en = prot_en self._add_attribute(Attribute('SafePos', bool, init_value=safe_pos)) self._add_attribute(Attribute('SafePosAct', bool, init_value=True)) @@ -1432,13 +1394,7 @@ def __init__(self, tag_name: str, tag_description: str = '', self._add_attribute(Attribute('RpmFbkCalc', bool, init_value=rpm_fbk_calc)) self._add_attribute(Attribute('RpmFbk', float, init_value=rpm_min)) self._add_attribute(Attribute('Trip', bool, init_value=True)) - self._add_attribute(Attribute('PermEn', bool, init_value=perm_en)) - self._add_attribute(Attribute('Permit', bool, init_value=0)) - self._add_attribute(Attribute('IntlEn', bool, init_value=intl_en)) - self._add_attribute(Attribute('Interlock', bool, init_value=0)) - self._add_attribute(Attribute('ProtEn', bool, init_value=prot_en)) - self._add_attribute(Attribute('Protect', bool, init_value=0)) - self._add_attribute(Attribute('ResetOp', bool, init_value=0, sub_cb=self.set_reset_op)) + self._add_attribute(Attribute('ResetOp', bool, init_value=0, sub_cb=self._set_reset_op)) self._add_attribute(Attribute('ResetAut', bool, init_value=0, sub_cb=self.set_reset_aut)) def _expect_save_pos(self): @@ -1455,14 +1411,7 @@ def _go_save_pos(self): self._stop_drv() def _run_allowed(self): - if self.attributes['PermEn'].value and self.attributes['Permit'].value == 0: - _logger.debug('Permission is not given') - return False - if self.attributes['IntlEn'].value and self.attributes['Interlock'].value == 0: - _logger.debug('Interlock is active') - return False - if self.attributes['ProtEn'].value and self.attributes['Protect'].value == 0: - _logger.debug('Protect is active') + if not self.locks._run_allowed(): return False if not self.attributes['Trip'].value: _logger.debug('Drive protection triggered') @@ -1489,7 +1438,7 @@ def set_stop_op(self, value: bool): self._stop_drv() self.attributes['StopOp'].set_value(False) - def set_reset_op(self, value: bool): + def _set_reset_op(self, value: bool): _logger.debug('ResetOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and value: self._reset_drv() @@ -1542,8 +1491,8 @@ def _stop_drv(self): self.attributes['RevFbk'].set_value(False) def _reset_drv(self): - if self.attributes['ProtEn'].value and self.attributes['Protect'].value == 0: - self.attributes['Protect'].set_value(True) + if self.locks.attributes['ProtEn'].value and self.locks.attributes['Protect'].value == 0: + self.locks.set_protect(True, reset=True) self.attributes['SafePosAct'].set_value(False) self._stop_drv() @@ -1606,29 +1555,6 @@ def set_trip(self, value: bool): _logger.debug('Trip set to %s' % value) self._expect_save_pos() - def set_permit(self, value: bool): - if not self.attributes['PermEn'].value: - value = True - self.attributes['Permit'].set_value(value) - _logger.debug('Permit set to %s' % value) - self._expect_save_pos() - - def set_interlock(self, value: bool): - if not self.attributes['IntlEn'].value: - value = True - self.attributes['Interlock'].set_value(value) - _logger.debug('Interlock set to %s' % value) - self._expect_save_pos() - - def set_protect(self, value: bool): - if not self.attributes['ProtEn'].value: - value = True - if value: - self._reset_drv() - self.attributes['Protect'].set_value(value) - _logger.debug('Protect set to %s' % value) - self._expect_save_pos() - def get_rpm(self): return self.attributes['Rpm'].value @@ -1867,7 +1793,7 @@ def set_stop_op(self, value: bool): self._stop_drv() self.attributes['StopOp'].set_value(False) - def set_reset_op(self, value: bool): + def _set_reset_op(self, value: bool): _logger.debug('ResetOp set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value and value: if self.attributes['MonEn'].value: diff --git a/src/mtppy/opcua_server_pea.py b/src/mtppy/opcua_server_pea.py index 02c196f..593a14a 100644 --- a/src/mtppy/opcua_server_pea.py +++ b/src/mtppy/opcua_server_pea.py @@ -34,7 +34,7 @@ def __init__(self, mtp_generator: MTPGenerator = None, endpoint: str = 'opc.tcp: """Folders that are created in the OPC UA server if found in a data assembly even if they are not of type SUCDataAssembly.""" - self._leaves = ['op_src_mode', 'state_machine', 'procedure_control'] + self._leaves = ['op_src_mode', 'state_machine', 'procedure_control', 'locks'] """Folders that are created in the OPC UA server if found in a data assembly even if they are not of type SUCDataAssembly. @@ -179,7 +179,8 @@ def _build_opcua_server(self): self.mtp.add_opcua_server(self.endpoint) # add service elements - self._create_opcua_element(self.service_set, "services") + if self.service_set.__len__() > 0: + self._create_opcua_element(self.service_set, "services") # add active, indicator and operation elements if self.active_elements.__len__() > 0: From 0f06a7e694e85a865f95b159d4e4ead2ac60f701 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Sat, 6 Sep 2025 12:05:51 +0200 Subject: [PATCH 43/61] made var names state_0 and state_1 more consistent. Plus some refactoring --- src/mtppy/operation_elements.py | 33 +++++++++++++++++---------------- src/mtppy/suc_data_assembly.py | 14 ++++++++++++++ 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/mtppy/operation_elements.py b/src/mtppy/operation_elements.py index b25817c..8d0fc01 100644 --- a/src/mtppy/operation_elements.py +++ b/src/mtppy/operation_elements.py @@ -31,7 +31,8 @@ def __init__(self, tag_name: str, tag_description: str = '', self._add_attribute(Attribute('VSclMin', float, init_value=v_scl_min)) self._add_attribute(Attribute('VSclMax', float, init_value=v_scl_max)) self._add_attribute(Attribute('VUnit', int, init_value=v_unit)) - self._add_attribute(Attribute('VMan', float, init_value=init_value, sub_cb=self.set_v_man)) + self._add_attribute( + Attribute('VMan', float, init_value=init_value, sub_cb=self._set_v_man)) self._add_attribute(Attribute('VMin', float, init_value=v_min)) self._add_attribute(Attribute('VMax', float, init_value=v_max)) self._add_attribute(Attribute('VRbk', float, init_value=init_value)) @@ -40,7 +41,7 @@ def __init__(self, tag_name: str, tag_description: str = '', def valid_value(self, value: float) -> bool: return self.v_min <= value <= self.v_max - def set_v_man(self, value: float): + def _set_v_man(self, value: float): _logger.debug(f"VMan set to {value}") if self.valid_value(value): self.set_v_out(value) @@ -86,7 +87,7 @@ def __init__(self, tag_name: str, tag_description: str = '', self._add_attribute(Attribute('WQC', int, init_value=0)) self._add_attribute(Attribute('VInt', float, init_value=init_value, sub_cb=self.set_v_int)) - def set_v_man(self, value: float): + def _set_v_man(self, value: float): _logger.debug(f"VMan set to {value}") if self.op_src_mode.attributes['SrcManAct'].value and self.valid_value(value): self.set_v_out(value) @@ -119,7 +120,7 @@ def __init__(self, tag_name: str, tag_description: str = '', self._add_attribute(Attribute('VSclMin', int, init_value=v_scl_min)) self._add_attribute(Attribute('VSclMax', int, init_value=v_scl_max)) self._add_attribute(Attribute('VUnit', int, init_value=v_unit)) - self._add_attribute(Attribute('VMan', int, init_value=init_value, sub_cb=self.set_v_man)) + self._add_attribute(Attribute('VMan', int, init_value=init_value, sub_cb=self._set_v_man)) self._add_attribute(Attribute('VMin', int, init_value=v_min)) self._add_attribute(Attribute('VMax', int, init_value=v_max)) self._add_attribute(Attribute('VRbk', int, init_value=init_value)) @@ -128,7 +129,7 @@ def __init__(self, tag_name: str, tag_description: str = '', def valid_value(self, value: int) -> bool: return self.v_min <= value <= self.v_max - def set_v_man(self, value: int): + def _set_v_man(self, value: int): _logger.debug(f"VMan set to {value}") if self.valid_value(value): self.set_v_out(value) @@ -174,7 +175,7 @@ def __init__(self, tag_name: str, tag_description: str = '', self._add_attribute(Attribute('WQC', int, init_value=0)) self._add_attribute(Attribute('VInt', int, init_value=init_value, sub_cb=self.set_v_int)) - def set_v_man(self, value: int): + def _set_v_man(self, value: int): _logger.debug(f"VMan set to {value}") if self.op_src_mode.attributes['SrcManAct'].value and self.valid_value(value): self.set_v_out(value) @@ -192,21 +193,21 @@ class BinMan(SUCOperationElement): """ def __init__(self, tag_name: str, tag_description: str = '', - v_state0: str = 'Off', v_state1: str = 'On', + v_state_0: str = 'Off', v_state_1: str = 'On', init_value: bool = False): super().__init__(tag_name, tag_description) - self.v_state0 = v_state0 - self.v_state1 = v_state1 + self.v_state_0 = v_state_0 + self.v_state_1 = v_state_1 self._add_attribute(Attribute('VOut', bool, init_value=init_value)) - self._add_attribute(Attribute('VState0', str, init_value=v_state0)) - self._add_attribute(Attribute('VState1', str, init_value=v_state1)) - self._add_attribute(Attribute('VMan', bool, init_value=init_value, sub_cb=self.set_v_man)) + self._add_attribute(Attribute('VState0', str, init_value=v_state_0)) + self._add_attribute(Attribute('VState1', str, init_value=v_state_1)) + self._add_attribute(Attribute('VMan', bool, init_value=init_value, sub_cb=self._set_v_man)) self._add_attribute(Attribute('VRbk', bool, init_value=init_value)) self._add_attribute(Attribute('VFbk', bool, init_value=init_value)) - def set_v_man(self, value: bool): + def _set_v_man(self, value: bool): _logger.debug(f"VMan set to {value}") self.set_v_out(value) @@ -235,10 +236,10 @@ class BinManInt(BinMan): """ def __init__(self, tag_name: str, tag_description: str = '', - v_state0: str = 'Off', v_state1: str = 'On', + v_state_0: str = 'Off', v_state_1: str = 'On', init_value: bool = False): super().__init__(tag_name, tag_description, - v_state0=v_state0, v_state1=v_state1, + v_state_0=v_state_0, v_state_1=v_state_1, init_value=init_value) self.op_src_mode = SourceMode() @@ -247,7 +248,7 @@ def __init__(self, tag_name: str, tag_description: str = '', self._add_attribute(Attribute('WQC', int, init_value=0)) self._add_attribute(Attribute('VInt', bool, init_value=init_value, sub_cb=self.set_v_int)) - def set_v_man(self, value: bool): + def _set_v_man(self, value: bool): _logger.debug(f"VMan set to {value}") if self.op_src_mode.attributes['SrcManAct'].value: self.set_v_out(value) diff --git a/src/mtppy/suc_data_assembly.py b/src/mtppy/suc_data_assembly.py index 2a851f7..1aff7ec 100644 --- a/src/mtppy/suc_data_assembly.py +++ b/src/mtppy/suc_data_assembly.py @@ -34,6 +34,20 @@ def __init__(self, tag_name: str, tag_description: str): super().__init__(tag_name, tag_description) self._add_attribute(Attribute('OSLevel', int, init_value=0)) + @abstractmethod + def set_v_int(self, value): + """ + Set VInt if StateIntAct. + """ + pass + + @abstractmethod + def get_v_out(self): + """ + Get the current value of VOut (current operation value). + """ + pass + class SUCParameterElement(SUCDataAssembly): def __init__(self, tag_name: str, tag_description: str): From e1d8b0e448f4afc6479bb31102bfdbc3d2cd19f6 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Sat, 6 Sep 2025 12:06:24 +0200 Subject: [PATCH 44/61] bugfix and changed op mode to start as Aut and src mode to start as Int --- src/mtppy/operation_source_mode.py | 31 +++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/mtppy/operation_source_mode.py b/src/mtppy/operation_source_mode.py index ba1946e..ab4d939 100644 --- a/src/mtppy/operation_source_mode.py +++ b/src/mtppy/operation_source_mode.py @@ -294,14 +294,14 @@ def __init__(self, name_of_parent: str = ''): # --- Operation mode control --- 'StateChannel': Attribute('StateChannel', bool, init_value=True, sub_cb=self.set_state_channel), 'StateOffAut': Attribute('StateOffAut', bool, init_value=False, sub_cb=self.set_state_off_aut), - 'StateOpAut': Attribute('StateOpAut', bool, init_value=True, sub_cb=self.set_state_op_aut), + 'StateOpAut': Attribute('StateOpAut', bool, init_value=False, sub_cb=self.set_state_op_aut), 'StateAutAut': Attribute('StateAutAut', bool, init_value=False, sub_cb=self.set_state_aut_aut), 'StateOffOp': Attribute('StateOffOp', bool, init_value=False, sub_cb=self.set_state_off_op), 'StateOpOp': Attribute('StateOpOp', bool, init_value=False, sub_cb=self.set_state_op_op), 'StateAutOp': Attribute('StateAutOp', bool, init_value=False, sub_cb=self.set_state_aut_op), - 'StateOpAct': Attribute('StateOpAct', bool, init_value=True), - 'StateAutAct': Attribute('StateAutAct', bool, init_value=False), + 'StateOpAct': Attribute('StateOpAct', bool, init_value=False), + 'StateAutAct': Attribute('StateAutAct', bool, init_value=True), 'StateOffAct': Attribute('StateOffAct', bool, init_value=False) }) @@ -364,7 +364,7 @@ def set_state_op_op(self, value: bool): if self.attributes['StateOffAct'].value or self.attributes['StateAutAct'].value: self._opmode_to_op() if value: - self.attributes['StateOffOp'].set_value(False) + self.attributes['StateOpOp'].set_value(False) def set_state_aut_op(self, value: bool): _logger.debug(f'{self._name_of_parent}StateAutOp set to {value}') @@ -372,7 +372,17 @@ def set_state_aut_op(self, value: bool): if self.attributes['StateOffAct'].value or self.attributes['StateOpAct'].value: self._opmode_to_aut() if value: - self.attributes['StateOffOp'].set_value(False) + self.attributes['StateAutOp'].set_value(False) + + # --- Accessors for operation mode state --- + def is_opmode_off(self) -> bool: + return self.attributes['StateOffAct'].value + + def is_opmode_aut(self) -> bool: + return self.attributes['StateAutAct'].value + + def is_opmode_op(self) -> bool: + return self.attributes['StateOpAct'].value class SourceMode(): @@ -387,11 +397,11 @@ def __init__(self, name_of_parent: str = ''): # --- Source mode control --- 'SrcChannel': Attribute('SrcChannel', bool, init_value=True, sub_cb=self.set_src_channel), 'SrcManAut': Attribute('SrcManAut', bool, init_value=False, sub_cb=self.set_src_man_aut), - 'SrcIntAut': Attribute('SrcIntAut', bool, init_value=True, sub_cb=self.set_src_int_aut), + 'SrcIntAut': Attribute('SrcIntAut', bool, init_value=False, sub_cb=self.set_src_int_aut), 'SrcIntOp': Attribute('SrcIntOp', bool, init_value=False, sub_cb=self.set_src_int_op), 'SrcManOp': Attribute('SrcManOp', bool, init_value=False, sub_cb=self.set_src_man_op), - 'SrcIntAct': Attribute('SrcIntAct', bool, init_value=False), + 'SrcIntAct': Attribute('SrcIntAct', bool, init_value=True), 'SrcManAct': Attribute('SrcManAct', bool, init_value=False) }) @@ -434,6 +444,13 @@ def set_src_int_op(self, value: bool): if value: self.attributes['SrcIntOp'].set_value(False) + # --- Accessors for source mode state --- + def is_srcmode_int(self) -> bool: + return self.attributes['SrcIntAct'].value + + def is_srcmode_man(self) -> bool: + return self.attributes['SrcManAct'].value + class OperationSourceModeElement(OperationMode, SourceMode): def __init__(self, name_of_parent: str = ''): From 2a52bc8e6ab772426b4077afcffc515b3b8452af Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Wed, 10 Sep 2025 09:43:22 +0200 Subject: [PATCH 45/61] removed call scheme from service state methods implemented in a599378. Plus some refactoring --- src/mtppy/opcua_server_pea.py | 2 +- src/mtppy/service.py | 223 +++++++++++++++------------------- 2 files changed, 96 insertions(+), 129 deletions(-) diff --git a/src/mtppy/opcua_server_pea.py b/src/mtppy/opcua_server_pea.py index 593a14a..c53da15 100644 --- a/src/mtppy/opcua_server_pea.py +++ b/src/mtppy/opcua_server_pea.py @@ -162,7 +162,7 @@ def set_services_in_idle(self): Sets all services to idle state. """ for service in self.service_set.values(): - service.init_idle_state() + service._init_idle_state() def _build_opcua_server(self): """ diff --git a/src/mtppy/service.py b/src/mtppy/service.py index 54f051b..425d41e 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -28,20 +28,30 @@ class Service(SUCServiceControl): - execute - completing - Other methods can be overridden as needed. - By default the higher level methods (on the right side) will call the lower level methods. - See the following diagram for how the methods are called: - - +------------+ +------------+ +------------+ +------------+ +------------+ - | completing | <- | pausing | <- | holding | <- | stopping | <- | aborting | - +------------+ +------------+ +------------+ +------------+ +------------+ - +------------+ +------------+ +------------+ +------------+ - || | paused | <- | held | <- | stopped | <- | aborted | - +------------+ +------------+ +------------+ +------------+ - +------------+ +------------+ +------------+ - | starting | <- | resuming | <- | unholding | - +------------+ +------------+ +------------+ - + Additionally, you may override other state methods to implement specific behavior for + those states. Those include: + - completed + - stopping + - stopped + - aborting + - aborted + - resetting + + Some states are part of control loops that can be enabled or disabled. These states include: + - pause_loop + - pausing + - paused + - resuming + - hold_loop + - holding + - held + - unholding + + Methods ending with "ing" are transitional states. After running once they will automatically + transition to the next state. + If a function runs in a loop, it must check for the current state using `is_state(state_str)` + or it must check for the stop event using `get_state_stop_event()`. Because those indicate + that a state transition has been requested and the function must return. """ def __init__(self, tag_name: str, tag_description: str, exception_callback: Callable[[Exception], None] = None): @@ -52,7 +62,7 @@ def __init__(self, tag_name: str, tag_description: str, exception_callback: Call tag_name (str): Tag name of the service. tag_description (str): Tag description of the service. exception_callback (Callable[[Exception], None]): Function to call - when an exception occurs in the thread. + when an exception occurs in the thread. If None just logs the exception. """ super().__init__(tag_name, tag_description) self.exception = None @@ -66,10 +76,10 @@ def __init__(self, tag_name: str, tag_description: str, exception_callback: Call self.state_machine = StateMachine(operation_source_mode=self.op_src_mode, procedure_control=self.procedure_control, - execution_routine=self.state_change_callback) + execution_routine=self._state_change_callback) self.thread_ctrl = ThreadControl(service_name=tag_name, - state_change_function=self.state_change, + state_change_function=self._state_change, exception_callback=exception_callback) self.op_src_mode.add_enter_offline_callback(self.state_machine.command_en_ctrl.disable_all) @@ -78,8 +88,8 @@ def __init__(self, tag_name: str, tag_description: str, exception_callback: Call self.op_src_mode.add_exit_offline_callback(self.state_machine.command_en_ctrl.set_default) self.op_src_mode.add_exit_offline_callback(self.state_machine.update_command_en) - self.op_src_mode.add_exit_offline_callback(self.apply_configuration_parameters) - self.op_src_mode.add_exit_offline_callback(self.init_idle_state) + self.op_src_mode.add_exit_offline_callback(self._apply_configuration_parameters) + self.op_src_mode.add_exit_offline_callback(self._init_idle_state) self.op_src_mode.add_enter_operator_callback( # adds function to disable commands if no procedure is set @@ -107,14 +117,14 @@ def enable_pause_loop(self, enable: bool = True): """ self.state_machine.command_en_ctrl.enable_pause_loop(enable) - def enable_restart(self, enable: bool = True): + def enable_hold_loop(self, enable: bool = True): """ - Enables or disables the restart command. + Enables or disables the hold loop. Args: - enable (bool): If True, enables the restart command. If False, disables it. + enable (bool): If True, enables the hold loop. If False, disables it. """ - self.state_machine.command_en_ctrl.enable_restart(enable) + self.state_machine.command_en_ctrl.enable_hold_loop(enable) def enable_restart(self, enable: bool = True): """ @@ -125,36 +135,39 @@ def enable_restart(self, enable: bool = True): """ self.state_machine.command_en_ctrl.enable_restart(enable) - def get_current_procedure(self) -> Procedure: + def add_configuration_parameter(self, configuration_parameter: SUCParameterElement): """ - Returns the current procedure. + Adds a configuration parameter to the service. - Returns: - Procedure: The current procedure. + Args: + configuration_parameter (SUCParameterElement): Configuration parameter to add. """ - return self.procedures[self.procedure_control.get_procedure_cur()] + self.configuration_parameters[configuration_parameter.tag_name] = configuration_parameter - def init_idle_state(self): - """ - Initializes the idle state. + def add_procedure(self, procedure: Procedure): """ - self.state_change_callback() + Adds a procedure to the service. - def state_change_callback(self): + Args: + procedure (Procedure): Procedure to add. """ - Callback for state changes. + self.procedures[procedure.attributes['ProcedureId'].value] = procedure + if procedure.attributes['IsDefault'].value: + self.procedure_control.default_procedure_id = procedure.attributes['ProcedureId'].value + self.procedure_control.attributes['ProcedureOp'].init_value = self.procedure_control.default_procedure_id + self.procedure_control.attributes['ProcedureInt'].init_value = self.procedure_control.default_procedure_id + self.procedure_control.attributes['ProcedureExt'].init_value = self.procedure_control.default_procedure_id + self.procedure_control.attributes['ProcedureReq'].init_value = self.procedure_control.default_procedure_id + self.procedure_control.set_procedure_req(self.procedure_control.default_procedure_id) + + def get_current_procedure(self) -> Procedure: """ - if self.op_src_mode.attributes['StateOffAct'].value: - return + Returns the current procedure. - state_str = self.state_machine.get_current_state_str() - function_to_execute = eval(f'self.{state_str}') - self.thread_ctrl.request_state(state_str, function_to_execute) - self.thread_ctrl.reallocate_running_thread() - if state_str == 'idle': - self.op_src_mode.allow_switch_to_offline_mode(True) - else: - self.op_src_mode.allow_switch_to_offline_mode(False) + Returns: + Procedure: The current procedure. + """ + return self.procedures[self.procedure_control.get_procedure_cur()] def is_state(self, state_str): """ @@ -185,17 +198,39 @@ def get_state_stop_event(self) -> Event: return self.thread_ctrl.thread.stop_event - def state_change(self): + def _init_idle_state(self): + """ + Initializes the idle state. + """ + self._state_change_callback() + + def _state_change_callback(self): + """ + Callback for state changes. + """ + if self.op_src_mode.attributes['StateOffAct'].value: + return + + state_str = self.state_machine.get_current_state_str() + function_to_execute = eval(f'self.{state_str}') + self.thread_ctrl.request_state(state_str, function_to_execute) + self.thread_ctrl.reallocate_running_thread() + if state_str == 'idle': + self.op_src_mode.allow_switch_to_offline_mode(True) + else: + self.op_src_mode.allow_switch_to_offline_mode(False) + + def _state_change(self): """ - Changes the state. Has to be called by each transitional state method. + Changes the state. Is automatically called after each state function returns. """ # don't automatically change state for non self-completing procedures if self.state_machine.get_current_state_str() == "execute": - if not self.is_self_completing(): + if not self._is_self_completing(): return self.state_machine.state_change() - def is_self_completing(self) -> bool: + def _is_self_completing(self) -> bool: """ Checks if the current Procedure is self-completing. @@ -204,16 +239,7 @@ def is_self_completing(self) -> bool: """ return self.get_current_procedure().attributes['IsSelfCompleting'].value - def add_configuration_parameter(self, configuration_parameter: SUCParameterElement): - """ - Adds a configuration parameter to the service. - - Args: - configuration_parameter (SUCParameterElement): Configuration parameter to add. - """ - self.configuration_parameters[configuration_parameter.tag_name] = configuration_parameter - - def apply_configuration_parameters(self): + def _apply_configuration_parameters(self): """ Applies configuration parameters. """ @@ -221,22 +247,6 @@ def apply_configuration_parameters(self): for configuration_parameter in self.configuration_parameters.values(): configuration_parameter.set_v_out() - def add_procedure(self, procedure: Procedure): - """ - Adds a procedure to the service. - - Args: - procedure (Procedure): Procedure to add. - """ - self.procedures[procedure.attributes['ProcedureId'].value] = procedure - if procedure.attributes['IsDefault'].value: - self.procedure_control.default_procedure_id = procedure.attributes['ProcedureId'].value - self.procedure_control.attributes['ProcedureOp'].init_value = self.procedure_control.default_procedure_id - self.procedure_control.attributes['ProcedureInt'].init_value = self.procedure_control.default_procedure_id - self.procedure_control.attributes['ProcedureExt'].init_value = self.procedure_control.default_procedure_id - self.procedure_control.attributes['ProcedureReq'].init_value = self.procedure_control.default_procedure_id - self.procedure_control.set_procedure_req(self.procedure_control.default_procedure_id) - def idle(self): """ Idle state. @@ -253,7 +263,7 @@ def starting(self): """ Starting state. """ - self.state_change() + self._state_change() @abstractmethod def execute(self): @@ -267,7 +277,7 @@ def completing(self): """ Completing state. """ - self.state_change() + pass def completed(self): """ @@ -275,19 +285,13 @@ def completed(self): """ if self.state_machine.get_current_state_str() == "completed": _logger.debug(f"{self.tag_name} - Completed -") - else: - pass def pausing(self): """ - Pausing state. If not overridden, it will call the completing method. + Pausing state. """ if self.state_machine.get_current_state_str() == "pausing": _logger.debug(f"{self.tag_name} - Pausing -") - else: - pass - # call the completing method to also execute the logic for the completing state - self.completing() def paused(self): """ @@ -295,96 +299,62 @@ def paused(self): """ if self.state_machine.get_current_state_str() == "paused": _logger.debug(f"{self.tag_name} - Paused -") - else: - pass def resuming(self): """ - Resuming state. If not overridden, it will call the starting method. + Resuming state. """ if self.state_machine.get_current_state_str() == "resuming": _logger.debug(f"{self.tag_name} - Resuming -") - else: - pass - # call the starting method to also execute the logic for the starting state - self.starting() def holding(self): """ - Holding state. If not overridden, it will call the pausing method. + Holding state. """ if self.state_machine.get_current_state_str() == "holding": _logger.debug(f"{self.tag_name} - Holding -") - else: - pass - # call the pausing method to also execute the logic for the pausing state - self.pausing() def held(self): """ - Held state. If not overridden, it will call the paused method. + Held state. """ if self.state_machine.get_current_state_str() == "held": _logger.debug(f"{self.tag_name} - Held -") - else: - pass - # call the paused method to also execute the logic for the paused state - self.paused() def unholding(self): """ - Unholding state. If not overridden, it will call the resuming method. + Unholding state. """ if self.state_machine.get_current_state_str() == "unholding": _logger.debug(f"{self.tag_name} - Unholding -") - else: - pass - # call the resuming method to also execute the logic for the resuming state - self.resuming() def stopping(self): """ - Stopping state. If not overridden, it will call the holding method. + Stopping state. """ if self.state_machine.get_current_state_str() == "stopping": _logger.debug(f"{self.tag_name} - Stopping -") - else: - pass - # call the holding method to also execute the logic for the holding state - self.holding() def stopped(self): """ - Stopped state. If not overridden, it will call the held method. + Stopped state. """ if self.state_machine.get_current_state_str() == "stopped": _logger.debug(f"{self.tag_name} - Stopped -") - else: - pass - # call the held method to also execute the logic for the held state - self.held() def aborting(self): """ - Aborting state. If not overridden, it will call the stopping method. + Aborting state. """ if self.state_machine.get_current_state_str() == "aborting": _logger.debug(f"{self.tag_name} - Aborting -") - else: - pass - # call the stopping method to also execute the logic for the stopping state - self.stopping() def aborted(self): """ - Aborted state. If not overridden, it will call the stopped method. + Aborted state. """ if self.state_machine.get_current_state_str() == "aborted": _logger.debug(f"{self.tag_name} - Aborted -") - else: - pass - # call the stopped method to also execute the logic for the stopped state - self.stopped() def resetting(self): """ @@ -392,6 +362,3 @@ def resetting(self): """ if self.state_machine.get_current_state_str() == "resetting": _logger.debug(f"{self.tag_name} - Resetting -") - else: - pass - # Reset the state machine to idle From 565278e0215cd78878b78092f9761c34b9ab3da9 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Fri, 19 Sep 2025 11:33:34 +0200 Subject: [PATCH 46/61] Some refactoring, added SUCServiceElement --- src/mtppy/active_elements.py | 4 ++- src/mtppy/service.py | 3 -- src/mtppy/suc_data_assembly.py | 60 ++++++++++++++++++---------------- 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/mtppy/active_elements.py b/src/mtppy/active_elements.py index 974c87a..3f843fa 100644 --- a/src/mtppy/active_elements.py +++ b/src/mtppy/active_elements.py @@ -1,6 +1,5 @@ import logging import threading -from abc import abstractmethod from collections.abc import Callable from mtppy.attribute import Attribute @@ -796,6 +795,9 @@ def get_open_fbk(self): def get_close_fbk(self): return self.attributes['CloseFbk'].value + def get_ctrl(self): + return self.attributes['Ctrl'].value + class MonBinVlvValues: def __init__(self, open_aut, open_op, close_aut, close_op, reset_aut, reset_op): diff --git a/src/mtppy/service.py b/src/mtppy/service.py index 425d41e..daef183 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -10,14 +10,11 @@ from mtppy.operation_source_mode import OperationSourceMode from mtppy.state_machine import StateMachine from mtppy.procedure_control import ProcedureControl -from mtppy.state_codes import StateCodes from mtppy.procedure import Procedure from mtppy.suc_data_assembly import SUCParameterElement _logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") -StateCodes = StateCodes() - class Service(SUCServiceControl): """Represents a service of the PEA. diff --git a/src/mtppy/suc_data_assembly.py b/src/mtppy/suc_data_assembly.py index 1aff7ec..16121e6 100644 --- a/src/mtppy/suc_data_assembly.py +++ b/src/mtppy/suc_data_assembly.py @@ -49,63 +49,67 @@ def get_v_out(self): pass -class SUCParameterElement(SUCDataAssembly): +class SUCActiveElement(SUCDataAssembly): def __init__(self, tag_name: str, tag_description: str): super().__init__(tag_name, tag_description) self._add_attribute(Attribute('OSLevel', int, init_value=0)) self._add_attribute(Attribute('WQC', int, init_value=255)) - @abstractmethod - def set_v_int(self, value): - """ - Set Parameter value if StateIntAct. - """ - pass - - @abstractmethod - def get_v_out(self): - """ - Get the current value of VOut (current parameter value). - """ - pass - @abstractmethod - def set_v_fbk(self, value): - """ - Set VFbk (feedback value) to the specified value. - """ - pass +class SUCDiagnosticElement(SUCDataAssembly): + def __init__(self, tag_name: str, tag_description: str): + super().__init__(tag_name, tag_description) + self._add_attribute(Attribute('WQC', int, init_value=255)) -class SUCActiveElement(SUCDataAssembly): +class SUCServiceElement(SUCDataAssembly): def __init__(self, tag_name: str, tag_description: str): super().__init__(tag_name, tag_description) - self._add_attribute(Attribute('OSLevel', int, init_value=0)) self._add_attribute(Attribute('WQC', int, init_value=255)) -class SUCServiceControl(SUCDataAssembly): +class SUCServiceControl(SUCServiceElement): def __init__(self, tag_name: str, tag_description: str): super().__init__(tag_name, tag_description) self._add_attribute(Attribute('OSLevel', int, init_value=0)) - self._add_attribute(Attribute('WQC', int, init_value=255)) self._add_attribute(Attribute('PosTextID', int, init_value=0)) self._add_attribute(Attribute('InteractQuestionID', int, init_value=0)) self._add_attribute(Attribute('InteractAnswerID', int, init_value=0)) -class SUCDiagnosticElement(SUCDataAssembly): +class SUCProcedureHealthView(SUCServiceElement): def __init__(self, tag_name: str, tag_description: str): super().__init__(tag_name, tag_description) - self._add_attribute(Attribute('WQC', int, init_value=255)) -class SUCHealthStateView(SUCDiagnosticElement): +class SUCParameterElement(SUCServiceElement): def __init__(self, tag_name: str, tag_description: str): super().__init__(tag_name, tag_description) + self._add_attribute(Attribute('OSLevel', int, init_value=0)) + + @abstractmethod + def set_v_int(self, value): + """ + Set Parameter value if StateIntAct. + """ + pass + + @abstractmethod + def get_v_out(self): + """ + Get the current value of VOut (current parameter value). + """ + pass + + @abstractmethod + def set_v_fbk(self, value): + """ + Set VFbk (feedback value) to the specified value. + """ + pass -class SUCServiceProcedure(SUCHealthStateView): +class SUCServiceProcedure(SUCProcedureHealthView): def __init__(self, procedure_id: int, tag_name: str, tag_description: str, is_self_completing=False, is_default=True): super().__init__(tag_name, tag_description) From 3faae6865ceddcaa7dcd48507bf344b2543dd4d2 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Sat, 18 Oct 2025 18:00:13 +0200 Subject: [PATCH 47/61] fixed wrong code --- src/mtppy/state_codes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mtppy/state_codes.py b/src/mtppy/state_codes.py index add3ff1..0a6c349 100644 --- a/src/mtppy/state_codes.py +++ b/src/mtppy/state_codes.py @@ -17,7 +17,7 @@ def __init__(self): self.unholding = 4096 self.pausing = 8192 self.resuming = 16384 - self.resetting = 32678 + self.resetting = 32768 self.completing = 65536 self.completed = 131072 @@ -36,7 +36,7 @@ def __init__(self): self.int_code[4096] = 'unholding' self.int_code[8192] = 'pausing' self.int_code[16384] = 'resuming' - self.int_code[32678] = 'resetting' + self.int_code[32768] = 'resetting' self.int_code[65536] = 'completing' self.int_code[131072] = 'completed' From 0ec16e0531f1a12e1d5998a30c023c628581587b Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Sat, 18 Oct 2025 18:00:45 +0200 Subject: [PATCH 48/61] better handling command reset --- src/mtppy/state_machine.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/mtppy/state_machine.py b/src/mtppy/state_machine.py index b6a0a42..8bec0ac 100644 --- a/src/mtppy/state_machine.py +++ b/src/mtppy/state_machine.py @@ -55,8 +55,12 @@ def set_command_ext(self, value: int): self.command_execution(value) def command_execution(self, com_var: int): + # skip if command code was just reset to 0 + if com_var == 0: + return + if com_var not in CommandCodes.get_list_int(): - _logger.debug(f'Command Code {com_var} does not exist') + _logger.warning(f'Command Code {com_var} does not exist') return cmd_str = CommandCodes.int_code[com_var] From df5af5e70419945fdb7aeb169a405ca144165af1 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Sat, 18 Oct 2025 18:01:21 +0200 Subject: [PATCH 49/61] fix for running state being set to late --- src/mtppy/thread_control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mtppy/thread_control.py b/src/mtppy/thread_control.py index 3fe32d2..7cd8ebb 100644 --- a/src/mtppy/thread_control.py +++ b/src/mtppy/thread_control.py @@ -62,8 +62,8 @@ def reallocate_running_thread(self): self.thread = StoppableThread(target=self.run_thread, args=(self.callback_function,), name=f"{self.service_name}_{self.requested_state}") - self.thread.start() self.running_state = self.requested_state + self.thread.start() def run_thread(self, target_function: Callable): """ From 263968c14a52dd33d272c56d82fbecbed15c84c8 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Sat, 18 Oct 2025 18:02:41 +0200 Subject: [PATCH 50/61] added new class diagram --- res/architecture/class_diagram.drawio | 2835 +++++++++++++++++++++++++ res/architecture/class_entitys.txt | 737 +++++++ 2 files changed, 3572 insertions(+) create mode 100644 res/architecture/class_diagram.drawio create mode 100644 res/architecture/class_entitys.txt diff --git a/res/architecture/class_diagram.drawio b/res/architecture/class_diagram.drawio new file mode 100644 index 0000000..e7fd9a6 --- /dev/null +++ b/res/architecture/class_diagram.drawio @@ -0,0 +1,2835 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/architecture/class_entitys.txt b/res/architecture/class_entitys.txt new file mode 100644 index 0000000..619cd4e --- /dev/null +++ b/res/architecture/class_entitys.txt @@ -0,0 +1,737 @@ +Locks ++perm_en: bool ++intl_en: bool ++prot_en: bool ++attributes: dict +-- ++permit_status(): bool ++interlock_status(): bool ++protect_status(): bool +-set_permit(bool): None +-set_interlock(bool): None +-set_protect(bool, reset=False): None +-_run_allowed(): bool + +AnaVlv ++op_src_mode: OperationSourceModeElement ++locks: Locks ++pos_min: float ++pos_max: float ++pos_scl_min: float ++pos_scl_max: float ++pos_unit: int ++open_fbk_calc: bool ++close_fbk_calc: bool ++pos_fbk_calc: bool ++safe_pos: int ++safe_pos_en: bool +-- ++get_pos(): float ++get_pos_rbk(): float ++get_pos_fbk(): float ++get_open_fbk(): bool ++get_close_fbk(): bool +-set_open_aut(bool): None +-set_close_aut(bool): None +-set_reset_aut(bool): None +-set_open_op(bool): None +-set_close_op(bool): None +-set_pos_int(float): None +-set_pos_man(float): None +-set_pos_rbk(float): None +-set_pos_fbk(float): None +-set_open_fbk(bool): None +-set_close_fbk(bool): None +-_expect_save_pos(): None +-_go_save_pos(): None +-_run_allowed(): bool +-_set_pos(float): None + +MonAnaVlv ++mon_en: bool ++mon_safe_pos: bool ++mon_stat_ti: float ++mon_dyn_ti: float ++pos_tolerance: float +-- ++start_monitor(): None ++set_stop_monitor(): None +-monitor_static_error(): None +-monitor_dynamic_error(): None +-monitor_position_reached(): None +-_handle_monitored_error(): None + +BinVlv ++op_src_mode: OperationMode ++locks: Locks ++open_fbk_calc: bool ++close_fbk_calc: bool ++safe_pos: int ++safe_pos_en: bool +-- ++get_open_fbk(): bool ++get_close_fbk(): bool ++get_ctrl(): bool +-set_open_aut(bool): None +-set_close_aut(bool): None +-set_reset_aut(bool): None +-set_open_op(bool): None +-set_close_op(bool): None +-set_ctrl(bool): None +-_expect_save_pos(): None +-_go_safe_pos(): None +-_run_allowed(bool): bool + +MonBinVlv ++mon_en: bool ++mon_safe_pos: bool ++mon_stat_ti: float ++mon_dyn_ti: float +-- ++start_monitor(): None ++set_stop_monitor(): None +-monitor_static_error(): None +-monitor_dynamic_error(): None +-_handle_monitored_error(): None + +BinDrv ++op_src_mode: OperationMode ++locks: Locks ++rev_fbk_calc: bool ++fwd_fbk_calc: bool ++safe_pos: int ++fwd_en: bool ++rev_en: bool +-- ++get_fwd_fbk(): bool ++get_rev_fbk(): bool +-set_fwd_op(bool): None +-set_rev_op(bool): None +-set_stop_op(bool): None +-set_fwd_aut(bool): None +-set_rev_aut(bool): None +-set_stop_aut(bool): None +-_expect_save_pos(): None +-_go_safe_pos(): None +-_run_allowed(): bool + +MonBinDrv ++mon_en: bool ++mon_safe_pos: bool ++mon_stat_ti: float ++mon_dyn_ti: float +-- ++start_monitor(): None ++set_stop_monitor(): None +-monitor_static_error(): None +-monitor_dynamic_error(): None +-_handle_monitored_error(): None + +AnaDrv ++op_src_mode: OperationSourceModeElement ++locks: Locks ++rpm_min: float ++rpm_max: float ++rpm_scl_min: float ++rpm_scl_max: float ++rpm_unit: int ++rev_fbk_calc: bool ++fwd_fbk_calc: bool ++rpm_fbk_calc: bool ++safe_pos: int ++fwd_en: bool ++rev_en: bool +-- ++get_rpm(): float ++get_rpm_rbk(): float ++get_rpm_fbk(): float +-set_fwd_op(bool): None +-set_rev_op(bool): None +-set_stop_op(bool): None +-set_fwd_aut(bool): None +-set_rev_aut(bool): None +-set_stop_aut(bool): None +-set_rpm_int(float): None +-set_rpm_man(float): None +-set_rpm_rbk(float): None +-set_rpm_fbk(float): None +-set_trip(bool): None +-_expect_save_pos(): None +-_go_save_pos(): None +-_run_allowed(): bool + +MonAnaDrv ++mon_en: bool ++mon_safe_pos: bool ++mon_stat_ti: float ++mon_dyn_ti: float ++rpm_ah_en: bool ++rpm_al_en: bool ++rpm_ah_lim: float ++rpm_al_lim: float +-- ++start_monitor(): None ++set_stop_monitor(): None +-monitor_static_error(): None +-monitor_dynamic_error(): None +-monitor_rpm_error(): None +-monitor_rpm_high_limit(): None +-monitor_rpm_low_limit(): None +-_handle_monitored_error(): None + +PIDController ++kp: float ++ki: float ++kd: float ++mv_min: float ++mv_max: float ++sp: float ++pv: float ++mv: float ++sample_time: float +-- ++start(): None ++stop(): None ++set_sp(float): None ++set_pv(float): None ++get_mv(): float ++set_kp(float): None ++set_ki(float): None ++set_kd(float): None + +PIDCtrl ++op_src_mode: OperationSourceModeElement ++pv_scl_min: float ++pv_scl_max: float ++pv_unit: int ++sp_scl_min: float ++sp_scl_max: float ++sp_unit: int ++sp_int_min: float ++sp_int_max: float ++sp_man_min: float ++sp_man_max: float ++mv_min: float ++mv_max: float ++mv_unit: int ++mv_scl_min: float ++mv_scl_max: float ++P: float ++Ti: float ++Td: float +-- ++get_mv(): float ++get_sp(): float ++set_pv(float): None ++set_sp_man(float): None ++set_sp_int(float): None ++set_mv_man(float): None +-set_p(float): None +-set_ti(float): None +-set_td(float): None +-_start_aut_mv_update_loop(): None +-_stop_aut_mv_update_loop(): None + +Attribute ++name: str ++type: type ++init_value: Any ++value: Any ++comm_obj: Any ++sub_cb: Callable[[Any], None] +-- ++set_value(Any): bool ++attach_communication_object(Any): None ++attach_subscription_callback(Callable[[Any], None]): None ++remove_subscription_callback(): None +-_correct_type(Any): Any + +CommandCodes ++reset: int ++start: int ++stop: int ++hold: int ++unhold: int ++pause: int ++resume: int ++abort: int ++restart: int ++complete: int ++int_code: dict +-- ++get_list_int(): list ++get_list_str(): list + +CommandEnControl ++command_en: dict ++hold_enabled: bool ++pause_enabled: bool ++restart_enabled: bool +-- ++set_default(): None ++disable_all(): None ++is_enabled(str): bool ++get_command_en(): int ++set_command_en(str, bool): None ++enable_hold_loop(bool): None ++enable_pause_loop(bool): None ++enable_restart(bool): None ++disable_restart_temporarily(): None ++restore_restart(): None ++execute(str): None + +OPCUACommunicationObject ++opcua_node_obj: object ++node_id: str ++write_value_callback: Callable +-- + +AnaView ++v_scl_min: float ++v_scl_max: float ++v_unit: int +-- ++set_v(float): None + +BinView ++v_state_0: str ++v_state_1: str +-- ++set_v(bool): None + +DIntView ++v_scl_min: int ++v_scl_max: int ++v_unit: int +-- ++set_v(int): None + +StringView +-- ++set_v(str): None + +MTPGenerator ++template_path: str ++writer_infos: dict ++tree: ET.ElementTree ++root: ET.Element ++export_path: str ++instance_hierarchy_mtp: ET.Element ++instance_hierarchy_service: ET.Element ++module_type_package: ET.Element ++communication_set: ET.Element ++services_set: ET.Element ++instance_list: ET.Element ++source_list: ET.Element ++opcua_server: ET.Element ++service: ET.Element ++service_procedure: ET.Element +-- ++edit_writer_information(): None ++add_module_type_package(str, str, str): None ++create_instance(SUCDataAssembly, str): ET.Element ++add_data_assembly_to_instance_list(str, str, str, str): ET.Element ++create_components_for_services(SUCDataAssembly, str): str ++add_components_to_services(SUCDataAssembly, str, str): str ++add_opcua_server(str): None ++add_external_interface(str, int, str, int): None ++apply_add_supported_role_class(): None ++export_manifest(): None ++pretty_print(ET.Element, int): None +-_init_manifest(): None +-_init_instance_hierarchies(): None +-_add_communication_set(): None +-_add_services_set(str): None +-_add_service_to_service_set(str, str, str): None +-_add_linked_attr(ET.Element, str): None +-_generate_attributes(ET.Element, str, str, str): None +-_random_id_generator(): str + +OPCUAServerPEA ++service_set: dict[str, Service] ++active_elements: dict[str, SUCActiveElement] ++indicator_elements: dict[str, SUCIndicatorElement] ++operation_elements: dict[str, SUCOperationElement] ++custom_data_assembly_sets: dict[str, dict[str, SUCDataAssembly]] ++endpoint: str ++opcua_server: Server ++opcua_ns: int ++subscription_list: SubscriptionList ++mtp: MTPGenerator ++_folders: list[str] ++_leaves: list[str] +-- ++add_service(Service): None ++add_active_element(SUCActiveElement): None ++add_indicator_element(SUCIndicatorElement): None ++add_operation_element(SUCOperationElement): None ++add_custom_data_assembly_set(str, dict[str, SUCDataAssembly]): None ++add_folders(list[str]): None ++add_leaves(list[str]): None ++get_opcua_server(): Server ++get_opcua_ns(): int ++run_opcua_server(): None ++set_services_in_idle(): None +-_init_opcua_server(): None +-_build_opcua_server(): None +-_create_opcua_element(dict[str, SUCDataAssembly], str): None +-_create_opcua_objects_for_folders(SUCDataAssembly, str, Node, str): None +-_create_opcua_objects_for_leaves(SUCDataAssembly, str, Node, ET.Element): None +-_start_subscription(): None +-_infer_data_type(type): ua.VariantType +-_get_objects_attributes(object): dict + +SubscriptionList ++sub_list: dict +-- ++append(Node, Callable): None ++get_nodeid_list(): list ++get_callback(Node): Callable + +Marshalling ++subscription_list: SubscriptionList +-- ++import_subscription_list(SubscriptionList): None ++datachange_notification(Node, Any, Any): None ++find_set_callback(Node): Callable + +AnaMan ++v_min: float ++v_max: float ++v_scl_min: float ++v_scl_max: float ++v_unit: int +-- ++valid_value(float): bool ++set_v_out(float): None ++set_v_rbk(float): None ++set_v_fbk(float): None ++get_v_out(): float + +AnaManInt ++op_src_mode: SourceMode +-- ++set_v_int(float): None + +DIntMan ++v_min: int ++v_max: int ++v_scl_min: int ++v_scl_max: int ++v_unit: int +-- ++valid_value(int): bool ++set_v_out(int): None ++set_v_rbk(int): None ++set_v_fbk(int): None ++get_v_out(): int + +DIntManInt ++op_src_mode: SourceMode +-- ++set_v_int(int): None + +BinMan ++v_state_0: str ++v_state_1: str +-- ++set_v_out(bool): None ++set_v_rbk(bool): None ++set_v_fbk(bool): None ++get_v_out(): bool + +BinManInt ++op_src_mode: SourceMode +-- ++set_v_int(bool): None + +OperationSourceMode ++attributes: dict[str, Attribute] ++switch_to_offline_mode_allowed: bool ++enter_offline_callbacks: list[callable] ++exit_offline_callbacks: list[callable] ++enter_operator_callbacks: list[callable] ++exit_operator_callbacks: list[callable] ++enter_automatic_callbacks: list[callable] ++exit_automatic_callbacks: list[callable] ++linked_op_src_modes: list[OperationSourceMode] +-- ++allow_switch_to_offline_mode(bool): None ++add_enter_offline_callback(callable): None ++add_exit_offline_callback(callable): None ++add_enter_operator_callback(callable): None ++add_exit_operator_callback(callable): None ++add_enter_automatic_callback(callable): None ++add_exit_automatic_callback(callable): None ++add_linked_op_src_mode(OperationSourceMode): None ++set_state_channel(bool): None ++set_state_aut_aut(bool): None ++set_state_aut_op(bool): None ++set_state_off_aut(bool): None ++set_state_off_op(bool): None ++set_state_op_aut(bool): None ++set_state_op_op(bool): None ++set_src_channel(bool): None ++set_src_ext_aut(bool): None ++set_src_ext_op(bool): None ++set_src_int_aut(bool): None ++set_src_int_op(bool): None + +OperationMode ++attributes: dict[str, Attribute] +-- ++set_state_channel(bool): None ++set_state_off_aut(bool): None ++set_state_op_aut(bool): None ++set_state_aut_aut(bool): None ++set_state_off_op(bool): None ++set_state_op_op(bool): None ++set_state_aut_op(bool): None ++is_opmode_off(): bool ++is_opmode_aut(): bool ++is_opmode_op(): bool + +SourceMode ++attributes: dict[str, Attribute] +-- ++set_src_channel(bool): None ++set_src_man_aut(bool): None ++set_src_man_op(bool): None ++set_src_int_aut(bool): None ++set_src_int_op(bool): None ++is_srcmode_int(): bool ++is_srcmode_man(): bool + +OperationSourceModeElement +-- + +AnaServParam ++op_src_mode: OperationSourceMode ++v_min: float ++v_max: float ++v_scl_min: float ++v_scl_max: float ++v_unit: int +-- ++set_v_op(float): None ++set_v_int(float): None ++set_v_ext(float): None ++set_v_req(float): None ++set_v_out(): None ++get_v_out(): float ++set_v_fbk(float): None + +BinServParam ++op_src_mode: OperationSourceMode ++v_state_0: str ++v_state_1: str +-- ++set_v_op(bool): None ++set_v_int(bool): None ++set_v_ext(bool): None ++set_v_req(bool): None ++set_v_out(): None ++get_v_out(): bool ++set_v_fbk(bool): None + +DIntServParam ++op_src_mode: OperationSourceMode ++v_min: int ++v_max: int ++v_scl_min: int ++v_scl_max: int ++v_unit: int +-- ++set_v_op(int): None ++set_v_int(int): None ++set_v_ext(int): None ++set_v_req(int): None ++set_v_out(): None ++get_v_out(): int ++set_v_fbk(int): None + +StringServParam ++op_src_mode: OperationSourceMode +-- ++set_v_op(str): None ++set_v_int(str): None ++set_v_ext(str): None ++set_v_req(str): None ++set_v_out(): None ++get_v_out(): str ++set_v_fbk(str): None + +ProcedureControl ++attributes: dict[str, Attribute] ++procedures: dict ++op_src_mode: OperationSourceMode ++default_procedure_id: int +-- ++set_procedure_op(int): None ++set_procedure_int(int): None ++set_procedure_ext(int): None ++set_procedure_req(int): None ++set_procedure_cur(): None ++get_procedure_cur(): int ++get_procedure_req(): int ++apply_procedure_parameters(): None + +Procedure ++procedure_parameters: dict[str, SUCParameterElement] ++process_value_ins: dict ++report_values: dict[str, SUCIndicatorElement] ++process_value_outs: dict[str, SUCIndicatorElement] +-- ++add_procedure_parameter(SUCParameterElement): None ++add_procedure_value_in(Any): None ++add_report_value(SUCIndicatorElement): None ++add_procedure_value_out(SUCIndicatorElement): None ++apply_procedure_parameters(): None + +Service ++exception: Exception ++op_src_mode: OperationSourceMode ++configuration_parameters: dict ++procedures: dict ++procedure_control: ProcedureControl ++state_machine: StateMachine ++thread_ctrl: ThreadControl +-- ++enable_pause_loop(bool): None ++enable_hold_loop(bool): None ++enable_restart(bool): None ++add_configuration_parameter(SUCParameterElement): None ++add_procedure(Procedure): None ++get_current_procedure(): Procedure ++is_state(str): bool ++get_state_stop_event(): Event ++idle(): None ++starting(): None ++execute(): None ++completing(): None ++completed(): None ++pausing(): None ++paused(): None ++resuming(): None ++holding(): None ++held(): None ++unholding(): None ++stopping(): None ++stopped(): None ++aborting(): None ++aborted(): None ++resetting(): None +-_init_idle_state(): None +-_state_change_callback(): None +-_state_change(): None +-_is_self_completing(): bool +-_apply_configuration_parameters(): None + +StateCodes ++stopped: int ++starting: int ++idle: int ++paused: int ++execute: int ++stopping: int ++aborting: int ++aborted: int ++holding: int ++held: int ++unholding: int ++pausing: int ++resuming: int ++resetting: int ++completing: int ++completed: int ++int_code: dict[int, str] +-- ++get_list_int(): list[int] ++get_list_str(): list[str] + +StateMachine ++attributes: dict[str, Attribute] ++op_src_mode: OperationSourceMode ++procedure_control: ProcedureControl ++execution_routine: callable ++command_en_ctrl: CommandEnControl ++act_state: int ++prev_state: int +-- ++set_command_op(int): None ++set_command_int(int): None ++set_command_ext(int): None ++command_execution(int): None ++start(): None ++restart(): None ++complete(): None ++pause(): None ++resume(): None ++reset(): None ++hold(): None ++unhold(): None ++stop(): None ++abort(): None ++state_change(): None ++update_command_en(): None ++get_current_state_str(): str ++disable_commands_if_no_procedure(int): None + +SUCDataAssembly ++tag_name: str ++tag_description: str ++attributes: dict[str, Attribute] +-- ++_add_attribute(Attribute): None + +SUCIndicatorElement +-- ++set_v(Any): None + +SUCOperationElement +-- ++set_v_int(Any): None ++get_v_out(): Any + +SUCActiveElement +-- + +SUCDiagnosticElement +-- + +SUCServiceElement +-- + +SUCServiceControl +-- + +SUCProcedureHealthView +-- + +SUCParameterElement +-- ++set_v_int(Any): None ++get_v_out(): Any ++set_v_fbk(Any): None + +SUCServiceProcedure +-- + +ThreadControl ++service_name: str ++state_change_function: Callable ++thread: StoppableThread ++running_state: str ++requested_state: str ++callback_function: Callable ++exception_callback: Callable[[Exception], None] +-- ++request_state(str, Callable): None ++reallocate_running_thread(): None ++run_thread(Callable): None ++stop_thread(bool): None + +StoppableThread ++stop_event: Event +-- ++stop(): None From fb46e953025e0ad23c40aaa1efcc960a53efb1a8 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Tue, 21 Oct 2025 11:39:25 +0200 Subject: [PATCH 51/61] change logger name --- src/mtppy/active_elements.py | 6 +++--- src/mtppy/attribute.py | 2 +- src/mtppy/command_en_control.py | 2 +- src/mtppy/indicator_elements.py | 2 +- src/mtppy/operation_elements.py | 2 +- src/mtppy/operation_source_mode.py | 2 +- src/mtppy/parameter_elements.py | 2 +- src/mtppy/procedure.py | 2 +- src/mtppy/service.py | 2 +- src/mtppy/thread_control.py | 2 +- 10 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/mtppy/active_elements.py b/src/mtppy/active_elements.py index 3f843fa..3e0fb48 100644 --- a/src/mtppy/active_elements.py +++ b/src/mtppy/active_elements.py @@ -4,13 +4,13 @@ from mtppy.attribute import Attribute -from mtppy.operation_source_mode import OperationSourceModeElement, OperationMode +from mtppy.operation_source_mode import OperationSourceModeElement, OperationMode, OperationSourceMode from mtppy.suc_data_assembly import SUCActiveElement from time import sleep from simple_pid import PID -_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") +_logger = logging.getLogger(__name__) class Locks(): @@ -1941,7 +1941,7 @@ def __init__(self, tag_name, tag_description='', P=100, Ti=10, Td=1): super().__init__(tag_name, tag_description) - self.op_src_mode = OperationSourceModeElement() + self.op_src_mode = OperationSourceMode() self.pv_scl_min = pv_scl_min self.pv_scl_max = pv_scl_max diff --git a/src/mtppy/attribute.py b/src/mtppy/attribute.py index 80e2b92..8942734 100644 --- a/src/mtppy/attribute.py +++ b/src/mtppy/attribute.py @@ -4,7 +4,7 @@ from typing import Any -_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") +_logger = logging.getLogger(__name__) class Attribute: diff --git a/src/mtppy/command_en_control.py b/src/mtppy/command_en_control.py index 805e450..10ca285 100644 --- a/src/mtppy/command_en_control.py +++ b/src/mtppy/command_en_control.py @@ -1,6 +1,6 @@ import logging -_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") +_logger = logging.getLogger(__name__) class CommandEnControl: diff --git a/src/mtppy/indicator_elements.py b/src/mtppy/indicator_elements.py index 97642f7..cf86145 100644 --- a/src/mtppy/indicator_elements.py +++ b/src/mtppy/indicator_elements.py @@ -3,7 +3,7 @@ from mtppy.attribute import Attribute from mtppy.suc_data_assembly import SUCIndicatorElement -_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") +_logger = logging.getLogger(__name__) class AnaView(SUCIndicatorElement): diff --git a/src/mtppy/operation_elements.py b/src/mtppy/operation_elements.py index 8d0fc01..804cd5c 100644 --- a/src/mtppy/operation_elements.py +++ b/src/mtppy/operation_elements.py @@ -5,7 +5,7 @@ from mtppy.suc_data_assembly import SUCOperationElement -_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") +_logger = logging.getLogger(__name__) class AnaMan(SUCOperationElement): diff --git a/src/mtppy/operation_source_mode.py b/src/mtppy/operation_source_mode.py index ab4d939..fc35b93 100644 --- a/src/mtppy/operation_source_mode.py +++ b/src/mtppy/operation_source_mode.py @@ -2,7 +2,7 @@ from mtppy.attribute import Attribute -_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") +_logger = logging.getLogger(__name__) class OperationSourceMode: diff --git a/src/mtppy/parameter_elements.py b/src/mtppy/parameter_elements.py index 6cca751..1ccb017 100644 --- a/src/mtppy/parameter_elements.py +++ b/src/mtppy/parameter_elements.py @@ -4,7 +4,7 @@ from mtppy.operation_source_mode import OperationSourceMode from mtppy.suc_data_assembly import SUCParameterElement -_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") +_logger = logging.getLogger(__name__) class AnaServParam(SUCParameterElement): diff --git a/src/mtppy/procedure.py b/src/mtppy/procedure.py index 16d3c9d..4d92bab 100644 --- a/src/mtppy/procedure.py +++ b/src/mtppy/procedure.py @@ -2,7 +2,7 @@ from mtppy.suc_data_assembly import * -_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") +_logger = logging.getLogger(__name__) class Procedure(SUCServiceProcedure): diff --git a/src/mtppy/service.py b/src/mtppy/service.py index daef183..2beec2d 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -13,7 +13,7 @@ from mtppy.procedure import Procedure from mtppy.suc_data_assembly import SUCParameterElement -_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") +_logger = logging.getLogger(__name__) class Service(SUCServiceControl): diff --git a/src/mtppy/thread_control.py b/src/mtppy/thread_control.py index 7cd8ebb..8f75a78 100644 --- a/src/mtppy/thread_control.py +++ b/src/mtppy/thread_control.py @@ -2,7 +2,7 @@ from threading import Thread, Event, current_thread from collections.abc import Callable -_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") +_logger = logging.getLogger(__name__) class StoppableThread(Thread): From 7d6311bc1e9a5a74b665c198370fd48fb257bc4b Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Tue, 21 Oct 2025 11:39:58 +0200 Subject: [PATCH 52/61] fixes for generation of mtp file --- src/mtppy/opcua_server_pea.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/mtppy/opcua_server_pea.py b/src/mtppy/opcua_server_pea.py index c53da15..5117583 100644 --- a/src/mtppy/opcua_server_pea.py +++ b/src/mtppy/opcua_server_pea.py @@ -5,7 +5,7 @@ from mtppy.suc_data_assembly import SUCDataAssembly, SUCActiveElement, SUCIndicatorElement, SUCOperationElement from mtppy.mtp_generator import MTPGenerator -_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") +_logger = logging.getLogger(__name__) class OPCUAServerPEA: @@ -202,7 +202,7 @@ def _build_opcua_server(self): else: self._create_opcua_element(data_assembly_set, root_folder_name) - # add SupportedRoleClass to all InternalElements + # add SupportedRoleClass to all InternalElements if self.mtp: self.mtp.apply_add_supported_role_class() @@ -230,7 +230,7 @@ def _create_opcua_element(self, elements: dict[str, SUCDataAssembly], folder_nam def _create_opcua_objects_for_folders(self, data_assembly: SUCDataAssembly, parent_opcua_prefix: str, parent_opcua_object: Node, - name: str = None): + name: str = None, instance=None, link_id=None): """ Iterates over data assemblies to create OPC UA folders. @@ -239,6 +239,8 @@ def _create_opcua_objects_for_folders(self, data_assembly: SUCDataAssembly, parent_opcua_prefix (str): Prefix to add in front of the data assembly tag. parent_opcua_object: Parent OPC UA node where the data assembly is added. name (str): Name of the data assembly. If None, the tag name of the data assembly is used. + instance: Instance to what the data assembly belongs. + link_id: ID to link the data assembly to. """ if name is None: name = data_assembly.tag_name @@ -250,17 +252,14 @@ def _create_opcua_objects_for_folders(self, data_assembly: SUCDataAssembly, da_type = parent_opcua_prefix.split('=')[-1].split('.')[-1] # create instance of ServiceControl, HealthStateView, DIntServParam etc. - if self.mtp: - instance = self.mtp.create_instance(data_assembly, da_node_id) - else: - instance = None + if name not in self._leaves and not isinstance(data_assembly, dict): + if self.mtp: + instance = self.mtp.create_instance(data_assembly, da_node_id) - if self.mtp: - link_id = self.mtp.random_id_generator() - if da_type == 'services' or da_type in self._folders: - link_id = self.mtp.create_components_for_services(data_assembly, da_type) - else: - link_id = None + if self.mtp: + link_id = self.mtp.random_id_generator() + if da_type == 'services' or da_type in self._folders: + link_id = self.mtp.create_components_for_services(data_assembly, da_type) # Add attributes of data assembly if hasattr(data_assembly, 'attributes'): @@ -274,10 +273,11 @@ def _create_opcua_objects_for_folders(self, data_assembly: SUCDataAssembly, if name in self._leaves: continue # do not create folders for leaves folder_name = value.tag_name if hasattr(value, 'tag_name') else variable_name - self._create_opcua_objects_for_folders(value, da_node_id, da_node, folder_name) + self._create_opcua_objects_for_folders( + value, da_node_id, da_node, folder_name, instance, link_id) # create linked obj between instance and service component - if self.mtp: + if self.mtp and name not in self._leaves: self.mtp.add_linked_attr(instance, link_id) def _create_opcua_objects_for_leaves(self, opcua_object: SUCDataAssembly, parent_opcua_prefix: str, parent_opcua_object: Node, par_instance): From ae59a187078bc0a5bbc0ea254a7443ed9c73dffe Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Tue, 21 Oct 2025 11:40:54 +0200 Subject: [PATCH 53/61] better handling resetting of Command... commands --- src/mtppy/procedure_control.py | 27 ++++++++++++++++++++++----- src/mtppy/state_machine.py | 17 ++++++++--------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/mtppy/procedure_control.py b/src/mtppy/procedure_control.py index 7439489..4d947b8 100644 --- a/src/mtppy/procedure_control.py +++ b/src/mtppy/procedure_control.py @@ -3,7 +3,7 @@ from mtppy.attribute import Attribute from mtppy.operation_source_mode import OperationSourceMode -_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") +_logger = logging.getLogger(__name__) class ProcedureControl: @@ -27,6 +27,9 @@ def __init__(self, procedures: dict, service_op_src_mode: OperationSourceMode): self.op_src_mode = service_op_src_mode self.default_procedure_id = None + # will be set to True when resetting procedure commands to avoid also setting ProcedureReq to 0 + self.__resetting_procedure_commands = 0 + def set_procedure_op(self, value: int): _logger.debug('ProcedureOP set to %s' % value) if self.op_src_mode.attributes['StateOpAct'].value: @@ -42,6 +45,15 @@ def set_procedure_ext(self, value: int): if self.op_src_mode.attributes['StateAutAct'].value and self.op_src_mode.attributes['SrcExtAct'].value: self.set_procedure_req(value) + def reset_procedure_commands(self): + # ignore the next two invalid ProcedureReq settings + # one from direct setting and one from data change callback + # TODO: improve this logic + self.__resetting_procedure_commands = 2 + self.attributes['ProcedureOp'].set_value(0) + self.attributes['ProcedureInt'].set_value(0) + self.attributes['ProcedureExt'].set_value(0) + def valid_value(self, value: int): if value not in self.procedures.keys(): return False @@ -52,10 +64,15 @@ def set_procedure_req(self, value: int): if self.valid_value(value): self.attributes['ProcedureReq'].set_value(value) _logger.debug('ProcedureReq set to %s' % value) - else: - self.attributes['ProcedureReq'].set_value(0) - if value != 0: - _logger.warning('ProcedureReq cannot be set to %s (out of range)' % value) + return + if self.__resetting_procedure_commands > 0: + self.__resetting_procedure_commands -= 1 + return + + # invalid value -> reset ProcedureReq to 0 + self.attributes['ProcedureReq'].set_value(0) + if value != 0: + _logger.warning('ProcedureReq cannot be set to %s (out of range)' % value) def set_procedure_cur(self): procedure_req = self.attributes['ProcedureReq'].value diff --git a/src/mtppy/state_machine.py b/src/mtppy/state_machine.py index 8bec0ac..006d796 100644 --- a/src/mtppy/state_machine.py +++ b/src/mtppy/state_machine.py @@ -1,5 +1,7 @@ import logging +from collections.abc import Callable + from mtppy.attribute import Attribute from mtppy.state_codes import StateCodes from mtppy.command_codes import CommandCodes @@ -9,20 +11,20 @@ StateCodes = StateCodes() CommandCodes = CommandCodes() -_logger = logging.getLogger(f"mtp.{__name__.split('.')[-1]}") +_logger = logging.getLogger(__name__) class StateMachine: def __init__(self, operation_source_mode: OperationSourceMode, procedure_control: ProcedureControl, - execution_routine: callable): + execution_routine: Callable[[], None]): """ Represents a state machine for a service. Args: operation_source_mode (OperationSourceMode): Operation and source mode control. procedure_control (ProcedureControl): Procedure control. - execution_routine (callable): Execution routine for state changing. + execution_routine (Callable): Execution routine for state changing. """ self.attributes = { @@ -37,6 +39,7 @@ def __init__(self, operation_source_mode: OperationSourceMode, self.op_src_mode: OperationSourceMode = operation_source_mode self.procedure_control: ProcedureControl = procedure_control self.execution_routine = execution_routine + self.command_en_ctrl = CommandEnControl() self.act_state = StateCodes.idle @@ -86,18 +89,14 @@ def start(self): if self.command_en_ctrl.is_enabled('start'): self.procedure_control.set_procedure_cur() - self.procedure_control.attributes['ProcedureOp'].set_value(0) - self.procedure_control.attributes['ProcedureInt'].set_value(0) - self.procedure_control.attributes['ProcedureExt'].set_value(0) + self.procedure_control.reset_procedure_commands() self.procedure_control.apply_procedure_parameters() self._change_state_to(StateCodes.starting) def restart(self): if self.command_en_ctrl.is_enabled('restart'): self.procedure_control.set_procedure_cur() - self.procedure_control.attributes['ProcedureOp'].set_value(0) - self.procedure_control.attributes['ProcedureInt'].set_value(0) - self.procedure_control.attributes['ProcedureExt'].set_value(0) + self.procedure_control.reset_procedure_commands() self.procedure_control.apply_procedure_parameters() self._change_state_to(StateCodes.starting) From f60cd14ba611e98626ac29acad1c2a55a00acec8 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Thu, 23 Oct 2025 11:29:23 +0200 Subject: [PATCH 54/61] fix for restart bug that didn't stop the old execute thread if the starting state completed to fast --- src/mtppy/service.py | 15 ++++++++++----- src/mtppy/thread_control.py | 35 +++++++++++++++++++++++++---------- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/mtppy/service.py b/src/mtppy/service.py index 2beec2d..5a9bc4b 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -6,7 +6,7 @@ from abc import abstractmethod from mtppy.suc_data_assembly import SUCServiceControl -from mtppy.thread_control import ThreadControl +from mtppy.thread_control import ThreadControl, StoppableThread from mtppy.operation_source_mode import OperationSourceMode from mtppy.state_machine import StateMachine from mtppy.procedure_control import ProcedureControl @@ -177,6 +177,10 @@ def is_state(self, state_str): bool: True if the current state matches, False otherwise. """ if state_str is self.state_machine.get_current_state_str(): + # handel the case where there are multiple threads for the same state are running + # (e.g. execute state after restart) + if self.get_state_stop_event().is_set(): + return False return True else: # start the next thread if the state is not the current one @@ -190,10 +194,11 @@ def get_state_stop_event(self) -> Event: Returns: Event: Event that is set when the state should stop. """ - if self.thread_ctrl.thread is None: - raise RuntimeError("Thread is not running.") + current_thread = self.thread_ctrl.get_current_thread() + if not isinstance(current_thread, StoppableThread): + raise RuntimeError("Current thread is not a state thread. No stop event available.") - return self.thread_ctrl.thread.stop_event + return current_thread.stop_event def _init_idle_state(self): """ @@ -250,7 +255,7 @@ def idle(self): """ _logger.debug(f"{self.tag_name} - Idle -") cycle = 0 - while self.is_state("idle") and not self.thread_ctrl.thread.stop_event.is_set(): + while self.is_state("idle"): _logger.debug(f"{self.tag_name} - Idle cycle {cycle}") cycle += 1 time.sleep(3) diff --git a/src/mtppy/thread_control.py b/src/mtppy/thread_control.py index 8f75a78..ab61ddc 100644 --- a/src/mtppy/thread_control.py +++ b/src/mtppy/thread_control.py @@ -32,7 +32,7 @@ def __init__(self, service_name: str = '', """ self.service_name = service_name self.state_change_function = state_change_function - self.thread: StoppableThread = None + self.current_thread: StoppableThread = None self.running_state = '' self.requested_state = '' self.callback_function: Callable = None @@ -56,14 +56,15 @@ def reallocate_running_thread(self): """ if (self.requested_state is not self.running_state or (self.running_state == "idle" and - (self.thread is None or not self.thread.is_alive()))): + (self.current_thread is None or not self.current_thread.is_alive()))): _logger.debug(f'Reallocate thread to state {self.requested_state}') self.stop_thread(stop_if_current_thread=False) - self.thread = StoppableThread(target=self.run_thread, args=(self.callback_function,), - name=f"{self.service_name}_{self.requested_state}") + new_thread_name = f"{self.service_name}_{self.requested_state}" + self.current_thread = StoppableThread(target=self.run_thread, args=(self.callback_function,), + name=new_thread_name) self.running_state = self.requested_state - self.thread.start() + self.current_thread.start() def run_thread(self, target_function: Callable): """ @@ -75,8 +76,13 @@ def run_thread(self, target_function: Callable): try: try: target_function() + _logger.debug(f"State {current_thread().name} ended") # changes state for transitional states, only if the state is the current state. if self.running_state is target_function.__name__: + # check if the running state actually corresponds the the current state + if current_thread() is not self.current_thread: + _logger.debug(f"Ending of old thread, not changing state") + return if self.state_change_function: self.state_change_function() # Catch InterruptedError thrown by stop events. Should not cause an error. @@ -85,7 +91,7 @@ def run_thread(self, target_function: Callable): except Exception as e: self.exception_callback(e) if self.exception_callback else _logger.error( - f"Exception in thread {self.thread.name}: {e}", exc_info=True) + f"Exception in thread {self.current_thread.name}: {e}", exc_info=True) def stop_thread(self, stop_if_current_thread: bool = True): """ @@ -94,7 +100,16 @@ def stop_thread(self, stop_if_current_thread: bool = True): Args: stop_if_current_thread (bool): If True, stops the thread also if it is the current thread. """ - if self.thread and self.thread.is_alive(): - if stop_if_current_thread or self.thread is not current_thread(): - _logger.debug(f'Stopping thread {self.thread.name}') - self.thread.stop() + if self.current_thread and self.current_thread.is_alive(): + if stop_if_current_thread or self.current_thread is not current_thread(): + _logger.debug(f'Stopping thread {self.current_thread.name}') + self.current_thread.stop() + + def get_current_thread(self) -> StoppableThread: + """ + Gets the thread object of the thread where this method is called. + + Returns: + StoppableThread: The current thread object. + """ + return current_thread() From cea16bcdeaea7db0d42c8b94361cc2e94e5b508b Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Fri, 24 Oct 2025 15:00:33 +0200 Subject: [PATCH 55/61] better implementation for custom data assemblies --- src/mtppy/opcua_server_pea.py | 68 +++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/src/mtppy/opcua_server_pea.py b/src/mtppy/opcua_server_pea.py index 5117583..05de8cd 100644 --- a/src/mtppy/opcua_server_pea.py +++ b/src/mtppy/opcua_server_pea.py @@ -21,7 +21,7 @@ def __init__(self, mtp_generator: MTPGenerator = None, endpoint: str = 'opc.tcp: self.active_elements: dict[str, SUCActiveElement] = {} self.indicator_elements: dict[str, SUCIndicatorElement] = {} self.operation_elements: dict[str, SUCOperationElement] = {} - self.custom_data_assembly_sets: dict[str, dict[str, SUCDataAssembly]] = {} + self.custom_data_assembly_set: dict[str, SUCDataAssembly] = {} self.endpoint: str = endpoint self.opcua_server: Server = None self.opcua_ns: int = 3 @@ -76,29 +76,39 @@ def add_operation_element(self, operation_element: SUCOperationElement): """ self.operation_elements[operation_element.tag_name] = operation_element - def add_custom_data_assembly_set(self, root_folder_name: str, data_assembly_set: dict[str, SUCDataAssembly]): + def add_custom_data_assembly(self, data_assembly: SUCDataAssembly, folder_name: str = ''): """ Add a custom data assembly to the PEA. Args: - root_folder_name (str): Root folder name for the custom data assembly. - data_assembly (dict[str, SUCDataAssembly]): Custom data assembly instance. - Has to be a dictionary with folder names as keys and SUCDataAssembly instances as values. - If name is '', the data assembly is added to the root of the PEA. + data_assembly (SUCDataAssembly): Custom data assembly instance. + Has to be a class derived from SUCDataAssembly. + folder_name (str): Folder to add the data assembly to. + If name is '', the data assembly is added to the root of the Server. """ - if root_folder_name in self.custom_data_assembly_sets: - raise ValueError(f"Data assembly with tag name '{root_folder_name}' already exists.") - if not isinstance(data_assembly_set, dict) or data_assembly_set.__len__() == 0: - raise ValueError("Data assembly set must be a non-empty dictionary.") - if not isinstance(next(iter(data_assembly_set)), str): - raise TypeError("Data assembly set keys must be strings.") - if not isinstance(next(iter(data_assembly_set.values())), SUCDataAssembly): - raise TypeError("Data assembly set values must be instances of SUCDataAssembly.") + if not isinstance(data_assembly, SUCDataAssembly): + raise TypeError('data_assembly must be derived from type SUCDataAssembly') - root_folder_name = (next(iter(data_assembly_set)) if root_folder_name == '' - else root_folder_name) - self.custom_data_assembly_sets[root_folder_name] = data_assembly_set + if folder_name == '': + if data_assembly.tag_name in self.custom_data_assembly_set: + raise ValueError( + f'Custom data assembly set with name {data_assembly.tag_name} ' + f'in the root folder already exists.') + self.custom_data_assembly_set[data_assembly.tag_name] = data_assembly + return + + # create an empty data assembly as folder + if folder_name not in self.custom_data_assembly_set: + self.custom_data_assembly_set[folder_name] = SUCDataAssembly(folder_name) + + # add data assembly as attribute to the empty data assembly that is used as a folder + da_folder = self.custom_data_assembly_set[folder_name] + if hasattr(da_folder, data_assembly.tag_name): + raise ValueError( + f'Custom data assembly with name {data_assembly.tag_name} ' + f'in folder {folder_name} already exists.') + setattr(da_folder, data_assembly.tag_name, data_assembly) def add_folders(self, folders: list[str]): """ @@ -191,16 +201,8 @@ def _build_opcua_server(self): self._create_opcua_element(self.operation_elements, "operation_elements") # add custom data assemblies - for root_folder_name, data_assembly_set in self.custom_data_assembly_sets.items(): - _logger.info(f'- custom data assembly {root_folder_name}') - # if root_folder_name is the same as the first key add to root - if root_folder_name == next(iter(data_assembly_set)): - ns = self.opcua_ns - server = self.opcua_server.get_objects_node() - self._create_opcua_objects_for_folders( - data_assembly_set[root_folder_name], f"ns={ns};s={root_folder_name}", server, root_folder_name) - else: - self._create_opcua_element(data_assembly_set, root_folder_name) + if self.custom_data_assembly_set.__len__() > 0: + self._create_opcua_element(self.custom_data_assembly_set, "") # add SupportedRoleClass to all InternalElements if self.mtp: @@ -217,12 +219,16 @@ def _create_opcua_element(self, elements: dict[str, SUCDataAssembly], folder_nam Args: elements (dict[str, SUCDataAssembly]): Dictionary of elements. - folder_name (str): Name of the folder to create in the OPC UA server. + folder_name (str): Name of the folder to create in the OPC UA server. + If folder_name is '', the elements are added to the root of the Server. """ ns = self.opcua_ns - server = self.opcua_server.get_objects_node() + root_node = self.opcua_server.get_objects_node() element_node_id = f'ns={ns};s={folder_name}' - element_node = server.add_folder(element_node_id, folder_name) + if folder_name != '': + element_node = root_node.add_folder(element_node_id, folder_name) + else: + element_node = root_node _logger.info(f'- {folder_name}') for element in elements.values(): _logger.info(f'-- element {element.tag_name}') @@ -244,7 +250,7 @@ def _create_opcua_objects_for_folders(self, data_assembly: SUCDataAssembly, """ if name is None: name = data_assembly.tag_name - da_node_id = f'{parent_opcua_prefix}.{name}' + da_node_id = f'{parent_opcua_prefix}{"." if parent_opcua_prefix != "" else ""}{name}' da_node = parent_opcua_object.add_folder(da_node_id, name) _logger.debug(f'OPCUA Folder: {da_node_id}, Name: {name}') From c4fb1b2c6311592b136f54617df4b6866f84453a Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Sun, 26 Oct 2025 16:39:02 +0100 Subject: [PATCH 56/61] fix and refactor for adding node to root --- src/mtppy/opcua_server_pea.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/mtppy/opcua_server_pea.py b/src/mtppy/opcua_server_pea.py index 05de8cd..2de3994 100644 --- a/src/mtppy/opcua_server_pea.py +++ b/src/mtppy/opcua_server_pea.py @@ -250,7 +250,12 @@ def _create_opcua_objects_for_folders(self, data_assembly: SUCDataAssembly, """ if name is None: name = data_assembly.tag_name - da_node_id = f'{parent_opcua_prefix}{"." if parent_opcua_prefix != "" else ""}{name}' + da_node_id = parent_opcua_prefix + if parent_opcua_prefix != f'ns={self.opcua_ns};s=': + # node not added to root folder + da_node_id += '.' + da_node_id += name + da_node = parent_opcua_object.add_folder(da_node_id, name) _logger.debug(f'OPCUA Folder: {da_node_id}, Name: {name}') From 011c39b0450426a287635a78396c934ac1ae2b1e Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Sun, 26 Oct 2025 16:40:08 +0100 Subject: [PATCH 57/61] some cleanup --- src/mtppy/service.py | 1 - src/mtppy/state_machine.py | 10 ++++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/mtppy/service.py b/src/mtppy/service.py index 5a9bc4b..722fc2a 100644 --- a/src/mtppy/service.py +++ b/src/mtppy/service.py @@ -265,7 +265,6 @@ def starting(self): """ Starting state. """ - self._state_change() @abstractmethod def execute(self): diff --git a/src/mtppy/state_machine.py b/src/mtppy/state_machine.py index 006d796..250a4b0 100644 --- a/src/mtppy/state_machine.py +++ b/src/mtppy/state_machine.py @@ -160,6 +160,7 @@ def state_change(self): self._change_state_to(StateCodes.aborted) def _change_state_to(self, new_state: int): + self.prev_state = self.act_state self.act_state = new_state self.attributes['StateCur'].set_value(new_state) new_state_str = StateCodes.int_code[new_state] @@ -183,6 +184,15 @@ def get_current_state_str(self) -> str: """ return StateCodes.int_code[self.act_state] + def get_prev_state_str(self) -> str: + """ + Get the previous state as a string. + + Returns: + str: Previous state as a string. + """ + return StateCodes.int_code[self.prev_state] + def disable_commands_if_no_procedure(self, value: int): """ Disables all commands if value is 0 and the current state is idle. From 566a72908010783c88311230b445622a58cf8711 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Sun, 26 Oct 2025 16:41:19 +0100 Subject: [PATCH 58/61] added and modified examples to show new and improved functionality --- examples/data_objects.py | 270 +++ examples/virtual_pea_minimal.py | 143 +- examples/virtual_pea_with_events.py | 205 ++ .../virtual_pea_with_pause_hold_restart.py | 175 ++ examples/virtual_pea_with_recipe.py | 188 +- manifest_files/example_minimal_manifest.aml | 970 ++++----- manifest_files/example_recipe_manifest.aml | 1900 +++++++++-------- 7 files changed, 2270 insertions(+), 1581 deletions(-) create mode 100644 examples/data_objects.py create mode 100644 examples/virtual_pea_with_events.py create mode 100644 examples/virtual_pea_with_pause_hold_restart.py diff --git a/examples/data_objects.py b/examples/data_objects.py new file mode 100644 index 0000000..27dafd8 --- /dev/null +++ b/examples/data_objects.py @@ -0,0 +1,270 @@ +from opcua import Client + +from mtppy.opcua_server_pea import OPCUAServerPEA + +from mtppy.indicator_elements import * +from mtppy.operation_elements import * +from mtppy.active_elements import * +from mtppy.suc_data_assembly import SUCDataAssembly +from mtppy.attribute import Attribute + +import time + +logging.basicConfig( + format='%(asctime)s.%(msecs)03d; [%(levelname)s]; ' + '%(module)s.%(funcName)s: %(message)s', + datefmt='%H:%M:%S', level=logging.INFO) +logging.getLogger("opcua").setLevel(logging.ERROR) +logging.getLogger("mtppy.indicator_elements").setLevel(logging.DEBUG) +logging.getLogger("mtppy.operation_elements").setLevel(logging.DEBUG) +logging.getLogger("mtppy.active_elements").setLevel(logging.DEBUG) + +_log = logging.getLogger(__name__) + + +module = OPCUAServerPEA(endpoint="opc.tcp://127.0.0.1:4840/") + +### Indicator elements ### +bin_view = BinView('indicator_bin', 'demo object', v_state_0='Off', v_state_1='On') +dint_view = DIntView('indicator_dint', 'demo object', v_scl_min=0, v_scl_max=100, v_unit=1342) +ana_view = AnaView('indicator_ana', 'demo object', v_scl_min=0, v_scl_max=100, v_unit=1342) +str_view = StringView('indicator_str', 'demo object') + +# add indicator elements to the module +module.add_indicator_element(bin_view) +module.add_indicator_element(dint_view) +module.add_indicator_element(ana_view) +module.add_indicator_element(str_view) + + +### Operation elements ### +bin_man = BinMan('op_bin', 'demo object', v_state_0='Off', v_state_1='On', init_value=0) +dint_man = DIntMan('op_dint', 'demo object', v_min=0, v_max=100, + v_scl_min=0, v_scl_max=100, v_unit=1342, init_value=0) +ana_man = AnaMan('op_ana', 'demo object', v_min=0, v_max=100, + v_scl_min=0, v_scl_max=100, v_unit=1342, init_value=0) + +# add operation elements to the module +module.add_operation_element(bin_man) +module.add_operation_element(dint_man) +module.add_operation_element(ana_man) + + +## Internal operation elements ## +bin_man_int = BinManInt('op_bin_int', 'demo object', v_state_0='Off', v_state_1='On', init_value=0) +dint_man_int = DIntManInt('op_dint_int', 'demo object', v_min=0, v_max=100, + v_scl_min=0, v_scl_max=100, v_unit=1342, init_value=0) +ana_man_int = AnaManInt('op_ana_int', 'demo object', v_min=0, v_max=100, + v_scl_min=0, v_scl_max=100, v_unit=1342, init_value=0) + +# add internal operation elements to the module +module.add_operation_element(bin_man_int) +module.add_operation_element(dint_man_int) +module.add_operation_element(ana_man_int) + + +### Active elements ### +bin_vlv = BinVlv('active_bin', 'demo object', safe_pos=0, safe_pos_en=True, + perm_en=True, intl_en=True, prot_en=True) + +# add active elements to the module +module.add_active_element(bin_vlv) + + +### Custom Data Assembly ### +# it is also possible to create custom data assemblies by inheriting from SUCDataAssembly +# Attributes can be using the self._add_attribute() function inside the __init__ function +class CustomDataAssembly(SUCDataAssembly): + def __init__(self, tag_name: str, tag_description: str = '', pol_request: int = 0): + super().__init__(tag_name, tag_description) + + self._add_attribute(Attribute('POLRequest', int, + init_value=pol_request, sub_cb=self._set_pol_request)) + self._add_attribute(Attribute('PEAResponse', str, init_value='Hello World')) + + self.pol_request = pol_request + + def set_pea_response(self, value: str): + self.attributes['PEAResponse'].set_value(value) + _log.info(f'PEAResponse set to {value}') + + def get_pol_request(self) -> int: + return self.pol_request + + def _set_pol_request(self, value: int): + # ignore reset callback + if value == 0: + return + self.pol_request = value + _log.info(f'POLRequest set to {value} ') + + if value == 42: + self.set_pea_response('The answer to life, the universe and everything.') + else: + self.set_pea_response(f'POLRequest was {value}.') + + self.attributes['POLRequest'].set_value(0) # reset POLRequest after reading + + +custom_da = CustomDataAssembly('custom_da_1', 'demo object', pol_request=123) +module.add_custom_data_assembly(custom_da, "custom_data_assemblies") + +# start the OPC UA server +module.run_opcua_server() + +# while True: +# time.sleep(1) + +############################################################################################### +# Test +opcua_client = Client(module.endpoint) +opcua_client.connect() +time.sleep(1) + + +print('--- Set values of indicator elements ---') +bin_view.set_v(1) +dint_view.set_v(55) +ana_view.set_v(33.3) +str_view.set_v('Hello MTPPy!') +time.sleep(1) + +print('--- Print values of indicator elements (Simulating HMI side) ---') +print(f"bin_view: {opcua_client.get_node('ns=3;s=indicator_elements.indicator_bin.V').get_value()}") +print(f"dint_view: {opcua_client.get_node('ns=3;s=indicator_elements.indicator_dint.V').get_value()}") +print(f"ana_view: {opcua_client.get_node('ns=3;s=indicator_elements.indicator_ana.V').get_value()}") +print(f"str_view: {opcua_client.get_node('ns=3;s=indicator_elements.indicator_str.V').get_value()}") +time.sleep(2) + + +print() +print('--- Set values of operation elements (Simulating HMI side) ---') +opcua_client.get_node('ns=3;s=operation_elements.op_bin.VMan').set_value(1) +opcua_client.get_node('ns=3;s=operation_elements.op_dint.VMan').set_value(77) +opcua_client.get_node('ns=3;s=operation_elements.op_ana.VMan').set_value(44.4) +time.sleep(1) + +print('--- Print values of operation elements ---') +print(f"bin_man: {bin_man.get_v_out()}") +print(f"dint_man: {dint_man.get_v_out()}") +print(f"ana_man: {ana_man.get_v_out()}") +time.sleep(2) + + +print() +print('--- Set values of internal operation elements ---') +bin_man_int.set_v_int(1) +dint_man_int.set_v_int(88) +ana_man_int.set_v_int(55.5) +time.sleep(1) + +print('--- Print values of internal operation elements ---') +print( + f"bin_man_int: {opcua_client.get_node('ns=3;s=operation_elements.op_bin_int.VOut').get_value()}") +print( + f"dint_man_int: {opcua_client.get_node('ns=3;s=operation_elements.op_dint_int.VOut').get_value()}") +print( + f"ana_man_int: {opcua_client.get_node('ns=3;s=operation_elements.op_ana_int.VOut').get_value()}") +time.sleep(2) + +print('--- Set SrcChannel to 0 (operator switches)') +opcua_client.get_node( + 'ns=3;s=operation_elements.op_bin_int.op_src_mode.SrcChannel').set_value(False) +print('--- Set to Manual via operator ---') +opcua_client.get_node('ns=3;s=operation_elements.op_bin_int.op_src_mode.SrcManOp').set_value(True) +print('--- Set value via operator ---') +opcua_client.get_node('ns=3;s=operation_elements.op_bin_int.VMan').set_value(0) +time.sleep(1) +print(f"bin_man_int: {bin_man_int.get_v_out()}") +time.sleep(1) + +print('--- Set SrcChannel to 1 (internal control) ---') +opcua_client.get_node( + 'ns=3;s=operation_elements.op_bin_int.op_src_mode.SrcChannel').set_value(True) +print('--- Set to Automatic via internal control ---') +opcua_client.get_node('ns=3;s=operation_elements.op_bin_int.op_src_mode.SrcIntAut').set_value(True) +print('--- Set value via internal control ---') +bin_man_int.set_v_int(1) +time.sleep(1) +print( + f"bin_man_int: {opcua_client.get_node('ns=3;s=operation_elements.op_bin_int.VOut').get_value()}") +time.sleep(2) + + +print() +print('--- Set values of active element ---') +bin_vlv.set_open_aut(True) +time.sleep(1) +print( + f"bin_vlv position: {opcua_client.get_node('ns=3;s=active_elements.active_bin.Ctrl').get_value()}") +time.sleep(1) +bin_vlv.set_close_aut(True) +time.sleep(1) +print( + f"bin_vlv position: {opcua_client.get_node('ns=3;s=active_elements.active_bin.Ctrl').get_value()}") +time.sleep(2) + +print('--- Demonstrate bin_vlv locks ---') +locks = bin_vlv.locks + +print(f"Initial permit status: {locks.permit_status()}") +print(f"Initial interlock status: {locks.interlock_status()}") +print(f"Initial protect status: {locks.protect_status()}") + +# Enable and disable permit +locks.set_permit(False) +print(f"Permit status after disabling: {locks.permit_status()}") +bin_vlv.set_open_aut(True) +time.sleep(1) +print( + f"bin_vlv position (should not change): {opcua_client.get_node('ns=3;s=active_elements.active_bin.Ctrl').get_value()}") +time.sleep(1) +locks.set_permit(True) +print(f"Permit status after enabling: {locks.permit_status()}") +time.sleep(1) +bin_vlv.set_open_aut(True) +time.sleep(1) +print( + f"bin_vlv position (should change now): {opcua_client.get_node('ns=3;s=active_elements.active_bin.Ctrl').get_value()}") +time.sleep(2) + +# Enable and disable interlock +locks.set_interlock(True) +print(f"Interlock status after enabling: {locks.interlock_status()}") +sleep(1) +print(f"Interlock triggered, valve should close automatically: valve status: {bin_vlv.get_ctrl()}") +locks.set_interlock(False) +print(f"Interlock status after disabling: {locks.interlock_status()}") + +# Enable and disable protect +locks.set_protect(True) +print(f"Protect status after enabling: {locks.protect_status()}") +bin_vlv.reset_vlv() +print(f"Protect status after disabling: {locks.protect_status()}") +time.sleep(2) + + +print() +print('--- Set and read Custom Data Assembly attributes ---') +print("--- Set POLRequest to 42 (Simulating HMI side) ---") +opcua_client.get_node('ns=3;s=custom_data_assemblies.custom_da_1.POLRequest').set_value(42) +time.sleep(1) +print("--- Read POLRequest and PEAResponse attributes ---") +print(f"POLRequest: {custom_da.get_pol_request()}") +print( + f"PEAResponse: {opcua_client.get_node('ns=3;s=custom_data_assemblies.custom_da_1.PEAResponse').get_value()}") +time.sleep(1) +print("--- Try setting the PEAResponse attribute from HMI side (should have no effect) ---") +try: + opcua_client.get_node('ns=3;s=custom_data_assemblies.custom_da_1.PEAResponse').set_value( + 'PEAResponse set from from HMI') +except Exception as e: + print(f"Caught exception as expected: {e}") +time.sleep(1) +print( + f"PEAResponse on server (should not be changed): {opcua_client.get_node('ns=3;s=custom_data_assemblies.custom_da_1.PEAResponse').get_value()}") +time.sleep(2) + +print('--- Demo complete ---') +opcua_client.disconnect() +module.get_opcua_server().stop() diff --git a/examples/virtual_pea_minimal.py b/examples/virtual_pea_minimal.py index 3ff8b41..e899cca 100644 --- a/examples/virtual_pea_minimal.py +++ b/examples/virtual_pea_minimal.py @@ -1,3 +1,10 @@ +import time +import random +from datetime import datetime +import logging + +from opcua import Client + from mtppy.opcua_server_pea import OPCUAServerPEA from mtppy.mtp_generator import MTPGenerator from mtppy.service import Service @@ -6,12 +13,20 @@ from mtppy.indicator_elements import * from mtppy.active_elements import * -import time -import random -from datetime import datetime +from mtppy.command_codes import CommandCodes +from mtppy.state_codes import StateCodes + +CommandCodes = CommandCodes() +StateCodes = StateCodes() -logging.basicConfig(format='%(asctime)s.%(msecs)03d [%(levelname)s] %(module)s.%(funcName)s: %(message)s', - datefmt='%Y-%m-%d %H:%M:%S', level=logging.INFO) +logging.basicConfig( + format='%(asctime)s.%(msecs)03d; [%(levelname)s];' + '%(module)s.%(funcName)s: %(message)s', + datefmt='%H:%M:%S', level=logging.INFO) +logging.getLogger("opcua").setLevel(logging.ERROR) +logging.getLogger("mtppy.service").setLevel(logging.DEBUG) + +_log = logging.getLogger(__name__) class RandomNumberGenerator(Service): @@ -19,94 +34,55 @@ def __init__(self, tag_name, tag_description): super().__init__(tag_name, tag_description) # Procedure definition - proc_0 = Procedure(0, 'cont', is_self_completing=False, is_default=True) + proc_1 = Procedure(1, 'cont', is_self_completing=False, is_default=True) # Adding two procedure parameters - proc_0.add_procedure_parameter( + proc_1.add_procedure_parameter( DIntServParam('lower_bound', v_min=0, v_max=100, v_scl_min=0, v_scl_max=100, v_unit=23)) - proc_0.add_procedure_parameter( + proc_1.add_procedure_parameter( DIntServParam('upper_bound', v_min=0, v_max=100, v_scl_min=0, v_scl_max=100, v_unit=23)) + # optional: link op_src_mode of procedure parameters to service op_src_mode + for parameter in proc_1.procedure_parameters.values(): + self.op_src_mode.add_linked_op_src_mode(parameter.op_src_mode) + # Adding procedure report value - proc_0.add_report_value( + proc_1.add_report_value( AnaView('generated_value', v_scl_min=0, v_scl_max=100, v_unit=23), ) # Allocating procedure to the service - self.add_procedure(proc_0) - - def idle(self): - print('- Idle -') - cycle = 0 - while self.is_state('idle'): - print(f'Idle cycle {cycle}') - print('Doing nothing...') - cycle += 1 - time.sleep(1) + self.add_procedure(proc_1) def starting(self): - print('- Starting -') - print('Applying procedure parameters...') - self.state_change() + _log.info('- Starting -') + _log.info('Applying procedure parameters...') def execute(self): - print('- Execute -') + _log.info('- Execute -') cycle = 0 while self.is_state('execute'): - print('Execute cycle %i' % cycle) + _log.info('Execute cycle %i' % cycle) # Read procedure parameters - lower_bound = self.procedures[0].procedure_parameters['lower_bound'].get_v_out() - upper_bound = self.procedures[0].procedure_parameters['upper_bound'].get_v_out() + lower_bound = self.procedures[1].procedure_parameters['lower_bound'].get_v_out() + upper_bound = self.procedures[1].procedure_parameters['upper_bound'].get_v_out() # Execute random number generation generated_number = random.randint(lower_bound, upper_bound) # Return report value - self.procedures[0].report_values['generated_value'].set_v(generated_number) + self.procedures[1].report_values['generated_value'].set_v(generated_number) cycle += 1 time.sleep(0.1) def completing(self): - self.state_change() + _log.info('- Completing -') - def completed(self): - pass - - def pausing(self): - pass - - def paused(self): - pass - - def resuming(self): - pass - - def holding(self): - pass - - def held(self): - pass - - def unholding(self): - pass - - def stopping(self): - pass - - def stopped(self): - pass - - def aborting(self): - pass - - def aborted(self): - pass - - def resetting(self): - print('- Resetting -') - self.state_change() + # starting, execute, completing methods have to be defined for each service + # other state methods (see service documentation) can be overridden as needed, + # by default they only log state entries if __name__ == '__main__': @@ -119,7 +95,7 @@ def resetting(self): manifest_template_path = '../manifest_files/manifest_template.xml' mtp_generator = MTPGenerator(writer_info_dict, export_manifest_path, manifest_template_path) - module = OPCUAServerPEA(mtp_generator) + module = OPCUAServerPEA(mtp_generator, endpoint="opc.tcp://127.0.0.1:4840/") # Service definition service_1 = RandomNumberGenerator('rand_num_gen', 'This services generates random number') @@ -133,36 +109,49 @@ def resetting(self): print('--- Start OPC UA server ---') module.run_opcua_server() + ############################################################################################### # Test - opcua_server = module.get_opcua_server() - opcua_ns = module.get_opcua_ns() + opcua_client = Client(module.endpoint) + opcua_client.connect() time.sleep(1) print('--- Set procedure parameters to Operator mode ---') - opcua_server.get_node('ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateOpOp').set_value(True) - opcua_server.get_node('ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateOpOp').set_value(True) + opcua_client.get_node( + 'ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateOpOp').set_value(True) + opcua_client.get_node( + 'ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateOpOp').set_value(True) time.sleep(1) print('--- Set procedure parameter values ---') - opcua_server.get_node('ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VOp').set_value(40) - opcua_server.get_node('ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VOp').set_value(60) + opcua_client.get_node( + 'ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VOp').set_value(40) + opcua_client.get_node( + 'ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VOp').set_value(60) time.sleep(1) print('--- Set service to Operator mode ---') - opcua_server.get_node('ns=3;s=services.rand_num_gen.op_src_mode.StateOpOp').set_value(True) + opcua_client.get_node('ns=3;s=services.rand_num_gen.op_src_mode.StateOpOp').set_value(True) time.sleep(1) print('--- Start service ---') - opcua_server.get_node('ns=3;s=services.rand_num_gen.state_machine.CommandOp').set_value(4) + opcua_client.get_node('ns=3;s=services.rand_num_gen.state_machine.CommandOp').set_value( + CommandCodes.start) time.sleep(10) print('--- Complete service ---') - opcua_server.get_node('ns=3;s=services.rand_num_gen.state_machine.CommandOp').set_value(1024) + opcua_client.get_node('ns=3;s=services.rand_num_gen.state_machine.CommandOp').set_value( + CommandCodes.complete) time.sleep(1) print('--- Reset service ---') - opcua_server.get_node('ns=3;s=services.rand_num_gen.state_machine.CommandOp').set_value(2) + opcua_client.get_node('ns=3;s=services.rand_num_gen.state_machine.CommandOp').set_value( + CommandCodes.reset) + time.sleep(1) - print('--- Set service dummy to Offline mode ---') - opcua_server.get_node('ns=3;s=services.rand_num_gen.op_src_mode.StateOffOp').set_value(True) + print('--- Set service to Offline mode ---') + opcua_client.get_node('ns=3;s=services.rand_num_gen.op_src_mode.StateOffOp').set_value(True) time.sleep(1) + + print('--- Demo complete ---') + opcua_client.disconnect() + module.get_opcua_server().stop() diff --git a/examples/virtual_pea_with_events.py b/examples/virtual_pea_with_events.py new file mode 100644 index 0000000..f58e50d --- /dev/null +++ b/examples/virtual_pea_with_events.py @@ -0,0 +1,205 @@ +import time +import threading +import logging + +from opcua import Client + +from mtppy.opcua_server_pea import OPCUAServerPEA +from mtppy.service import Service +from mtppy.procedure import Procedure +from mtppy.parameter_elements import * +from mtppy.indicator_elements import * +from mtppy.active_elements import * + +from mtppy.command_codes import CommandCodes +from mtppy.state_codes import StateCodes + +CommandCodes = CommandCodes() +StateCodes = StateCodes() + +logging.basicConfig( + format='%(asctime)s.%(msecs)03d; [%(levelname)s]; ' + '%(module)s.%(funcName)s: %(message)s', + datefmt='%H:%M:%S', level=logging.INFO) +logging.getLogger("opcua").setLevel(logging.ERROR) +logging.getLogger("mtppy.service").setLevel(logging.DEBUG) + +_log = logging.getLogger(__name__) + +external_event = threading.Event() + +################################################################################################### +# Function to wait for two events at the same time +# !!! This modifies the standard threading.Event, could break if it would be changed in the library !!! + + +def _or_set(self): + # calls the original set method and then calls the changed callback + self._set() + self.changed() + + +def _or_clear(self): + # calls the original clear method and then calls the changed callback + self._clear() + self.changed() + + +def _orify(e: threading.Event, changed_callback): + # save original methods to also be called when set/clear is called + e._set = e.set + e._clear = e.clear + # add changed callback, which is called when the event is set or cleared + e.changed = changed_callback + # redefine set and clear methods to call modified methods that also call changed + e.set = lambda: _or_set(e) + e.clear = lambda: _or_clear(e) + + +def OrEvent(*events: threading.Event): + or_event = threading.Event() + + # check if any of the events are the same + if len(events) != len(set(events)): + raise ValueError("Events must be unique.") + + # check if any of the events are already orified + for e in events: + if hasattr(e, 'changed'): + raise ValueError("Events must not be orified already.") + + def changed(): + bools = [e.is_set() for e in events] + if any(bools): + or_event.set() + else: + or_event.clear() + + # Make every event able to call the changed function + # when it is set or cleared. + for e in events: + _orify(e, changed) + changed() + return or_event + + +def wait_for_either(e1, e2, timeout: float = None) -> bool: + """ + Waits for either of the two events to be set. + Args: + e1 (threading.Event): First event to wait for. + e2 (threading.Event): Second event to wait for. + timeout (float): Maximum time to wait in seconds. If None, waits indefinitely. + + Returns: + bool: True if either event is set, False if timeout occurs. + """ + return OrEvent(e1, e2).wait(timeout=timeout) +################################################################################################### + + +class WaitForExternalEvent(Service): + def __init__(self, tag_name, tag_description): + super().__init__(tag_name, tag_description) + + # Procedure definition + proc_1 = Procedure(1, 'default', is_self_completing=True, is_default=True) + + # Adding procedure report value + proc_1.add_report_value( + BinView('event_status', v_state_0='no', v_state_1='yes'), + ) + + # Allocating procedure to the service + self.add_procedure(proc_1) + + def starting(self): + _log.info('- Starting -') + _log.info('Creating external event...') + + global external_event + external_event = threading.Event() + + def execute(self): + _log.info('- Execute -') + + global external_event + + # This will wait until either the external event is set + # or the service is requested to stop. + wait_for_either(external_event, self.get_state_stop_event()) + + if external_event.is_set(): + _log.info('External event detected!') + self.get_current_procedure().report_values['event_status'].set_v(1) + else: + _log.info('Service stop requested before external event.') + self.get_current_procedure().report_values['event_status'].set_v(0) + + def completing(self): + _log.info('- Completing -') + + # starting, execute, completing methods have to be defined for each service + # other state methods (see service documentation) can be overridden as needed, + # by default they only log state entries + + +if __name__ == '__main__': + module = OPCUAServerPEA(endpoint="opc.tcp://127.0.0.1:4840/") + + # Service definition + service = WaitForExternalEvent('WaitForEvent', + 'Service that waits for an external event') + module.add_service(service) + + # Start server + print('--- Start OPC UA server ---') + module.run_opcua_server() + + ############################################################################################### + # Test + opcua_client = Client(module.endpoint) + opcua_client.connect() + time.sleep(1) + + print('--- Set service to Operator mode ---') + opcua_client.get_node('ns=3;s=services.WaitForEvent.op_src_mode.StateOpOp').set_value(True) + time.sleep(1) + + print('--- Start service ---') + opcua_client.get_node('ns=3;s=services.WaitForEvent.state_machine.CommandOp').set_value( + CommandCodes.start) + time.sleep(4) + + print('--- Trigger external event ---') + external_event.set() + time.sleep(2) + + print('--- Reset service ---') + opcua_client.get_node('ns=3;s=services.WaitForEvent.state_machine.CommandOp').set_value( + CommandCodes.reset) + external_event.clear() + time.sleep(1) + + print('--- Start service ---') + opcua_client.get_node('ns=3;s=services.WaitForEvent.state_machine.CommandOp').set_value( + CommandCodes.start) + time.sleep(4) + + print('--- Stop service before external event ---') + opcua_client.get_node('ns=3;s=services.WaitForEvent.state_machine.CommandOp').set_value( + CommandCodes.stop) + time.sleep(2) + + print('--- Reset service ---') + opcua_client.get_node('ns=3;s=services.WaitForEvent.state_machine.CommandOp').set_value( + CommandCodes.reset) + time.sleep(1) + + print('--- Set service to Offline mode ---') + opcua_client.get_node('ns=3;s=services.WaitForEvent.op_src_mode.StateOffOp').set_value(True) + time.sleep(1) + + print('--- Demo complete ---') + opcua_client.disconnect() + module.get_opcua_server().stop() diff --git a/examples/virtual_pea_with_pause_hold_restart.py b/examples/virtual_pea_with_pause_hold_restart.py new file mode 100644 index 0000000..a6fa55f --- /dev/null +++ b/examples/virtual_pea_with_pause_hold_restart.py @@ -0,0 +1,175 @@ +import time +import logging + +from opcua import Client + +from mtppy.opcua_server_pea import OPCUAServerPEA +from mtppy.service import Service +from mtppy.procedure import Procedure +from mtppy.parameter_elements import * +from mtppy.indicator_elements import * +from mtppy.active_elements import * + +from mtppy.command_codes import CommandCodes +from mtppy.state_codes import StateCodes + +CommandCodes = CommandCodes() +StateCodes = StateCodes() + +logging.basicConfig( + format='%(asctime)s.%(msecs)03d; [%(levelname)s]; ' + '%(module)s.%(funcName)s: %(message)s', + datefmt='%H:%M:%S', level=logging.INFO) +logging.getLogger("opcua").setLevel(logging.ERROR) +logging.getLogger("mtppy.service").setLevel(logging.DEBUG) + +_log = logging.getLogger(__name__) + + +class Counter(Service): + def __init__(self, tag_name, tag_description): + super().__init__(tag_name, tag_description) + + # Procedure definition + proc_1 = Procedure(1, 'cont', is_self_completing=True, is_default=True) + + # Adding procedure report value + proc_1.add_report_value( + DIntView('count', v_scl_min=0, v_scl_max=100, v_unit=23), + ) + + # Allocating procedure to the service + self.add_procedure(proc_1) + + # Enable pause and hold functionality + self.enable_pause_loop() + self.enable_hold_loop() + + # Enable reset functionality + # self.enable_restart() + + # add count variable + self.count = 0 + + def starting(self): + _log.info('- Starting -') + self.count = 0 + self.get_current_procedure().report_values['count'].set_v(self.count) + # time.sleep(1) + + def execute(self): + _log.info('- Execute -') + start_time = int(time.time() % 100) + # state_stop_event = self.get_state_stop_event() + # _log.info(f"State stop event: {state_stop_event.is_set()} for start at {start_time}") + + while self.is_state("execute") and self.count < 100: + + # Execute counting + self.count += 1 + self.get_current_procedure().report_values['count'].set_v(self.count) + _log.info(f'Count value: {self.count:3d} start at {start_time}') + + time.sleep(0.1) + + def completing(self): + _log.info('- Completing -') + + _log.info(f'Final count value: {self.count}') + + # starting, execute, completing methods have to be defined for each service + + # if enabled at least one of the pausing/ holding functions should be implemented + def pausing(self): + _log.info(f'Count value at pausing: {self.count}') + + def paused(self): + _log.info(f"Count value at paused: {self.count}") + + def resuming(self): + return super().resuming() + + def holding(self): + _log.info(f"Count value at holding: {self.count}") + + def hold(self): + _log.info(f'Count value at hold: {self.count}') + + def unholding(self): + return super().unholding() + + def restarting(self): + _log.info('- Restarting -') + + _log.info("Resetting count value") + # other state methods (see service documentation) can be overridden as needed, + # by default they only log state entries + + +if __name__ == '__main__': + + module = OPCUAServerPEA(endpoint="opc.tcp://127.0.0.1:4840/") + + # Service definition + service_1 = Counter('counter', 'Counts from 0 to 100') + module.add_service(service_1) + + # Start server + print('--- Start OPC UA server ---') + module.run_opcua_server() + + ############################################################################################### + # Test + opcua_client = Client(module.endpoint) + opcua_client.connect() + time.sleep(1) + + print('--- Set service to Operator mode ---') + opcua_client.get_node('ns=3;s=services.counter.op_src_mode.StateOpOp').set_value(True) + time.sleep(1) + + print('--- Start service ---') + opcua_client.get_node('ns=3;s=services.counter.state_machine.CommandOp').set_value( + CommandCodes.start) + time.sleep(4) + + print('--- Restart service ---') + opcua_client.get_node('ns=3;s=services.counter.state_machine.CommandOp').set_value( + CommandCodes.restart) + time.sleep(4) + + print('--- Pause service ---') + opcua_client.get_node('ns=3;s=services.counter.state_machine.CommandOp').set_value( + CommandCodes.pause) + time.sleep(2) + + print('--- Resume service ---') + opcua_client.get_node('ns=3;s=services.counter.state_machine.CommandOp').set_value( + CommandCodes.resume) + time.sleep(4) + + print('--- Hold service ---') + opcua_client.get_node('ns=3;s=services.counter.state_machine.CommandOp').set_value( + CommandCodes.hold) + time.sleep(2) + + print('--- Unhold service ---') + opcua_client.get_node('ns=3;s=services.counter.state_machine.CommandOp').set_value( + CommandCodes.unhold) + + print('--- Waiting until service completes ---') + while opcua_client.get_node('ns=3;s=services.counter.state_machine.StateCur').get_value() != StateCodes.completed: + time.sleep(1) + + print('--- Reset service ---') + opcua_client.get_node('ns=3;s=services.counter.state_machine.CommandOp').set_value( + CommandCodes.reset) + time.sleep(1) + + print('--- Set service to Offline mode ---') + opcua_client.get_node('ns=3;s=services.counter.op_src_mode.StateOffOp').set_value(True) + time.sleep(1) + + print('--- Demo complete ---') + opcua_client.disconnect() + module.get_opcua_server().stop() diff --git a/examples/virtual_pea_with_recipe.py b/examples/virtual_pea_with_recipe.py index 4527dcf..950e9e0 100644 --- a/examples/virtual_pea_with_recipe.py +++ b/examples/virtual_pea_with_recipe.py @@ -1,3 +1,10 @@ +import time +import random +from datetime import datetime +import logging + +from opcua import Client + from mtppy.opcua_server_pea import OPCUAServerPEA from mtppy.mtp_generator import MTPGenerator from mtppy.service import Service @@ -5,9 +12,20 @@ from mtppy.parameter_elements import * from mtppy.indicator_elements import * -import time -import random -from datetime import datetime +from mtppy.command_codes import CommandCodes +from mtppy.state_codes import StateCodes + +CommandCodes = CommandCodes() +StateCodes = StateCodes() + +logging.basicConfig( + format='%(asctime)s.%(msecs)03d; [%(levelname)s];' + '%(module)s.%(funcName)s: %(message)s', + datefmt='%H:%M:%S', level=logging.INFO) +logging.getLogger("opcua").setLevel(logging.ERROR) +logging.getLogger("mtppy.service").setLevel(logging.DEBUG) + +_log = logging.getLogger(__name__) class ServiceDummy(Service): @@ -18,24 +36,28 @@ def __init__(self, tag_name, tag_description): def add_service_parameters(self): serv_parameters = [AnaServParam('serv_param_ana', v_min=0, v_max=50, v_scl_min=0, v_scl_max=10, v_unit=23), - DIntServParam('serv_param_dint', v_min=-10, v_max=10, v_scl_min=0, v_scl_max=-10, v_unit=23), - BinServParam('serv_param_bin', v_state_0='state_0', v_state_1='state_1'), + DIntServParam('serv_param_dint', v_min=-10, v_max=10, + v_scl_min=0, v_scl_max=-10, v_unit=23), + BinServParam('serv_param_bin', v_state_0='state_0', + v_state_1='state_1'), StringServParam('serv_param_str') ] [self.add_configuration_parameter(serv_param) for serv_param in serv_parameters] def add_procedures(self): # Procedure 1 - proc_1 = Procedure(0, 'proc_1', is_self_completing=False, is_default=False) + proc_1 = Procedure(1, 'proc_1', is_self_completing=False, is_default=False) # Procedure 2 - proc_2 = Procedure(1, 'proc_2', is_self_completing=True, is_default=True) + proc_2 = Procedure(2, 'proc_2', is_self_completing=True, is_default=True) # Procedure 3 - proc_3 = Procedure(2, 'proc_3', is_self_completing=True, is_default=False) + proc_3 = Procedure(3, 'proc_3', is_self_completing=True, is_default=False) proc_parameters = [AnaServParam('proc_param_ana', v_min=0, v_max=50, v_scl_min=0, v_scl_max=10, v_unit=23), - DIntServParam('proc_param_dint', v_min=-10, v_max=10, v_scl_min=0, v_scl_max=-10, v_unit=23), - BinServParam('proc_param_bin', v_state_0='state_0', v_state_1='state_1'), + DIntServParam('proc_param_dint', v_min=-10, v_max=10, + v_scl_min=0, v_scl_max=-10, v_unit=23), + BinServParam('proc_param_bin', v_state_0='state_0', + v_state_1='state_1'), StringServParam('proc_param_str'), ] [proc_3.add_procedure_parameter(proc_param) for proc_param in proc_parameters] @@ -51,86 +73,40 @@ def add_procedures(self): self.add_procedure(proc_2) self.add_procedure(proc_3) - def idle(self): - print('- Idle -') - cycle = 0 - while self.is_state('idle'): - print('Idle cycle %i' % cycle) - cycle += 1 - time.sleep(1) - def starting(self): - print('- Starting -') - self.state_change() + _log.info('- Starting -') def execute(self): - print('- Execute -') + _log.info('- Execute -') cycle = 0 while self.is_state('execute'): - print('Execute cycle %i' % cycle) - print(f'ProcedureCur is {self.procedure_control.get_procedure_cur()}') - print('ServParameter %s has value %r' - % (self.configuration_parameters['serv_param_ana'].tag_name, - self.configuration_parameters['serv_param_ana'].get_v_out())) - print('ServParameter %s has value %r' - % (self.configuration_parameters['serv_param_dint'].tag_name, - self.configuration_parameters['serv_param_dint'].get_v_out())) - print('ServParameter %s has value %r' - % (self.configuration_parameters['serv_param_bin'].tag_name, - self.configuration_parameters['serv_param_bin'].get_v_out())) - print('ServParameter %s has value %r' - % (self.configuration_parameters['serv_param_str'].tag_name, - self.configuration_parameters['serv_param_str'].get_v_out())) - - if self.procedure_control.get_procedure_cur() == 2: - self.procedures[2].report_values['proc_rv_ana'].set_v(random.random()) - self.procedures[2].report_values['proc_rv_bin'].set_v( - not self.procedures[2].report_values['proc_rv_bin'].attributes['V'].value) - self.procedures[2].report_values['proc_rv_dint'].set_v(random.randint(-100, 100)) - self.procedures[2].report_values['proc_rv_str'].set_v(str(random.random())) + _log.info('Execute cycle %i' % cycle) + _log.info(f'ProcedureCur is {self.procedure_control.get_procedure_cur()}') + _log.info('ServParameter %s has value %r' + % (self.configuration_parameters['serv_param_ana'].tag_name, + self.configuration_parameters['serv_param_ana'].get_v_out())) + _log.info('ServParameter %s has value %r' + % (self.configuration_parameters['serv_param_dint'].tag_name, + self.configuration_parameters['serv_param_dint'].get_v_out())) + _log.info('ServParameter %s has value %r' + % (self.configuration_parameters['serv_param_bin'].tag_name, + self.configuration_parameters['serv_param_bin'].get_v_out())) + _log.info('ServParameter %s has value %r' + % (self.configuration_parameters['serv_param_str'].tag_name, + self.configuration_parameters['serv_param_str'].get_v_out())) + + if self.procedure_control.get_procedure_cur() == 3: + self.procedures[3].report_values['proc_rv_ana'].set_v(random.random()) + self.procedures[3].report_values['proc_rv_bin'].set_v( + not self.procedures[3].report_values['proc_rv_bin'].attributes['V'].value) + self.procedures[3].report_values['proc_rv_dint'].set_v(random.randint(-100, 100)) + self.procedures[3].report_values['proc_rv_str'].set_v(str(random.random())) cycle += 1 time.sleep(1) def completing(self): - self.state_change() - - def completed(self): - pass - - def pausing(self): - pass - - def paused(self): - pass - - def resuming(self): - pass - - def holding(self): - pass - - def held(self): - pass - - def unholding(self): - pass - - def stopping(self): - pass - - def stopped(self): - pass - - def aborting(self): - pass - - def aborted(self): - pass - - def resetting(self): - print('- Resetting -') - self.state_change() + _log.info('- Completing -') if __name__ == '__main__': @@ -140,7 +116,8 @@ def resetting(self): 'WriterVersion': '1.0.0', 'WriterRelease': '', 'LastWritingDateTime': str(datetime.now()), 'WriterProjectTitle': 'tu/plt/mtp', 'WriterProjectID': ''} export_manifest_path = '../manifest_files/example_recipe_manifest.aml' - mtp_generator = MTPGenerator(writer_info_dict, export_manifest_path) + manifest_template_path = '../manifest_files/manifest_template.xml' + mtp_generator = MTPGenerator(writer_info_dict, export_manifest_path, manifest_template_path) module = OPCUAServerPEA(mtp_generator) @@ -153,52 +130,65 @@ def resetting(self): module.run_opcua_server() # Test - opcua_server = module.get_opcua_server() - opcua_ns = module.get_opcua_ns() + opcua_client = Client(module.endpoint) + opcua_client.connect() time.sleep(1) print('--- Set parameters of service dummy to Operator mode ---') - opcua_server.get_node( + opcua_client.get_node( 'ns=3;s=services.dummy.configuration_parameters.serv_param_ana.op_src_mode.StateOpOp').set_value(True) - opcua_server.get_node( + opcua_client.get_node( 'ns=3;s=services.dummy.configuration_parameters.serv_param_dint.op_src_mode.StateOpOp').set_value(True) - opcua_server.get_node( + opcua_client.get_node( 'ns=3;s=services.dummy.configuration_parameters.serv_param_bin.op_src_mode.StateOpOp').set_value(True) - opcua_server.get_node( + opcua_client.get_node( 'ns=3;s=services.dummy.configuration_parameters.serv_param_str.op_src_mode.StateOpOp').set_value(True) time.sleep(1) print('--- Set parameters VOp of service dummy to different values ---') - opcua_server.get_node('ns=3;s=services.dummy.configuration_parameters.serv_param_ana.VOp').set_value(10.54) - opcua_server.get_node('ns=3;s=services.dummy.configuration_parameters.serv_param_dint.VOp').set_value(-5.11) - opcua_server.get_node('ns=3;s=services.dummy.configuration_parameters.serv_param_bin.VOp').set_value(True) - opcua_server.get_node('ns=3;s=services.dummy.configuration_parameters.serv_param_str.VOp').set_value('hello there') + opcua_client.get_node( + 'ns=3;s=services.dummy.configuration_parameters.serv_param_ana.VOp').set_value(10.54) + opcua_client.get_node( + 'ns=3;s=services.dummy.configuration_parameters.serv_param_dint.VOp').set_value(-5.11) + opcua_client.get_node( + 'ns=3;s=services.dummy.configuration_parameters.serv_param_bin.VOp').set_value(True) + opcua_client.get_node( + 'ns=3;s=services.dummy.configuration_parameters.serv_param_str.VOp').set_value('hello there') time.sleep(1) print('--- Change procedure to 2 ---') - opcua_server.get_node('ns=3;s=services.dummy.procedure_control.ProcedureOp').set_value(2) + opcua_client.get_node('ns=3;s=services.dummy.procedure_control.ProcedureOp').set_value(2) time.sleep(1) print('--- Set service dummy to Operator mode ---') - opcua_server.get_node('ns=3;s=services.dummy.op_src_mode.StateOpOp').set_value(True) + opcua_client.get_node('ns=3;s=services.dummy.op_src_mode.StateOpOp').set_value(True) time.sleep(2) print('--- Start service dummy ---') - opcua_server.get_node('ns=3;s=services.dummy.state_machine.CommandOp').set_value(4) - time.sleep(500) + opcua_client.get_node('ns=3;s=services.dummy.state_machine.CommandOp').set_value( + CommandCodes.start) + time.sleep(10) print('--- Try to unhold service dummy ---') - opcua_server.get_node('ns=3;s=services.dummy.state_machine.CommandOp').set_value(32) + opcua_client.get_node('ns=3;s=services.dummy.state_machine.CommandOp').set_value( + CommandCodes.unhold) time.sleep(2) print('--- Complete service dummy ---') - opcua_server.get_node('ns=3;s=services.dummy.state_machine.CommandOp').set_value(1024) + opcua_client.get_node('ns=3;s=services.dummy.state_machine.CommandOp').set_value( + CommandCodes.complete) time.sleep(1) print('--- Reset service dummy ---') - opcua_server.get_node('ns=3;s=services.dummy.state_machine.CommandOp').set_value(2) + opcua_client.get_node('ns=3;s=services.dummy.state_machine.CommandOp').set_value( + CommandCodes.reset) + time.sleep(1) print('--- Set service dummy to Offline mode ---') - opcua_server.get_node('ns=3;s=services.dummy.op_src_mode.StateOffOp').set_value(True) + opcua_client.get_node('ns=3;s=services.dummy.op_src_mode.StateOffOp').set_value(True) time.sleep(1) + + print('--- Demo complete ---') + opcua_client.disconnect() + module.get_opcua_server().stop() diff --git a/manifest_files/example_minimal_manifest.aml b/manifest_files/example_minimal_manifest.aml index 0d20e80..dfd00d1 100644 --- a/manifest_files/example_minimal_manifest.aml +++ b/manifest_files/example_minimal_manifest.aml @@ -17,7 +17,7 @@ www.tud.de 1.0.0 - 2022-08-19 18:45:58.883245 + 2025-10-26 16:33:34.065599 tu/plt/mtp @@ -2355,153 +2355,159 @@ - - + + 1.0.0 - - - + + + rand_num_gen This services generates random number - - 0 - DG3F-agAc-7UQ4-H7pG-vyfN - 255 - OyQ1-lAaU-M0N3-k9qp-6oL5 + qy8r-oydw-OK6h-X7jG-vhew + + + 0 + YriX-2HPW-bN1f-7xRw-EP7g 0 - hi6Y-Y2RM-J1kb-Gm8V-4EeO + hgqX-vAP6-5W6k-mQ8C-z7i2 0 - j1H7-HWQx-1wor-Qke8-dV2h + QCIp-lBwp-HsCY-9hr2-vr0T 0 - GE25-ykm6-vNC8-xqgp-QvAO + Hv5t-N2zf-4vqp-4Vlz-gbel False - RxI2-hAUI-9RGh-xeGK-9gCT + Mujr-27zs-OYb9-mt7S-H9Rh False - oIUR-CsH6-w12J-OnVH-jVmR + mJWV-MN8U-sA89-JvE4-M7nX False - cBgH-NOSL-qHOA-QMsN-rzli + rjKz-es36-aqOS-43xg-lmfc False - b7oO-vCFL-mCpg-UElL-itW8 + ANtP-KZsn-3PB6-rPkJ-TQyK False - jJIV-0hIS-K4iW-KvoA-5Wiz + oqwi-7QUg-PL2p-PMu2-pcxf False - SbGF-n3Th-YXKO-0JDg-ewMu + BpaM-jW17-mE6K-4fWQ-ExFt False - vP1s-tWms-TY5N-pM72-CJy0 + 4tuq-HCTN-Bj5Y-Tk6x-JoE4 False - 5eZ8-KhM6-YR29-LAfx-wQDE + cODk-U0AM-AqRn-YfwA-o89k False - kP6j-nmTQ-QtLr-rMhB-bALl + P6zU-q3u4-Ldy1-Yg6X-TPQC True - UsvK-B1v9-EJ3s-07dA-srjl + Epz3-Byzi-Z5dR-bKsU-63ZP False - enKT-YXF0-fLY7-wS6K-R6wJ + cW5d-N9ZR-k9eq-Ipd8-K5fX False - pDjQ-qsyb-rIOG-WZmB-Dnzu + oVvF-XiBc-6ry8-BYmK-nNMG False - 3EmL-iJY8-hzmG-Ulki-W1zy + Hrxp-eHxg-0Q2A-MABU-oBXm False - pj2L-1GAd-fRv4-MgyU-HuM5 + 6ZPL-loB6-N0VT-IpTC-5K8w False - 0xQE-k59L-JcB3-9hXn-6HmS + Q4gO-ZYpf-YJ67-JKq5-qGlH False - 24W9-RUmj-8mIF-eM1d-4NS0 + xHQj-qA4z-FcZT-cUYg-jhMp False - BZ4l-ELaW-pGZ7-fLGY-2Npj + GTfr-PujU-U91R-fTM7-SH4V - - 0 - eniN-JyA5-i5aP-yrhw-zLsM + + ZME4-BY5b-xYo0-fTsD-w35X - - 0 - K2zx-HrXP-Ktqb-62oV-VsFh + + ZME4-BY5b-xYo0-fTsD-w35X - - 0 - dyZu-g43v-i39z-Bocl-FUBR + + 1 + yUZn-Jjce-u6r2-l9GE-z12a - - 16 - FHKf-ji0g-Mw2k-AqkE-NbPg + + 1 + 23xa-WzSy-Dizq-vR1h-qY76 - - 0 - NGcL-zgMG-81nM-FIMN-nUoZ + + 1 + eazg-oETc-USsn-eCTL-bE6h - + 0 - ByLT-EL5F-8gIh-07gs-jTSG + fCOg-C1xy-lH7C-fHRe-JLMa - + + 1 + 9hbt-kr3E-ENDe-vt8Y-z10f + + 0 - DN8v-gACd-5OTJ-5umT-8GOx + gs3b-Yi48-kgLY-CJ6y-xfIN - + 0 - fWau-QYJZ-htU2-7VZE-3scw + 3O9I-3saC-hmUJ-btGd-NCFw - + 0 - PjwS-SkUQ-uJ8n-pPsI-Nij3 + ZTXt-gJYc-5GdE-i187-u4qW - + + 16 + wohG-ohIS-VpgZ-Enxd-VndK + + 0 - Ss87-SQjo-pHPG-GfnQ-Swyd + HJu8-aHP3-g6jO-Wj9E-MDjP - riqc-7XYh-Xufb-dnF1-Z8Gq + ZME4-BY5b-xYo0-fTsD-w35X - + cont @@ -2510,286 +2516,298 @@ 255 - JSFi-CrHo-qbc7-aoKX-Y56d + Jynk-Elvp-ZrKd-qz7n-0PO9 + + + nieu-k9TJ-c31P-Pp0D-9d1F + + + nieu-k9TJ-c31P-Pp0D-9d1F + + + nieu-k9TJ-c31P-Pp0D-9d1F + + + nieu-k9TJ-c31P-Pp0D-9d1F - wXfS-FX8T-7QC0-5wkJ-Owzp + nieu-k9TJ-c31P-Pp0D-9d1F - + lower_bound - - 0 - xi4p-uhWG-d5Om-U8VP-FVqE - 255 - HSoC-L2Pn-b4dv-kjUP-esuH + 7eHI-ibT2-8l1O-Iz7O-zCcb + + + 0 + CAO3-eW3A-lBL4-Ftzm-hqDI 0 - 2RgF-SBfj-oSU4-u3rg-osx5 + o3D2-7yR8-Lziu-XMYQ-EKh3 0 - 26L3-0ahM-gf38-x0rT-4BHM + NzSZ-kfeC-ls6Y-ZplC-PViZ 0 - Ot1l-CTZW-veLZ-4QrB-I06U + hxAF-lHEW-INKq-qxZo-TckJ 0 - jhbM-xwHT-tbdB-rv3D-iEPr + pJEH-gMGF-G9kb-AGxu-fWUY 0 - 8iN5-8FVd-dFBm-JIlx-WYVf + mIKF-LJcn-L4k9-0Qts-AOBr 0 - CXmH-Xvqp-xDqM-eZg3-m8ny + 9J3w-Z1jb-leL2-BbqP-uXlh 23 - Hlsd-0t2P-POzh-WpyR-tPAL + yIkZ-HxoG-Gv37-GZlm-IekO 0 - jatA-O3Zw-fzRl-CUkO-C6cQ + Y2wN-wnCh-Hqgh-XnAS-to8u 100 - 1pKS-q4uG-NkSg-j4or-qRb0 + AsD8-ay4P-QaDJ-uB0A-ULR1 0 - qutT-s4em-u0Iy-YW23-fUTI + F6Is-vU1R-RY8K-XrUJ-AjfM 100 - 4MOJ-iTga-mACt-fbXZ-3TDa + 1KXB-dIu0-BQ1r-rgT4-LFvg False - dlew-BCsr-QspF-jzWZ-iG4a + bWK6-RHLJ-zX5e-76F0-ZxXp False - 8FGT-I8wG-1ozd-P4Tx-C0uS + hJ4x-Yij7-69lW-0zKJ-mpPF False - DuoH-noGz-Wd2P-fjQT-INOM + uJks-4mys-tsOg-TFGZ-ZXOp False - RMCq-PYOG-3CMv-4NiK-uN0E + jBPD-xNS3-0hBb-2Zt5-8swZ False - IgWv-XBzU-OjdE-2BLV-JOR1 + qUMX-WxjC-91yh-lEKR-Ix25 False - QmjP-tqa1-ghFo-Nyxd-Hg0c + Cpin-2hEd-NkrM-5y4m-bvxa False - TnhM-poIv-5r9D-Eubl-DAiR + wck2-tzRV-aN3M-ZDyv-ofJH False - 89xK-nfYO-mXj0-PpkA-0U4B + x5rB-OXsj-GNkp-vxuU-uIUj False - 5eLI-Ybt4-CgIE-FNpa-aIBe + Jitq-kOce-6PN2-scAM-tP3d False - u0P9-UaBw-oEnU-Oa5t-B8Y9 + sRpC-pXbA-EzDn-rCGB-1Ovb True - kWUI-KzBV-HrsK-17i0-54h8 + GX7u-J20c-E8RU-UVvQ-1Caz False - 7RUz-IrsO-GeNo-jGtO-1zIk + yU5X-RBfa-zEAX-Aza4-PjyL False - VOA5-In5c-G6C2-64HS-FNAd + KAQP-HCxu-UhLi-pRWO-V0bE False - vEKi-mjQX-9Jhr-Goq4-GJBy + MIYL-h0U6-5ACE-DydW-oR8v False - QRfm-IfhY-IpiZ-9zby-9jhe + EF0N-wSBq-P8bG-f6mK-ktP9 False - u9hB-VY5T-Xv0x-Vm3Q-OngJ + 7Lt6-wbn2-bkNJ-iS09-cwyC False - MZ6F-dqGB-TaIo-fk17-dtgr + xtEQ-kE0W-j3V6-CYGi-YSa9 False - YJ9r-G4mq-1neH-NCmg-UzZH + vxza-HRNL-4RaE-02qW-7ORd - BSNu-qUTn-Eawh-CRKr-LvcS + foXx-WB2I-4coD-vAqY-LPeZ - + upper_bound - - 0 - EPBV-AO2s-MJs6-bDQc-mbaj - 255 - P7WM-AG1i-En6Y-JvqU-CG1m + AdaW-swzh-5uJB-L4O8-7Hka + + + 0 + 8UdT-Z8O9-Uned-8zTi-fMaV 0 - cdP9-LJ4U-pZlC-Lwz8-aZun + Gj5c-RPng-eNr0-nIDw-O2Jv 0 - Z0Ba-w7MU-igBp-61Vw-4sGz + 5mtg-imkv-y3xj-SuZ8-YAq2 0 - s5ft-YFKb-suVH-S1az-ASk8 + 5UJ4-pyXc-5eqo-obi4-lNfv 0 - wkgA-8RqZ-i0Bb-osf8-NhpF + vpKe-aClI-0lMh-ISLG-4jTo 0 - y3vH-CdY4-Ciup-MdLw-e6xy + mXOu-EfQm-hEpe-DwjO-Yz5Z 0 - sceA-cxme-clZA-yxmZ-80k7 + Kr15-k60T-SfKt-7jY2-7gIQ 23 - Hf82-nd5f-f2yA-JVhN-juJM + OcUy-Pk5e-WsLV-VN9h-LOgS 0 - p42H-0z8j-3bZv-bzPA-ghtp + lK2U-UIxW-stCN-t5C7-J753 100 - XwWu-pXur-gLF6-TiEz-qKmJ + rRpT-Nrmo-aqxR-Z94z-xvNo 0 - 68my-j0IX-oBx0-QLCu-p0NE + DO8w-2ARo-IVex-I7od-ifzS 100 - q5Bo-qeQT-PvmR-sxJ4-D0he + A4Cx-eU3n-8h7H-mrQK-gATa False - LIJS-xkiM-ic6Q-DczM-GZ9p + BRoY-8L4K-zb0Z-x2Yn-7bDM False - 4nhv-wmab-2szt-H0XA-GNHu + mMXQ-yI7O-BROd-MDlY-JfcT False - prvE-4Vne-jJc8-TawP-qVS4 + 8cj3-mUWY-NzhQ-29XR-o08P False - vbjt-2eom-aFtO-JYm6-XAeR + vSin-leGf-VOwS-DLHc-khfd False - ha5M-saCE-KNCD-Sm8B-ryOD + Me1j-eo5S-sPbF-6irh-ziFc False - 1ntL-4R25-lSVz-S3RB-MKRb + XqeE-JsTX-JYwE-CQhe-p9IU False - e8q4-BmGN-8QR1-yeFi-Lb4N + 29cZ-IbQC-30SU-UHyq-nKfo False - lKPL-uv01-XaKu-F9Hr-MVWZ + rTPe-TpGV-ZrYi-k8LE-Gw0M False - rZyA-ClFi-m5Sr-1WCn-jsBR + 8HBE-xjUy-Vtx3-nPse-q7gZ False - pxlg-DcIg-1YMQ-LRXJ-SqmN + 5xU2-wHxh-pfCs-EZBV-Kiud True - Kihs-MxSA-qSVs-26yP-61RG + zwEx-pMnG-PdHu-7J9y-jCn8 False - lZi7-i9n3-B4an-64DA-evLR + jihb-Jf1H-wJpa-D8TR-YylS False - 4lUS-wlpW-bPx3-LU4P-6t7S + vbRP-wqpy-P57q-hHOx-bo96 False - LuIh-lfsO-Zomt-H8aJ-JpHv + DUmi-5WFH-CV7P-FWwq-XJvP False - fLrS-BE7R-Ozuj-IhGg-nqkj + vgTL-4OeE-qp7J-a8PC-z3bx False - PRwW-B946-QV08-7Nbs-Nnhu + aj4V-5ES7-YwRq-2cHS-MwXF False - XUqY-e7ag-Qieo-LyO4-5Ufy + TA7K-osOc-vNmP-lZaM-kyRN False - i6aM-yOEg-CDzQ-pc2z-rai4 + TEtg-Rf10-OysK-fehX-IyOq - CXUf-VdDC-pa83-2V4m-ya8r + ofXQ-0oA6-h3iB-pi8y-09yB - + generated_value @@ -2798,34 +2816,34 @@ 0 - LJNe-XueK-fIcj-6TDF-bdVW + SoTx-79JG-Aesw-eMFm-wJDP 255 - a2Op-Wwdz-W49V-UkQV-Xw5I + 2VUv-L0X5-bpzk-pkga-hBu2 0.0 - g7Bz-D03Q-ytB2-3nH2-p4XM + TBno-weZa-y6HR-IEGC-lW2p 23 - l8BY-sH0O-fKlz-F3mX-7G5s + pydD-bavz-Hbp2-62H4-rSo8 0.0 - 1hiN-hUAq-BmRw-gCnb-16HJ + 3s1N-7Z8Q-zsdo-NSyK-GAlp 100.0 - TB31-TP6q-Xny3-dL6C-YHbG + 0wjb-dJjh-SdFZ-ZhFC-ULoi - iSh9-49dt-spIx-SquA-4ia9 + TFbt-FvCk-Jmv4-kXFv-r19v - + pid_ctrl @@ -2834,189 +2852,189 @@ 0 - LSgI-rD5b-tlkC-3R48-xDBo + 7PiR-fRFK-40wP-F9Up-bLl6 255 - Q27i-aMPk-3nv6-cST9-zvwm + oEAg-aqn1-kqKi-F1ay-Cp9G 0.0 - IVyM-Ub4I-THin-vVc3-6Pnt + wt4V-ldZ6-GgNT-tdoQ-YEsy 0.0 - jThP-64PC-B17n-wA8C-yo8J + GHsp-bR3A-MtYO-rUdC-nAax 100.0 - sN9I-joRm-OJCF-Gpgv-McVs + NYhl-Yh67-74jN-L5eE-FAtj 0 - YTCX-6wtM-PIZU-2N4W-K5HN + hHjy-6vON-Al4p-Nl92-2zcl 0.0 - mROo-lL0p-l4L5-9E0D-NFuU + dH0s-0TIS-uAWl-2oqD-XZYg 0.0 - qhFv-9NKZ-7oQk-kFo6-fxXe + KW8b-1vNP-eBYl-TZwX-M2hq 0.0 - zU3Q-CIAv-vdjM-CF9P-QgUz + 4E6y-fqLW-3Lzq-f3ue-ISha 100.0 - BEf2-5BU6-mUrq-UoPa-AVUh + VJc5-of5p-JfRZ-0yvA-LCzS 0 - y3Ox-hoAG-L3RG-pwNq-jp1f + iYjo-G5El-4Hdu-LOxl-pn83 0.0 - ONYH-GWqm-BEUK-Uv5z-fwu9 + UDkE-ZT7j-L6XC-c4Lr-8EIr 100.0 - JbYp-sdrH-NJdP-EOrG-cF6T + abDs-aMkZ-wli9-zNPm-OeIn 0.0 - 95fn-KqmM-1zdW-OI0U-8zsS + IKjL-j56g-xw7v-jCHU-cwGa 100.0 - KBPo-WNVm-wCjO-dh0l-KDLW + X7pM-M1Xv-xOwr-FJ3k-9TSk 0.0 - 2X9d-fRXH-gtQO-lFkN-XnZf + IEWz-Yiyd-UINT-uO9b-CQTU 0.0 - fv7D-3c6N-DPo2-Qvqf-H6op + MhW5-rH2A-NXZT-dQho-WKUn 0.0 - JS1Q-vlnz-15EW-2W9s-hNpY + mjXP-lWsp-2QoI-w6qD-tZGD 0.0 - g8dt-HGlE-SVD8-MfnS-6jKd + dLgr-hgCL-3WaL-u7St-AV3p 1000.0 - lq8R-IBb4-cKTR-we1M-dIjS + AKx0-gDIt-kgOQ-K4ET-I34C 0.0 - SXOx-ObE1-KmGS-yLa8-fdji + 1Ldm-Ns18-qNzW-yYz4-Obrh 0.0 - bBpU-Lyrq-Qr8k-ShiG-yt6n + Dd5R-uME2-tFVP-OV12-jMZ7 100.0 - idWh-Vsjb-57RJ-dleu-I5aE + DLUW-efuF-yFkb-rRqd-dASQ 100.0 - eFRL-tY8q-YpGr-dGYX-8X5M + rsD4-PCMb-t5Xc-RnD5-khxi 10.0 - UvNP-HTLJ-xqi2-er09-GhOT + P0jU-3AT0-U0Zi-T1fg-AruL 1.0 - oPwx-l9cF-wcj9-7HLo-SLPX + KNbp-NVFi-Jk68-GfKR-6lM9 False - en1j-Nopq-Um0D-7AQz-jWlA + 2Ae8-w3MX-DqsJ-VA8Z-wa8X False - J2Kd-s6lq-pIox-SHrk-9SoX + SojF-t8gV-fVC3-WkjR-yglH False - 2nRH-n26d-Or6S-K65d-Br6i + lMr0-dHkI-LMki-rtn7-jMP6 False - mNiB-8Irn-UheN-Ef7c-7SNK + izuE-APkw-8GoW-Gwi9-SaoZ False - jtN8-XbWl-T9fn-0Ngx-EzY8 + bxW8-ROwl-GqCa-HvUX-cJ8O False - N5ib-42N5-mhYS-w7u6-rA96 + DBEI-nFCh-wxiP-EaIO-Ne0D False - 7kbs-Dg14-xSmV-j54N-2mCW + 96Uq-Q2cW-IUGu-WmuO-Tc0a False - LijD-4Weo-Rx9H-pM8d-x69e + 5VGE-0dLG-Mvt9-dLti-FshJ False - aKRJ-meL8-jK8O-6DdT-df2I + 1Sbc-ZR7n-QNpm-49qy-34rP True - i57N-5m8r-Plk1-6FSv-NEK7 + qMKz-3RYI-cMfa-nbgz-9sPf False - 9zo2-pgSV-mkow-FfBT-7Zcm + 8Cyc-a30j-Pp79-tPdh-fIuk - + False - Cx3a-IRgF-erQY-XY7i-dYFD + vblO-wPFv-eIdF-mkBK-MDL4 False - EhTM-iwMS-n5pw-clO0-n4rE + uzbn-kheu-h6ZX-skMt-U0Ki False - Eo5k-flTO-xX6J-T6Bb-8XUe + WJgl-opt4-Y3FJ-gq3E-nPQm - + False - AXht-H9sC-n9iC-o58Z-5oOG + FeHc-f91D-xBd3-jzcw-I6Bg False - lUSW-2xyo-2Ruv-PseW-kbuC + blu8-I91E-Ei3c-gsl9-o07R - + False - XJOQ-qSVm-qF7H-jIpg-D7H1 + NjGW-OH8G-Ze9Q-saWJ-Xi9q - P8Un-K0Mf-eCTJ-ovOa-ENAl + Lz98-NVPL-TpJj-1zKS-KLAt - - + + opc.tcp://127.0.0.1:4840/ - + ns=3;s=services.rand_num_gen.tag_name @@ -3027,7 +3045,7 @@ 1 - + ns=3;s=services.rand_num_gen.tag_description @@ -3038,9 +3056,9 @@ 1 - + - ns=3;s=services.rand_num_gen.OSLevel + ns=3;s=services.rand_num_gen.WQC 3 @@ -3049,9 +3067,9 @@ 1 - + - ns=3;s=services.rand_num_gen.WQC + ns=3;s=services.rand_num_gen.OSLevel 3 @@ -3060,7 +3078,7 @@ 1 - + ns=3;s=services.rand_num_gen.PosTextID @@ -3071,7 +3089,7 @@ 1 - + ns=3;s=services.rand_num_gen.InteractQuestionID @@ -3082,7 +3100,7 @@ 1 - + ns=3;s=services.rand_num_gen.InteractAnswerID @@ -3093,9 +3111,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.tag_name + ns=3;s=services.rand_num_gen.op_src_mode.StateChannel 3 @@ -3104,9 +3122,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.tag_description + ns=3;s=services.rand_num_gen.op_src_mode.StateOffAut 3 @@ -3115,9 +3133,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.WQC + ns=3;s=services.rand_num_gen.op_src_mode.StateOpAut 3 @@ -3126,9 +3144,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.IsSelfCompleting + ns=3;s=services.rand_num_gen.op_src_mode.StateAutAut 3 @@ -3137,9 +3155,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.ProcedureId + ns=3;s=services.rand_num_gen.op_src_mode.StateOffOp 3 @@ -3148,9 +3166,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.IsDefault + ns=3;s=services.rand_num_gen.op_src_mode.StateOpOp 3 @@ -3159,9 +3177,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.tag_name + ns=3;s=services.rand_num_gen.op_src_mode.StateAutOp 3 @@ -3170,9 +3188,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.tag_description + ns=3;s=services.rand_num_gen.op_src_mode.StateOpAct 3 @@ -3181,9 +3199,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.OSLevel + ns=3;s=services.rand_num_gen.op_src_mode.StateAutAct 3 @@ -3192,9 +3210,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.WQC + ns=3;s=services.rand_num_gen.op_src_mode.StateOffAct 3 @@ -3203,9 +3221,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VOp + ns=3;s=services.rand_num_gen.op_src_mode.SrcChannel 3 @@ -3214,9 +3232,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VInt + ns=3;s=services.rand_num_gen.op_src_mode.SrcExtAut 3 @@ -3225,9 +3243,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VExt + ns=3;s=services.rand_num_gen.op_src_mode.SrcIntOp 3 @@ -3236,9 +3254,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VReq + ns=3;s=services.rand_num_gen.op_src_mode.SrcIntAut 3 @@ -3247,9 +3265,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VOut + ns=3;s=services.rand_num_gen.op_src_mode.SrcExtOp 3 @@ -3258,9 +3276,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VFbk + ns=3;s=services.rand_num_gen.op_src_mode.SrcIntAct 3 @@ -3269,9 +3287,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VUnit + ns=3;s=services.rand_num_gen.op_src_mode.SrcExtAct 3 @@ -3280,9 +3298,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VSclMin + ns=3;s=services.rand_num_gen.procedures.cont.tag_name 3 @@ -3291,9 +3309,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VSclMax + ns=3;s=services.rand_num_gen.procedures.cont.tag_description 3 @@ -3302,9 +3320,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VMin + ns=3;s=services.rand_num_gen.procedures.cont.WQC 3 @@ -3313,9 +3331,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VMax + ns=3;s=services.rand_num_gen.procedures.cont.IsSelfCompleting 3 @@ -3324,9 +3342,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.Sync + ns=3;s=services.rand_num_gen.procedures.cont.ProcedureId 3 @@ -3335,9 +3353,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateChannel + ns=3;s=services.rand_num_gen.procedures.cont.IsDefault 3 @@ -3346,9 +3364,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateOffAut + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.tag_name 3 @@ -3357,9 +3375,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateOpAut + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.tag_description 3 @@ -3368,9 +3386,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateAutAut + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.WQC 3 @@ -3379,9 +3397,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateOffOp + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.OSLevel 3 @@ -3390,9 +3408,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateOpOp + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VOp 3 @@ -3401,9 +3419,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateAutOp + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VInt 3 @@ -3412,9 +3430,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateOpAct + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VExt 3 @@ -3423,9 +3441,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateAutAct + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VReq 3 @@ -3434,9 +3452,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateOffAct + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VOut 3 @@ -3445,9 +3463,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.SrcChannel + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VFbk 3 @@ -3456,9 +3474,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.SrcExtAut + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VUnit 3 @@ -3467,9 +3485,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.SrcIntOp + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VSclMin 3 @@ -3478,9 +3496,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.SrcIntAut + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VSclMax 3 @@ -3489,9 +3507,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.SrcExtOp + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VMin 3 @@ -3500,9 +3518,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.SrcIntAct + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.VMax 3 @@ -3511,9 +3529,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.SrcExtAct + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.Sync 3 @@ -3522,9 +3540,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.tag_name + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateChannel 3 @@ -3533,9 +3551,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.tag_description + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateOffAut 3 @@ -3544,9 +3562,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.OSLevel + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateOpAut 3 @@ -3555,9 +3573,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.WQC + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateAutAut 3 @@ -3566,9 +3584,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VOp + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateOffOp 3 @@ -3577,9 +3595,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VInt + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateOpOp 3 @@ -3588,9 +3606,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VExt + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateAutOp 3 @@ -3599,9 +3617,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VReq + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateOpAct 3 @@ -3610,9 +3628,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VOut + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateAutAct 3 @@ -3621,9 +3639,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VFbk + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.StateOffAct 3 @@ -3632,9 +3650,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VUnit + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.SrcChannel 3 @@ -3643,9 +3661,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VSclMin + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.SrcExtAut 3 @@ -3654,9 +3672,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VSclMax + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.SrcIntOp 3 @@ -3665,9 +3683,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VMin + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.SrcIntAut 3 @@ -3676,9 +3694,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VMax + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.SrcExtOp 3 @@ -3687,9 +3705,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.Sync + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.SrcIntAct 3 @@ -3698,9 +3716,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateChannel + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.lower_bound.op_src_mode.SrcExtAct 3 @@ -3709,9 +3727,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateOffAut + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.tag_name 3 @@ -3720,9 +3738,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateOpAut + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.tag_description 3 @@ -3731,9 +3749,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateAutAut + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.WQC 3 @@ -3742,9 +3760,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateOffOp + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.OSLevel 3 @@ -3753,9 +3771,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateOpOp + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VOp 3 @@ -3764,9 +3782,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateAutOp + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VInt 3 @@ -3775,9 +3793,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateOpAct + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VExt 3 @@ -3786,9 +3804,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateAutAct + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VReq 3 @@ -3797,9 +3815,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateOffAct + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VOut 3 @@ -3808,9 +3826,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.SrcChannel + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VFbk 3 @@ -3819,9 +3837,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.SrcExtAut + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VUnit 3 @@ -3830,9 +3848,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.SrcIntOp + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VSclMin 3 @@ -3841,9 +3859,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.SrcIntAut + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VSclMax 3 @@ -3852,9 +3870,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.SrcExtOp + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VMin 3 @@ -3863,9 +3881,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.SrcIntAct + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.VMax 3 @@ -3874,9 +3892,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.SrcExtAct + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.Sync 3 @@ -3885,9 +3903,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.report_values.generated_value.tag_name + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateChannel 3 @@ -3896,9 +3914,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.report_values.generated_value.tag_description + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateOffAut 3 @@ -3907,9 +3925,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.report_values.generated_value.OSLevel + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateOpAut 3 @@ -3918,9 +3936,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.report_values.generated_value.WQC + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateAutAut 3 @@ -3929,9 +3947,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.report_values.generated_value.V + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateOffOp 3 @@ -3940,9 +3958,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.report_values.generated_value.VUnit + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateOpOp 3 @@ -3951,9 +3969,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.report_values.generated_value.VSclMin + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateAutOp 3 @@ -3962,9 +3980,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedures.cont.report_values.generated_value.VSclMax + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateOpAct 3 @@ -3973,9 +3991,9 @@ 1 - + - ns=3;s=services.rand_num_gen.op_src_mode.StateChannel + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateAutAct 3 @@ -3984,9 +4002,9 @@ 1 - + - ns=3;s=services.rand_num_gen.op_src_mode.StateOffAut + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.StateOffAct 3 @@ -3995,9 +4013,9 @@ 1 - + - ns=3;s=services.rand_num_gen.op_src_mode.StateOpAut + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.SrcChannel 3 @@ -4006,9 +4024,9 @@ 1 - + - ns=3;s=services.rand_num_gen.op_src_mode.StateAutAut + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.SrcExtAut 3 @@ -4017,9 +4035,9 @@ 1 - + - ns=3;s=services.rand_num_gen.op_src_mode.StateOffOp + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.SrcIntOp 3 @@ -4028,9 +4046,9 @@ 1 - + - ns=3;s=services.rand_num_gen.op_src_mode.StateOpOp + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.SrcIntAut 3 @@ -4039,9 +4057,9 @@ 1 - + - ns=3;s=services.rand_num_gen.op_src_mode.StateAutOp + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.SrcExtOp 3 @@ -4050,9 +4068,9 @@ 1 - + - ns=3;s=services.rand_num_gen.op_src_mode.StateOpAct + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.SrcIntAct 3 @@ -4061,9 +4079,9 @@ 1 - + - ns=3;s=services.rand_num_gen.op_src_mode.StateAutAct + ns=3;s=services.rand_num_gen.procedures.cont.procedure_parameters.upper_bound.op_src_mode.SrcExtAct 3 @@ -4072,9 +4090,9 @@ 1 - + - ns=3;s=services.rand_num_gen.op_src_mode.StateOffAct + ns=3;s=services.rand_num_gen.procedures.cont.report_values.generated_value.tag_name 3 @@ -4083,9 +4101,9 @@ 1 - + - ns=3;s=services.rand_num_gen.op_src_mode.SrcChannel + ns=3;s=services.rand_num_gen.procedures.cont.report_values.generated_value.tag_description 3 @@ -4094,9 +4112,9 @@ 1 - + - ns=3;s=services.rand_num_gen.op_src_mode.SrcExtAut + ns=3;s=services.rand_num_gen.procedures.cont.report_values.generated_value.OSLevel 3 @@ -4105,9 +4123,9 @@ 1 - + - ns=3;s=services.rand_num_gen.op_src_mode.SrcIntOp + ns=3;s=services.rand_num_gen.procedures.cont.report_values.generated_value.WQC 3 @@ -4116,9 +4134,9 @@ 1 - + - ns=3;s=services.rand_num_gen.op_src_mode.SrcIntAut + ns=3;s=services.rand_num_gen.procedures.cont.report_values.generated_value.V 3 @@ -4127,9 +4145,9 @@ 1 - + - ns=3;s=services.rand_num_gen.op_src_mode.SrcExtOp + ns=3;s=services.rand_num_gen.procedures.cont.report_values.generated_value.VUnit 3 @@ -4138,9 +4156,9 @@ 1 - + - ns=3;s=services.rand_num_gen.op_src_mode.SrcIntAct + ns=3;s=services.rand_num_gen.procedures.cont.report_values.generated_value.VSclMin 3 @@ -4149,9 +4167,9 @@ 1 - + - ns=3;s=services.rand_num_gen.op_src_mode.SrcExtAct + ns=3;s=services.rand_num_gen.procedures.cont.report_values.generated_value.VSclMax 3 @@ -4160,9 +4178,9 @@ 1 - + - ns=3;s=services.rand_num_gen.state_machine.CommandOp + ns=3;s=services.rand_num_gen.procedure_control.ProcedureOp 3 @@ -4171,9 +4189,9 @@ 1 - + - ns=3;s=services.rand_num_gen.state_machine.CommandInt + ns=3;s=services.rand_num_gen.procedure_control.ProcedureInt 3 @@ -4182,9 +4200,9 @@ 1 - + - ns=3;s=services.rand_num_gen.state_machine.CommandExt + ns=3;s=services.rand_num_gen.procedure_control.ProcedureExt 3 @@ -4193,9 +4211,9 @@ 1 - + - ns=3;s=services.rand_num_gen.state_machine.StateCur + ns=3;s=services.rand_num_gen.procedure_control.ProcedureCur 3 @@ -4204,9 +4222,9 @@ 1 - + - ns=3;s=services.rand_num_gen.state_machine.CommandEn + ns=3;s=services.rand_num_gen.procedure_control.ProcedureReq 3 @@ -4215,9 +4233,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedure_control.ProcedureOp + ns=3;s=services.rand_num_gen.state_machine.CommandOp 3 @@ -4226,9 +4244,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedure_control.ProcedureInt + ns=3;s=services.rand_num_gen.state_machine.CommandInt 3 @@ -4237,9 +4255,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedure_control.ProcedureExt + ns=3;s=services.rand_num_gen.state_machine.CommandExt 3 @@ -4248,9 +4266,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedure_control.ProcedureCur + ns=3;s=services.rand_num_gen.state_machine.StateCur 3 @@ -4259,9 +4277,9 @@ 1 - + - ns=3;s=services.rand_num_gen.procedure_control.ProcedureReq + ns=3;s=services.rand_num_gen.state_machine.CommandEn 3 @@ -4270,7 +4288,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.tag_name @@ -4281,7 +4299,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.tag_description @@ -4292,7 +4310,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.OSLevel @@ -4303,7 +4321,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.WQC @@ -4314,7 +4332,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.PV @@ -4325,7 +4343,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.PVSclMin @@ -4336,7 +4354,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.PVSclMax @@ -4347,7 +4365,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.PVUnit @@ -4358,7 +4376,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.SPMan @@ -4369,7 +4387,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.SPInt @@ -4380,7 +4398,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.SPSclMin @@ -4391,7 +4409,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.SPSclMax @@ -4402,7 +4420,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.SPUnit @@ -4413,7 +4431,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.SPIntMin @@ -4424,7 +4442,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.SPIntMax @@ -4435,7 +4453,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.SPManMin @@ -4446,7 +4464,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.SPManMax @@ -4457,7 +4475,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.SP @@ -4468,7 +4486,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.MVMan @@ -4479,7 +4497,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.MV @@ -4490,7 +4508,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.MVMin @@ -4501,7 +4519,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.MVMax @@ -4512,7 +4530,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.MVUnit @@ -4523,7 +4541,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.MVSclMin @@ -4534,7 +4552,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.MVSclMax @@ -4545,7 +4563,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.P @@ -4556,7 +4574,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.Ti @@ -4567,7 +4585,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.Td @@ -4578,7 +4596,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.op_src_mode.StateChannel @@ -4589,7 +4607,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.op_src_mode.StateOffAut @@ -4600,7 +4618,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.op_src_mode.StateOpAut @@ -4611,7 +4629,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.op_src_mode.StateAutAut @@ -4622,7 +4640,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.op_src_mode.StateOffOp @@ -4633,7 +4651,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.op_src_mode.StateOpOp @@ -4644,7 +4662,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.op_src_mode.StateAutOp @@ -4655,7 +4673,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.op_src_mode.StateOpAct @@ -4666,7 +4684,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.op_src_mode.StateAutAct @@ -4677,7 +4695,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.op_src_mode.StateOffAct @@ -4688,7 +4706,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.op_src_mode.SrcChannel @@ -4699,9 +4717,9 @@ 1 - + - ns=3;s=active_elements.pid_ctrl.op_src_mode.SrcManAut + ns=3;s=active_elements.pid_ctrl.op_src_mode.SrcExtAut 3 @@ -4710,7 +4728,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.op_src_mode.SrcIntOp @@ -4721,7 +4739,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.op_src_mode.SrcIntAut @@ -4732,9 +4750,9 @@ 1 - + - ns=3;s=active_elements.pid_ctrl.op_src_mode.SrcManOp + ns=3;s=active_elements.pid_ctrl.op_src_mode.SrcExtOp 3 @@ -4743,7 +4761,7 @@ 1 - + ns=3;s=active_elements.pid_ctrl.op_src_mode.SrcIntAct @@ -4754,9 +4772,9 @@ 1 - + - ns=3;s=active_elements.pid_ctrl.op_src_mode.SrcManAct + ns=3;s=active_elements.pid_ctrl.op_src_mode.SrcExtAct 3 @@ -4771,10 +4789,10 @@ - - + + - 4Isb-K1Xr-YTAM-NGto-Ar8O + PJ7I-JoCv-OjFg-VNqC-6g9h @@ -4782,14 +4800,14 @@ - - + + - riqc-7XYh-Xufb-dnF1-Z8Gq + ZME4-BY5b-xYo0-fTsD-w35X - + - 0 + 1 True @@ -4798,23 +4816,23 @@ False - wXfS-FX8T-7QC0-5wkJ-Owzp + nieu-k9TJ-c31P-Pp0D-9d1F - + - BSNu-qUTn-Eawh-CRKr-LvcS + foXx-WB2I-4coD-vAqY-LPeZ - + - CXUf-VdDC-pa83-2V4m-ya8r + ofXQ-0oA6-h3iB-pi8y-09yB - + - iSh9-49dt-spIx-SquA-4ia9 + TFbt-FvCk-Jmv4-kXFv-r19v diff --git a/manifest_files/example_recipe_manifest.aml b/manifest_files/example_recipe_manifest.aml index 8bf5c5d..cc8f157 100644 --- a/manifest_files/example_recipe_manifest.aml +++ b/manifest_files/example_recipe_manifest.aml @@ -17,7 +17,7 @@ www.tud.de 1.0.0 - 2022-05-02 12:08:23.084251 + 2025-10-26 16:36:52.166337 tu/plt/mtp @@ -2355,665 +2355,671 @@ - - + + 1.0.0 - - - + + + dummy description - - 0 - L0kY-IXxe-NGPI-X19q-ABlH - 255 - Tfs0-REf9-4PF2-58Yy-ZYQS + genf-h62q-TFjs-25RS-sReZ + + + 0 + Vs7l-Yn6m-R4uP-ztG2-n5vQ 0 - OspC-VWgs-PG4W-dBq1-n5Ue + BJ4G-8yrI-5Ovc-PTr7-OQdu 0 - Mpe9-aGo4-K1fe-GoP3-szfw + sU4K-yS1j-dpct-F8HX-sZMn 0 - axX5-JL4q-V7Tt-4zXq-5j1t + lzNv-4pT9-ODR5-Bx5R-I2RS False - dDcn-deTw-MHx8-OEy8-nt2p + Zmou-5HMy-9WB2-Nk1l-8Mq5 False - qhv2-7X2q-ZPmU-g6Pe-r4Wo + H6os-yGeY-tEhP-bkBj-cQRv False - 9PKL-OfZv-6VFi-aS69-GogM + Tz0Y-JNce-Je5b-khTU-ugaT False - iJxz-Mtfp-LwsE-CrTR-iurN + qzCk-2fE4-LT27-xVzw-khbZ False - GZP1-FftZ-tTAR-jgRs-Qbz2 + AvS6-luym-BQJ0-69Gf-uph5 False - SIAR-7BG8-w5SX-UhVr-qVK6 + I94s-6HYk-JuBN-0grP-WV1F False - VWwK-BQl2-Rg0W-4qn1-MfSZ + dE7x-8SyE-BUr3-kR0F-EC6G False - MJNt-OuJT-mnuZ-SJRo-nkFJ + PinA-8I0h-hpCR-TGzZ-aujJ False - foiC-8ZRH-lRbs-NJ5a-fWCT + a6lV-5Gkp-MyCI-xQ3N-SQ1a True - xkEb-Uz9M-UPTR-EaRt-HPua + CWLe-McRa-FWLn-nBNS-kXu1 False - oS6B-eHp3-Etpu-zBIL-Flag + v9TC-jyFs-cUXQ-sM0b-RfKX False - mPgG-aVvi-bXGg-D25j-IJZ6 + 4QdE-XrKh-PJXx-krS6-Knes False - ciu8-oX6n-lvfL-Bydn-MC43 + GkMP-RhcY-PJiS-wJ5H-m8Ez False - 1knm-SOTw-NjED-u5tH-31nE + soJx-0rIG-mfNE-oWYx-4DnO False - Ea4b-KXEh-6jow-fvUW-h7My + w1zv-ZRPb-BS93-WPav-NeQ6 False - Ry3C-uOas-bmQf-FMTi-Urdh + VPLm-rRZd-sSgA-gzjo-UAGi False - oOCY-sIA7-w7Py-pasI-DOsY + 8hOl-4219-Do5J-5zhV-1mQ8 + + + LmVk-ox94-8Nfo-fZrd-NyPq + + + LmVk-ox94-8Nfo-fZrd-NyPq + + + 2 + jHsK-RC5v-pZ6d-VHlZ-flmN + + + 2 + hHen-guQ4-2VyC-plkX-Eeqn + + + 2 + YfPo-0yHr-pcP8-6yQE-U9rd + + + 0 + xift-26nS-QJke-X6HI-AiJL + + + 2 + 75SQ-JWfF-i1OP-8Q4b-W8kr 0 - yp76-54Tv-Jmwt-JXLN-aU3d + 6eEY-ZrLE-HUFD-QeoL-f4Z0 0 - XKwf-Eni3-b8FG-ntdq-z8bo + aHto-DXhi-NVAZ-tJYz-6ms2 0 - GMeh-jvmS-7Tv8-0J8P-0S7w + Of0S-1icu-ZBPg-PXup-Rp1m 16 - fyu9-eCfV-eJp1-zqVn-BcWP + U0p9-FwXh-Jpvi-56YH-hOjw 0 - pf4H-qJON-7MEV-UX1q-d0v5 - - - 1 - 3tdg-MaFu-1LAM-xpqn-N0E9 - - - 1 - kr2G-qSEZ-a0xw-Iscb-RwmY - - - 1 - M1t6-Tngk-SbKd-H7OV-0hvV - - - 0 - dfMC-jVc9-HcrX-FRuo-cvIM - - - 0 - qcj2-VrCm-pkE1-S1ni-go0R + kr08-fTHG-kFpA-sNOB-wPvQ - Y9nh-ZYOj-D13L-f3Lj-kD7B + LmVk-ox94-8Nfo-fZrd-NyPq - + serv_param_ana - - 0 - JPjY-0FSB-QNFS-QFl6-3pVg - 255 - fmsV-WwFN-3Bwx-qRcz-nqst + GApf-huxC-DK8s-wfBU-Yju4 + + + 0 + FyvP-H5YW-NIpq-aUEm-P6C7 0.0 - iRs6-5zFU-Ky4G-c4m5-WURO + zL08-zxdy-Ioz6-o76z-iWbR 0.0 - pebq-PRQf-6ujI-skSX-tNx1 + bIwS-CrsR-uIKx-jb3k-XmWO 0.0 - 6huD-ekJ6-0gD2-yRlA-ceF1 + zZe8-wC6y-LbvT-Tl7K-3RZL 0.0 - 8Uo4-uwgt-w8bB-3W4O-iGmt + Play-u39X-AO04-5ULu-BHTx 0.0 - irH9-7Zwc-vCYB-zBJN-C0LV + uSFE-Fw8x-nwFU-1rxS-8jrn 0.0 - dxzF-0ncx-tDV5-8e9R-eZJK + c9qJ-hZHK-gTY6-PfhX-aYUc 23 - BUsW-jvQT-X9Pb-lzDm-SQbZ + Avi0-E5XF-sb9f-wxEI-2yBo 0.0 - K5pm-Vah9-1aO5-ZeFy-DnKZ + qG7C-tdqc-DPCA-psB2-9YHS 10.0 - dUsb-O3mx-pfHy-8QLJ-3vsY + HLob-SjsE-6hlg-vHCd-6j3S 0.0 - FHzZ-x9fe-6PTY-u4TY-VjOq + 7vXE-QLRD-QLUm-wmCQ-Tk0R 50.0 - SsmA-0xu7-OC2e-Z0sY-HZ5v + QZRP-Vdhc-1jKE-pXom-h7Lt False - xTZE-Vagu-q5Yp-cSIT-Ag9q + aPrQ-FG0w-u4sg-SgPe-TzuC False - 7hTp-7oba-w4Ec-P1Yf-IZ96 + EAmj-RIep-bD7T-BkXp-bgIA False - v8K2-M8DC-FBhw-Oto3-pOQ4 + m9n7-bDAM-1CQr-DIui-ZWcF False - hEIX-WAhR-1bpx-oM6n-JmTL + evV0-xIN9-dsxV-DTkS-yAf2 False - h2QL-uydK-d2U5-T0Lr-ouZs + BY81-HZrB-jSob-6lQp-nYFI False - E4nr-cpkf-LOh7-XHv0-JHO7 + JTNE-FaIE-eOrz-jVwc-sdkC False - 6ies-2HgP-7r5s-fZMi-5Omz + Xno5-L7YN-tHkQ-nR3r-nm5H False - shDJ-HKBc-KUnI-NSfU-Jlwb + hWBe-aG2A-yiQr-ea0A-crsm False - cRFx-7iso-tPwQ-BDr0-l1mv + V5wu-Qw1s-xlNL-vYaX-2xV3 False - xmB6-nESH-I5kz-sg4u-OSgh + V14s-PsTI-Grm3-HYKb-KMDu True - WS4s-AY8h-N3w7-fp8L-6kpd + OR9c-QUzJ-pdE4-7Hut-ZDCq False - wn0X-f3wJ-T3p7-zg5r-dpE8 + xyRV-Is0C-6BVI-zmQt-BFQz False - oyLI-g7HW-toKF-uZjL-Da7r + dyPt-ew5R-MWiq-uWpC-y3JV False - N20V-lTUo-eG8E-aNFz-2Gm1 + peJd-TSAo-Dej0-hHst-dIZ5 False - J27M-rlER-Vyjf-hoSJ-ZS5d + 80Et-y30T-6ZtP-z0fq-KUV0 False - r0BJ-dqRM-VaFn-AfZY-Fecb + u4fj-4BfI-rEbu-t7w4-GKos False - ja4G-Noxe-WVS0-7C8I-uoW5 + m386-UtcD-oRId-rW1M-CYWs False - YXAe-LMhT-QbTD-uwDW-Sg5q + Gf4Z-Qg3l-gTvn-bTBf-PWME - Gq27-6Jon-hJdl-fk50-laR8 + GdrK-5EV1-WNOu-h9Ee-YZb2 - + serv_param_dint - - 0 - fquT-7mVJ-sRmj-WGSa-5bPm - 255 - nA6s-kPNm-sISy-nCwE-EoAy + GHQV-RqiO-KE7b-D6dZ-ne8F + + + 0 + PdoL-L2rh-EbVG-OjuE-zReA -10 - neCX-SEmg-ZVcg-YDuH-updw + 2WX9-7XaK-9ufG-h5X8-aO2N -10 - rd5Y-6FBX-Frx7-RS26-mQVr + RCOB-rfCI-cLws-obyM-jRas -10 - yWwC-hLOv-XlOh-5JwQ-rxYI + db4h-04XL-p5Sy-zIOY-SY4a -10 - AQ7k-O9Uz-1WnV-hGOo-CyiA + 4bDr-Ji5C-3Krz-Urp4-1fHd 0 - CIVe-oZ13-HwL6-AnkN-9NAF + Skb4-vJra-qnW0-zGaW-Rki6 0 - O75J-7Xn1-BZqY-nxcH-gEzQ + hJAy-RNvW-ZQt9-Lv12-QXtL 23 - QF18-fDTe-Zkx6-CRZ5-EG6R + RVw0-gnct-gXSp-KcxD-ZWtn 0 - l7PL-lhtp-zDIs-sUMv-hDv5 + 9JLk-Xhrk-ysIm-h82U-vRLE -10 - 0tdD-mOhk-543I-HUfC-jy5k + 1VTY-31ot-3zWU-agZj-4vbX -10 - SigB-zxek-tlPC-2A43-o4gl + p4JO-8PQZ-f3oG-qaN2-lzGV 10 - 2OjJ-N3nW-nwVo-4bsg-BSCL + jhtC-uUJ8-2cMD-pDke-aR6v False - YuIL-si2Z-TmME-ENut-hKFT + PZnd-XPUv-gvFs-bJXh-IdiT False - yIiv-msaM-rvKy-Si8H-6EY5 + dyPY-30jy-xos0-7KgG-3RDl False - aNUj-4I1r-RvAz-10Ln-90Zp + F7Vv-IxP5-7ktG-Ndmc-0Sbm False - csV9-aJfQ-QLP6-YlbO-2lOQ + hYKf-ENJj-7Lo8-rlW9-uACi False - IVki-I1Cm-oKBm-T6U5-9DWq + F9b1-ie9j-x2zU-DUnN-37of False - QRSb-ymPi-YNq9-ms54-kIU9 + E8eZ-FiGz-37Do-KhxC-QPIm False - QkOf-Uwq5-Lz6T-q53y-gMcv + Ctq4-c9JR-Xa4M-4xcO-ZaUE False - 9PWH-bjz1-vI3u-FVp3-V7Tl + 6sNA-Z2hV-nUMc-kUop-3ViZ False - AUr3-gzZ8-g1fA-TJtk-qZYH + lobJ-yuBi-MPDd-ZJYX-inSv False - 3o0t-cVqF-pEIi-yjCo-ur2M + mR35-01ni-juc2-w2dj-SkX0 True - vuER-Cboa-lCYb-6G8W-mvqK + wqBT-a9QJ-aGOV-5yk8-Gshd False - 4iMC-4OIi-EHk2-vqxs-pE0x + IbOT-Pys6-O8ZG-7YGy-chmM False - AaoM-y03g-SjC0-7ZX3-xWwF + lWh7-8tB4-yDKE-1x9X-qmCM False - Jxwk-ib6f-Vuwo-frZb-dcqe + xjkr-vWbY-9T3m-O0QE-Yabs False - 3Yxu-eRj9-IqoZ-RquC-7xOb + 41bQ-p9h0-SfIA-XkU7-2MSW False - wdus-nC6D-Ipbo-lPa5-sOt1 + ryXe-9Iwi-zrYv-hT79-svV2 False - rVB1-QtsL-8P2e-WTmb-IBrs + zQeE-uy8d-FWLu-mvBw-YsRz False - gKNR-LjgH-Pdl0-i073-4hEI + qAxF-mSiG-twLx-caiT-YvZo - OWoA-Ipu3-LJKp-F9Ee-EJh3 + 3hLT-t2Ub-ebuy-7szi-zRQ9 - + serv_param_bin - - 0 - YuW0-T9PM-yx9q-OCEf-uBhE - 255 - ATam-ut2Q-iHEp-ZBHs-qbk9 + 2eTc-ZAcE-fWg8-NH5B-Ojg4 + + + 0 + xKTe-LF6D-RtOP-Grkw-GYfE False - yVeC-u0bR-TMsa-QLbg-rN38 + UuwC-DjQM-dXbG-M1oX-6ZvD False - zK6j-wQYp-4o2B-aG2n-ZakD + iFvs-kSQG-zvTs-JIpg-vnR2 False - 3ajA-JgP3-Zfsj-2o4w-yhfK + lb7y-oELS-6MsC-zDBl-R8hn False - Yjw2-NA6m-iGNY-j20M-4qkJ + OTpb-oM6H-XTAg-fnTw-v1b2 False - uP4y-vVGc-Utws-gAV4-Mxfd + xqXe-m1o0-pWcr-lTtz-hqx6 False - hQsY-ZsKw-7TuX-KykA-tunp + JOjh-YkdG-tE0S-mZGS-u0AW state_0 - 93Aq-04E1-4IUl-B8F5-wRfm + u946-wREo-dm3e-Nd2F-RvF1 state_1 - Bwlg-NKg1-vwV2-FYa0-WLi4 + cP6A-2zLA-XCYl-MWEu-UryN False - EVwW-uNSy-f7mP-w5aC-4qNg + mM1c-7UQs-4VO2-ryzI-5CHw False - ndGT-g7dK-rQ12-1CxO-xqUi + B4Fe-IgSb-IxWY-8df6-DcIC False - kIlC-usSh-aoyB-tPry-a3SD + T8D1-6NRK-w4Bz-7Xq9-fyWI False - p5xJ-4Po8-qw3A-azXf-WKrQ + 7lmz-p3oj-zi0n-DZVh-fjhQ False - LJuj-r65m-2dfF-oWb0-zFUO + Kxp0-zBw1-hxHc-87D1-yLfT False - 1tzV-isFY-RKpz-nDUI-fU2B + 08Cg-imvM-wpY5-cm4n-Ehrk False - JhMs-fB8j-R7cG-Ufh6-R7As + uij2-VJkY-1gsM-aRuJ-lxcb False - IzaA-hyKo-eWmz-urJ4-cJKV + BPc2-VFsJ-dIZV-W1AF-ZYhr False - LhgO-aBb5-Yf9s-M9XK-ltHT + DWgd-8JI5-q3uc-ZL5j-R3Ea False - GMLQ-e0N5-UbfG-C7sA-iMHE + s5oi-fpQ6-sJjU-OEb4-VSWk True - PY0b-8peT-uJWy-HRyU-eVYF + AY4N-Od0u-atqI-cREd-fDHj False - 5AJ3-bcrG-1VyF-mCIl-bsox + uQer-E60h-6OkA-xS0g-LITE False - UYsv-UDOI-X75G-u8LF-YfJx + NMAL-Ur04-pWNd-zTvK-N2H4 False - 5riE-GNQM-bC34-EavX-g5yr + VRGD-1ntN-cyZs-8wCp-ct7z False - JrHy-Sru9-c9X8-o5zP-9p5z + ITR2-35sg-LVvr-wLpe-2wJi False - Z1zd-XjWc-lfeN-sUD9-XdtO + a2Zr-vxOc-CJBL-xGpA-6e5f False - lk8L-cn7t-myZk-f3p7-G0qj + 3qOM-CdmB-g4MH-2hab-qzH1 False - LAEB-97Eo-3Gyc-oM1G-CU3p + TdWv-lZ0n-zxnk-hM9f-kpfG - jAme-IlwP-HK0v-TEQU-0QHD + 40jA-EyKs-T4e3-fh2n-wdNX - + serv_param_str - - 0 - UywJ-ecH8-j85w-LOtM-1Fdf - 255 - KkMe-lCzR-xSaU-Ir7g-3Q0F + WeSM-q1ca-DAVK-1cPt-6zCB + + + 0 + DFji-qJG3-cu8z-p9Mn-ogtm - js81-CaIQ-y4s9-0pXk-TLxA + ZIAn-26bB-bZrh-Fx8k-2TSb - fizB-4r2V-j2xp-1GTP-6tpV + cGqW-ZHFD-o2AV-uzgU-4ntH - JZqS-3qlf-gEn1-2hj6-vV8q + J2ye-3d4m-umwx-W8eG-wHSk - URqC-6b4U-ydtD-pmUx-PCst + Ewxi-3pjS-pdS3-hNxH-prGH - q38D-ykdK-H1Ky-FANX-zryE + N1qL-CyUI-hUcf-J8R2-xyGo - RNpU-3PnO-gtrn-gJ9v-jUls + fUGx-uNHT-1X2t-Ro9c-V6zc False - zmFk-SAoK-l13o-COE3-DG6q + A1s0-ud0c-127m-M1fZ-xUEk False - AkJC-kGcB-9WSA-lt0E-qcyz + g4W0-kyKO-sD5l-J4qZ-ts0w False - hrLI-kbgR-xmQk-nDhw-tNLM + 3kHT-JyY8-TuKb-BAR6-c3Zl False - 7Cj3-VREJ-0hqz-haxD-yOJx + GWox-Np5Z-nKUY-TYXP-YSXv False - Nzaf-m853-jP56-gCar-2iXn + n8ys-JqZO-pol1-KeHn-wlUq False - 7Ijx-vkMq-7UYa-IBbX-ASTv + fcO4-4tHO-cF5Q-BTNg-73Cl False - sd4L-OL04-WMfi-j5CN-PpFx + g3ho-3x4U-1URr-CtzT-YnJ7 False - N0hU-oNZy-u79s-uaWO-qV56 + sKYJ-tUGw-GkOZ-S5Rd-rlPX False - gNw6-0HNz-KCP2-bF8x-1HqI + ZUHk-F6oE-uk2f-znL9-zy5o False - e1Ib-tiKv-v9iy-A1bB-CVFo + ZfYQ-d9El-PBcg-umDl-rN74 True - ZfFi-gedy-m6MF-r7nL-TunL + x0UD-w0IP-0zQX-lciJ-pMh5 False - qFbt-fi3v-go5M-h6Sf-e0Mx + 2X8A-9XEh-SgKM-KN5I-joBD False - aNVm-XjOx-QGkU-ksa5-T3c8 + Y0Qh-t4bS-YrEG-e1Sp-1p2l False - 9DkX-dhM9-c9Kb-LMRm-e2Sg + uF2R-fXST-uCUa-HVXl-AXq6 False - jiYD-MZeO-ypxF-GSYg-jWek + 6n28-bu0D-2T4i-M1XJ-QwFx False - XFyE-Ul3R-jHv2-8KQ4-HF6C + W376-j2Vp-dMe1-FWXq-pILf False - 17FP-yZCF-qOlj-iEu7-hJaI + BGIz-X1xL-N2rd-Wj2z-L6rB False - 5xCv-7lNW-ztbZ-c0jw-Pmhb + JbHV-E72I-OeEY-UIAV-tGwR - ODP8-T3cW-ENLa-p2zC-HqMR + LBGP-5jWD-Fmus-qnTf-inJP - + proc_1 @@ -3022,14 +3028,26 @@ 255 - mqx0-CqXF-kOFY-mWFA-q5lo + oYST-iFpN-Wzru-6krR-j7us + + + afbI-LaKY-PvmG-B31c-kx5f + + + afbI-LaKY-PvmG-B31c-kx5f + + + afbI-LaKY-PvmG-B31c-kx5f + + + afbI-LaKY-PvmG-B31c-kx5f - GRDh-omR5-xMme-LlJA-dDx0 + afbI-LaKY-PvmG-B31c-kx5f - + proc_2 @@ -3038,14 +3056,26 @@ 255 - lpRq-UPpj-BMqs-bs8G-q8YZ + 3ir1-kfnM-msN0-50mX-vR1B + + + EF0Y-eXga-TsEn-lV6w-auQB + + + EF0Y-eXga-TsEn-lV6w-auQB + + + EF0Y-eXga-TsEn-lV6w-auQB + + + EF0Y-eXga-TsEn-lV6w-auQB - VELF-Bx0c-xdL3-NgXS-Zm5T + EF0Y-eXga-TsEn-lV6w-auQB - + proc_3 @@ -3054,526 +3084,538 @@ 255 - 1iew-zo19-sfmH-zeja-WoFV + YJhs-YdPn-3Snq-AagH-J8CQ + + + 5Xic-XpxI-c8wZ-f8kl-axMW + + + 5Xic-XpxI-c8wZ-f8kl-axMW - m1IK-dmiM-l91o-VD2j-JWqQ + 5Xic-XpxI-c8wZ-f8kl-axMW + + + 5Xic-XpxI-c8wZ-f8kl-axMW + + + 5Xic-XpxI-c8wZ-f8kl-axMW - + proc_param_ana - - 0 - yk3H-sHAw-41tx-DFxZ-N67j - 255 - 0zsP-ke6M-Wh5I-b4eF-gn2m + gMYh-X3Uc-gGux-SEPU-iPm3 + + + 0 + dFP2-4UTH-dzIS-PZNm-0GET 0.0 - zZbq-Io3R-hV2v-VYhk-SUKM + aYM9-PDqB-Breb-JxaP-PaMy 0.0 - mWVd-V7wa-n1PU-ogx8-1xVm + Cg5s-VgBJ-DnuQ-Xzkm-Y3yg 0.0 - SWhU-P6bF-YVar-qmSZ-BS1M + Okvm-SJbi-uAkP-HmdI-3eiN 0.0 - Glt6-gS2H-TBGV-3HJQ-Nkf1 + a1Lx-SixY-5gfX-8wcS-QbA9 0.0 - ni3a-Rtko-kwAs-gqLx-2sQl + erHp-Ypf7-Cysw-DicI-rcQO 0.0 - VZL5-6lYy-sd3Z-nzox-QyL6 + NAeh-Dy0p-GnI9-HczM-HYSa 23 - zZ7p-hZCO-aVwF-4hw9-ruL1 + XOTz-hEzB-49EN-q4LZ-JAxK 0.0 - BZp1-vyxZ-dsqx-Sp2f-ioJE + m5CL-4gXp-odKB-eqE5-9lWm 10.0 - R6un-ZQnW-Esl1-SMbv-gzQj + o29B-O8Mk-OoU3-LmFy-snxj 0.0 - jxh0-8QIn-5ekz-kxie-6MPA + F7fp-dakY-KSnT-rgjq-8frb 50.0 - ArTH-o2WC-Qpga-yHaU-m4SN + OvXr-s5wY-6pqP-d5vw-3rhA False - 8DrJ-3lGr-sp4T-4Fi1-FEe1 + QOvK-4D5h-48Vd-Rb82-RCrN False - UJD8-lDbm-qiGP-1b9v-E1ug + zIkx-NY2C-Q57V-RGwU-5IyH False - dpZX-i3IY-fQ3q-vm6x-ZWY9 + XVCZ-XJv9-ENLg-vhy8-ETKa False - Lj1d-eqAh-ruy7-VhAC-mYOP + Bqdv-1Jer-JA8V-YUh3-m3SN False - AOG3-n1io-83Cv-8yms-DtaU + yiHe-W2Dg-CTsb-cK7T-YerW False - UjKJ-iQ98-PGHa-OJFY-yRUj + Z5X3-flmJ-xj5Q-pLx9-aqH7 False - 1INn-Avut-a527-QUoD-3FDM + VpdL-epTM-ChY9-sy1u-msML False - TS6V-iKIR-zXjw-sX7R-iMYT + hrb1-3Dqn-RcFT-mY3M-XRxU False - 4z8H-w2ZJ-LoB6-AiI1-pAjc + 6boY-BSwZ-vaOz-J5sC-MAhk False - tcLx-jpUk-RPT7-FP3n-W2BE + v0uq-JyOa-d5Ck-hDbk-izlX True - LlYH-Gftq-lcRk-eT74-Xk1v + D0QK-dqR5-B5sa-HbdP-cidQ False - o127-Zk3M-gAMV-W6i1-jrFD + 2vUR-Siwr-S5Bt-9PTw-h4Y6 False - RpdK-dnrv-Oc8F-NeDB-x1sH + ARQY-kHKQ-CRuH-T2dn-NpRO False - WcA5-lYbd-b5Ki-i1O8-tBm3 + VIEu-OruM-a2Ai-LBD2-cnTe False - h01x-yBn7-W1aG-J5U0-Chy2 + frqz-c0eO-1Son-SbzV-3C5z False - wQU0-WV01-5DPb-TJkC-O6YL + woyO-MflU-RU8w-VZe6-0JhU False - 49JS-1tmP-YcDK-ZgDq-evo2 + uCy5-gWIJ-5Uvg-xn8B-1l7U False - lI5S-31zo-OPAT-yYci-adkR + Ub8H-m97v-O9Bs-GDX5-kjmP - AgfH-Mi2s-eFEB-HrZM-2ETD + pjZa-AURp-IfsS-Enh7-OlSy - + proc_param_dint - - 0 - l7is-9Ume-15ec-bIGe-p3m7 - 255 - D1uU-pdJ2-H9DR-K9rD-vBom + LE7Y-q07c-1bWg-ZyOm-GxQZ + + + 0 + LHXF-7gxs-67co-M5x6-2G3V -10 - TFKm-LoJ9-qmkb-gx4G-yRIS + o2gq-lJOz-rS6e-Ihtc-k98I -10 - vbD6-rMlE-OjyG-0Xqa-bPLd + PYKI-7SeT-DjQo-lyQ0-btvd -10 - kYbW-3l4G-vypw-kdZn-ZL7d + qpfz-QqTP-X9iG-P7Bt-f6vz -10 - 9bly-80ro-XU7s-ipZO-iZIy + HuVg-iQFe-Nj58-dWbs-LhIZ 0 - vhzr-zHqb-Xfr2-RqbX-NbRq + xgP0-0RI8-tNU9-dXo4-hYoA 0 - PUT7-1ku6-DH0z-OV0c-H7b9 + sgMJ-otf3-PvmC-uAGD-Y4qA 23 - lmY4-u4zp-15N0-6hWS-eD4U + K3Qu-Xnge-LZTy-Hgvy-uOqo 0 - aD9Q-BbZS-WrbP-pVcH-28rp + Gish-Tj5i-5qN6-TbmY-9ShJ -10 - 6dN4-PEiK-JobQ-U91F-zOlD + 4Qq8-bG8K-7BOM-BTbm-P6yo -10 - NaFb-PnvJ-T5R4-vTqK-GUsm + lnEx-LSiU-cwdj-25kL-X2hS 10 - C8p6-UPCI-XZI8-PXTY-1nJR + nFWp-ZeN5-XwtW-Mehg-oGep False - GZtx-OMG9-L1Kq-RMPU-1xQI + 0wxR-PUby-F62t-5Yhn-qPJ4 False - DKrW-6uvo-XBct-w8Nf-FbuL + U3gL-FY8Q-7i2m-6MBE-CXk5 False - gDJR-157i-tAyw-FKzn-4VYr + gl0P-jl9h-tnkr-fTW7-KOzQ False - lLxZ-hapB-u3jP-ozmJ-6rA5 + K8UE-IsxT-E7Oy-GPym-IURh False - G20C-oz6a-DZQa-NG9K-ibrg + CuWH-7ZnT-Qb7l-6kSt-xlKw False - IGes-b0Ch-CJ2P-9VXj-YvlP + BPMH-Tmwu-OIqd-mAqQ-Fc9g False - kZ1g-309J-CZ9S-ezxb-xUGI + mq9Q-KX0U-0lgK-90we-Fn9z False - fcTU-i6MQ-5wSF-Gxrf-G94w + mdhp-EhkX-RJzh-8p1Q-fQBU False - 0Uuo-wVuo-nTCH-iz1V-kWhq + La5H-GLe9-wpfU-QDYI-fe4p False - HhLn-7rzM-Ss38-JkXf-1gH4 + coH2-13jM-mB7x-3jQg-c8gv True - 6YiK-6Cda-p1qn-Dil2-aDIK + mC6f-p5Ku-JuWX-lzvP-XLcd False - jLeO-Y7pv-IDlK-Z5I1-vJXI + N0zU-TqIb-dxT8-e9od-Yhct False - TvV3-u6Hw-LuDW-r9Pq-C809 + SFdZ-gGsJ-Jdyk-yevW-VKjm False - 4iNa-cexI-x7Qq-uqRM-NjkG + mBGd-zpLn-tfGn-ZElQ-Z8Mo False - t0xq-GaUI-vdHx-0NKk-VnKB + tPQA-LIQb-6EVu-3Nhl-TBZK False - 83dX-OfWV-ZCVF-ZGOP-cQsU + SIY8-0wfW-1FNl-p7Ty-stO2 False - lXvV-7dOb-OH76-M6YS-hdS2 + wbJn-azyV-mxIl-mZiA-g5fx False - grZ6-Wdjt-YrXa-wkPm-hRrE + 1ptj-PcZ2-1UuO-YzNj-SjdZ - hADx-uTnV-DCjY-PpWj-JqmR + 6dzn-zCxM-orJC-g0zh-36rl - + proc_param_bin - - 0 - 1u9n-dHmJ-2Akx-EyoR-B1kQ - 255 - Yros-98jH-gMLK-b3yt-XRMT + habp-a7nM-N79l-IjlP-EA9F + + + 0 + 9BSg-rTfR-amPN-w0zQ-Diy8 False - h8Gy-yZzn-Yry6-zd1U-acZz + MdTq-HXfh-6qyG-jBgX-YNpI False - KWGd-F1SV-ynV0-gXvk-kBYi + uf1I-y1Ns-9T8j-dw82-y3lM False - aOFx-3Mv7-raeY-8eyF-x4fh + EuDj-gXho-bvwO-Y0NZ-0yBY False - SLrK-PKOn-Mdkr-kVL3-HhVE + CM9w-VIxm-a4U2-EG2m-VxTX False - HFBj-yEH8-VB62-iCgk-dBwx + qwhI-2quv-NJ0O-yFbC-IG4n False - FkLw-p280-5SFz-QrKu-spva + VXJ5-YuPr-HP9W-zdnM-oAtV state_0 - 5RQX-498X-3dpY-Pft6-PhYR + 2eMG-BJC3-UvZA-nsg9-4rAb state_1 - gRwL-Q8kW-iByk-ftVj-F6XE + AzO7-QsH6-1bTA-tBV0-KQYJ False - n76o-UgSe-rLIK-VblP-cwlX + Zesc-N89t-kZit-B38k-yFmu False - QMVh-5dEA-83s0-p2is-Aiwv + NiMn-bYt8-WXtm-dQW3-UGkQ False - Nzgk-9lgC-0msW-yTVh-dEyB + qnTl-Z1Sx-xKbD-UeIM-1fhJ False - 9vyZ-gx9j-xi4I-Voja-qe85 + Brw5-yUv4-NXSi-4gyr-47yT False - Oz9M-of7c-wFpi-dKSx-1JHK + UM6I-yHsZ-yAni-4Rf7-hXYQ False - 3G9Q-gqfw-jcBI-8Q1o-jP6c + mWMX-gdnE-kQAv-2EjV-cPrx False - aBz0-clnW-H5Yr-1mo5-yeHj + bPAM-lhZL-xeaB-FScX-CQkp False - rAyT-quza-MNks-wXmk-B7km + 6IvG-iYBN-LpJ6-hLZC-Wq1Y False - cUDT-Lo6l-wjMT-V4jh-9lNL + X6Uo-Gx0D-qeST-RetK-XezU False - kjNy-mYjK-PTZR-KfrP-hOAf + WYeG-ORXc-CfpL-57gR-x8ML True - DmJT-3Ur4-c1HX-wRMi-1o0G + Gq4s-Bw1T-2xbV-JZ7F-w4p8 False - f7cW-5aJB-2Pxi-rGsj-uLO8 + zhOY-hJE2-wsPo-o9VP-mtBs False - nSZF-WdSO-slZp-6OHr-tkGK + La16-67xs-Wdnu-VLWk-psLn False - oVLH-GTbw-qDRW-GBCm-7PGm + Nwzd-NJ6L-Wsjr-fq5J-uOI5 False - xLpn-1lqf-8qsD-2dmM-ukfP + yRlV-htyq-FglA-CNEe-9YDb False - aJz5-yGNv-Jyit-ANcj-XIpS + 6RfZ-MmEf-13jn-KxAE-IKJ0 False - ubHP-Agrn-bJ6K-QNi3-Oj4A + 7L8C-jNRB-2r4x-iMwy-eW1t False - xlAV-kfXi-XKgJ-NHad-PNwW + Yxic-ltnI-3Nfk-yxzT-0ftl - lg0V-kOfW-Wrmg-8xBV-xr6O + A83w-Kpvs-lpwj-EKh0-8MmX - + proc_param_str - - 0 - 3Tvw-z8UZ-LKmv-dH7W-pQJ8 - 255 - GRer-y1o7-DUWH-9WKE-3zgM + Z6FO-pYzX-xo2w-e7y9-LRcz + + + 0 + XiTc-0B1A-wO2b-jPi7-kMQ3 - 6Ouz-hGDl-XkLP-VFmt-peB3 + woA4-QUws-9FX1-XDa0-QJw3 - jk3T-9gIO-vDyl-JH7v-ZF80 + 70fZ-c3ua-xN12-okTc-PVgL - Vasm-LOhP-72Qo-ygZX-rkv9 + Fgtj-S08Q-CfnF-dvCW-ZM1B - upFg-pGiv-YDhX-qkBW-cj0C + EBZv-MdQm-IPcj-S2QK-HRMd - KwkC-9eja-0hw9-o9YX-wgDS + sVfb-jyYU-7AtU-CURh-9ClY - nDw2-p85q-MOgq-Iz7Z-lI7y + XVRr-H7yG-GYOX-4qBL-fnUW False - 9iZW-ExsC-U3Yd-AREk-HTaJ + t6JN-2UgS-eytU-IVbJ-LPru False - BmwJ-c3MH-Zl7d-yt1T-FYSh + Y3OV-TWy2-iebA-129D-RkT8 False - lWwH-aSBq-e6l0-n9wl-mEHq + pUrc-QZpd-Vug8-iDO7-WiXu False - Q9Yl-NcuP-ceGn-gTCr-T2nI + 4bKt-FYbO-vmsu-90uA-zIpf False - fOu9-iz1t-TeLv-48s0-MUH7 + PXiR-sd7h-YFPW-LETC-PpMU False - Vo64-oC3X-tq1s-h3bZ-7wKQ + FMkK-7tSw-Zz1V-49Gg-X48H False - iTDj-69yU-0N9k-SBuN-VreJ + 6g08-aZfv-Ye5j-AvGZ-l7Oe False - UY4h-EOnR-tmSN-NFb9-WqKk + NL8w-CVfj-b01C-CEeZ-rZXF False - 8PxM-OpT2-2VX6-78Hu-XeJs + QlDt-a64n-1lSj-TdWi-IbdD False - Q4hX-wi0U-Z8Ok-35sX-EHmN + vsAw-uGqn-oMZR-ESjx-sDIG True - wStr-hpaG-bzAH-sVrd-s0XA + u4Cq-Pu2Q-Iv2s-kq68-FTD8 False - ZUCb-fyBG-4iCO-zqOi-cnqg + Ki0D-i8Mx-irs6-uUoL-gvi9 False - alWY-O7gs-8CEr-KM35-vPpT + 6YRb-KLuU-wZMk-1rDh-N31J False - vFWa-jx9w-DY3I-m9eW-j1kb + PAMn-qBZR-kC3O-P02y-VJsi False - DWjA-0FeB-WyVs-Dc0a-sySI + h4Hv-kmvB-XEeb-GARD-WsgB False - Mmkr-CzY2-42Go-oJdt-tc0T + 1NXF-DU5b-hrLv-EjZz-rJAQ False - e2CW-FEdn-Hz6w-nI4Z-My9V + Xhe4-ky8V-8tUQ-tL8F-GZvI False - GRgL-uiTg-p0Yn-v4dZ-4hzG + cLwg-mbzt-QRZ6-zZeC-xdbK - waWs-Acno-DAz5-cJXs-x8AP + KwHW-OJDm-Dkzy-9eW5-nURv - + proc_rv_ana @@ -3582,34 +3624,34 @@ 0 - XSz6-HLGo-THew-4xDM-fCjT + uVzS-4Zzt-LqJR-FMdC-ql6n 255 - F8SU-6nsG-CiT6-RmnS-Q93T + HPYN-7Qwc-J7XC-EUaf-aG40 0.0 - dAIY-SNKM-oGrw-MOpH-BSEk + cfZY-WDNM-9rJB-UWbq-atXk 23 - 34Vj-ywRe-KX0W-vPCj-tdND + Uhfm-KEmo-DNuI-WG4s-PHmi 0.0 - sM7k-wFnQ-Aogm-Hv6w-Yrt5 + iDAH-ZOLj-Cqt0-mrdc-4uLv 10.0 - nKNk-J3Uy-WofM-lAnC-Fn5X + OcoI-SOWl-wDk4-7CV4-2gfp - kasC-KHPD-7qns-FBky-Epje + Wu4i-9Z6g-Bfcb-bypW-1QrJ - + proc_rv_dint @@ -3618,34 +3660,34 @@ 0 - 7hqR-xdeZ-58OL-0LoD-mpU1 + qLoX-6iIv-Nk3y-ZHYz-p1nx 255 - IVP7-EfP4-qQJi-AwRi-R6co + zMg3-uO1H-vHFy-6EjO-bHKE 0 - 28GJ-cRnb-YzxO-AylR-xQjP + hV7K-MwKb-jHNJ-ToKs-ZBf5 23 - tCOq-K4PY-tsRz-4kum-k9QF + MAxO-JXk7-0cT9-Wc8t-qYME 0 - 3t0X-W6D7-BjlL-tIgC-hvaq + NwE0-uENO-yVqf-N3az-AUG1 -10 - ONAz-drJ3-ldtq-McxB-UoAw + aOb3-RqJQ-DRCU-v8Ac-4nFG - xtDa-fBDy-q2tX-bJE8-3pVL + 3SJ8-HXgp-VLnX-dLVW-zx9E - + proc_rv_bin @@ -3654,30 +3696,30 @@ 0 - f1Xu-GBe4-HlB9-HnG2-waZM + zgb8-zswb-BtF5-VXwb-jA2B 255 - MbVX-Gpz1-cOVb-tZVe-yT7P + YI5q-9NhS-dTDp-CKIR-Rq1n False - fLAz-IbPo-NJZh-TUJt-NRTi + wc6B-pWd4-Bb1n-1hpX-Ef7T state_0 - Zq2m-HN78-eVXm-6xEJ-olt2 + vtlp-N6dZ-Ixp5-qW2w-Yfq6 state_1 - HZ7K-vNCT-dG7e-7W8n-ECKe + uSYI-6cpb-I3au-cei0-b1EW - Purl-2bCJ-Vw1J-YhfX-Cnlw + SDOb-bGH8-EYCg-Y4My-oM9r - + proc_rv_str @@ -3686,29 +3728,29 @@ 0 - R3ZM-4dxb-JS8r-78ky-OMYr + XI1p-QYR5-xK2I-N6WU-tSKC 255 - AVJS-7pnJ-qYwJ-os5f-nPZj + R3ab-8rn9-9TEk-sODE-eZqD False - 5NYt-15CH-DGoh-gxPj-QT5H + v0Sk-qK9n-3tNG-f85R-xYqk - QsWD-eUKw-j2a6-0p4m-SkU7 + yLb7-mbNB-C6vk-bsRI-B9Cx - - + + - opc.tcp://0.0.0.0:4840/ + opc.tcp://127.0.0.1:4840/ - + ns=3;s=services.dummy.tag_name @@ -3719,7 +3761,7 @@ 1 - + ns=3;s=services.dummy.tag_description @@ -3730,9 +3772,9 @@ 1 - + - ns=3;s=services.dummy.OSLevel + ns=3;s=services.dummy.WQC 3 @@ -3741,9 +3783,9 @@ 1 - + - ns=3;s=services.dummy.WQC + ns=3;s=services.dummy.OSLevel 3 @@ -3752,7 +3794,7 @@ 1 - + ns=3;s=services.dummy.PosTextID @@ -3763,7 +3805,7 @@ 1 - + ns=3;s=services.dummy.InteractQuestionID @@ -3774,7 +3816,7 @@ 1 - + ns=3;s=services.dummy.InteractAnswerID @@ -3785,7 +3827,194 @@ 1 - + + + ns=3;s=services.dummy.op_src_mode.StateChannel + + + 3 + + + 1 + + + + + ns=3;s=services.dummy.op_src_mode.StateOffAut + + + 3 + + + 1 + + + + + ns=3;s=services.dummy.op_src_mode.StateOpAut + + + 3 + + + 1 + + + + + ns=3;s=services.dummy.op_src_mode.StateAutAut + + + 3 + + + 1 + + + + + ns=3;s=services.dummy.op_src_mode.StateOffOp + + + 3 + + + 1 + + + + + ns=3;s=services.dummy.op_src_mode.StateOpOp + + + 3 + + + 1 + + + + + ns=3;s=services.dummy.op_src_mode.StateAutOp + + + 3 + + + 1 + + + + + ns=3;s=services.dummy.op_src_mode.StateOpAct + + + 3 + + + 1 + + + + + ns=3;s=services.dummy.op_src_mode.StateAutAct + + + 3 + + + 1 + + + + + ns=3;s=services.dummy.op_src_mode.StateOffAct + + + 3 + + + 1 + + + + + ns=3;s=services.dummy.op_src_mode.SrcChannel + + + 3 + + + 1 + + + + + ns=3;s=services.dummy.op_src_mode.SrcExtAut + + + 3 + + + 1 + + + + + ns=3;s=services.dummy.op_src_mode.SrcIntOp + + + 3 + + + 1 + + + + + ns=3;s=services.dummy.op_src_mode.SrcIntAut + + + 3 + + + 1 + + + + + ns=3;s=services.dummy.op_src_mode.SrcExtOp + + + 3 + + + 1 + + + + + ns=3;s=services.dummy.op_src_mode.SrcIntAct + + + 3 + + + 1 + + + + + ns=3;s=services.dummy.op_src_mode.SrcExtAct + + + 3 + + + 1 + + + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.tag_name @@ -3796,7 +4025,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.tag_description @@ -3807,9 +4036,9 @@ 1 - + - ns=3;s=services.dummy.configuration_parameters.serv_param_ana.OSLevel + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.WQC 3 @@ -3818,9 +4047,9 @@ 1 - + - ns=3;s=services.dummy.configuration_parameters.serv_param_ana.WQC + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.OSLevel 3 @@ -3829,7 +4058,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.VOp @@ -3840,7 +4069,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.VInt @@ -3851,7 +4080,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.VExt @@ -3862,7 +4091,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.VReq @@ -3873,7 +4102,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.VOut @@ -3884,7 +4113,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.VFbk @@ -3895,7 +4124,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.VUnit @@ -3906,7 +4135,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.VSclMin @@ -3917,7 +4146,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.VSclMax @@ -3928,7 +4157,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.VMin @@ -3939,7 +4168,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.VMax @@ -3950,7 +4179,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.Sync @@ -3961,7 +4190,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.op_src_mode.StateChannel @@ -3972,7 +4201,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.op_src_mode.StateOffAut @@ -3983,7 +4212,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.op_src_mode.StateOpAut @@ -3994,7 +4223,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.op_src_mode.StateAutAut @@ -4005,7 +4234,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.op_src_mode.StateOffOp @@ -4016,7 +4245,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.op_src_mode.StateOpOp @@ -4027,7 +4256,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.op_src_mode.StateAutOp @@ -4038,7 +4267,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.op_src_mode.StateOpAct @@ -4049,7 +4278,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.op_src_mode.StateAutAct @@ -4060,7 +4289,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.op_src_mode.StateOffAct @@ -4071,7 +4300,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.op_src_mode.SrcChannel @@ -4082,7 +4311,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.op_src_mode.SrcExtAut @@ -4093,7 +4322,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.op_src_mode.SrcIntOp @@ -4104,7 +4333,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.op_src_mode.SrcIntAut @@ -4115,7 +4344,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.op_src_mode.SrcExtOp @@ -4126,7 +4355,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.op_src_mode.SrcIntAct @@ -4137,7 +4366,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_ana.op_src_mode.SrcExtAct @@ -4148,7 +4377,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.tag_name @@ -4159,7 +4388,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.tag_description @@ -4170,9 +4399,9 @@ 1 - + - ns=3;s=services.dummy.configuration_parameters.serv_param_dint.OSLevel + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.WQC 3 @@ -4181,9 +4410,9 @@ 1 - + - ns=3;s=services.dummy.configuration_parameters.serv_param_dint.WQC + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.OSLevel 3 @@ -4192,7 +4421,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.VOp @@ -4203,7 +4432,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.VInt @@ -4214,7 +4443,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.VExt @@ -4225,7 +4454,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.VReq @@ -4236,7 +4465,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.VOut @@ -4247,7 +4476,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.VFbk @@ -4258,7 +4487,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.VUnit @@ -4269,7 +4498,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.VSclMin @@ -4280,7 +4509,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.VSclMax @@ -4291,7 +4520,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.VMin @@ -4302,7 +4531,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.VMax @@ -4313,7 +4542,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.Sync @@ -4324,7 +4553,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.op_src_mode.StateChannel @@ -4335,7 +4564,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.op_src_mode.StateOffAut @@ -4346,7 +4575,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.op_src_mode.StateOpAut @@ -4357,7 +4586,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.op_src_mode.StateAutAut @@ -4368,7 +4597,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.op_src_mode.StateOffOp @@ -4379,7 +4608,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.op_src_mode.StateOpOp @@ -4390,7 +4619,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.op_src_mode.StateAutOp @@ -4401,7 +4630,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.op_src_mode.StateOpAct @@ -4412,7 +4641,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.op_src_mode.StateAutAct @@ -4423,7 +4652,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.op_src_mode.StateOffAct @@ -4434,7 +4663,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.op_src_mode.SrcChannel @@ -4445,7 +4674,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.op_src_mode.SrcExtAut @@ -4456,7 +4685,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.op_src_mode.SrcIntOp @@ -4467,7 +4696,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.op_src_mode.SrcIntAut @@ -4478,7 +4707,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.op_src_mode.SrcExtOp @@ -4489,7 +4718,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.op_src_mode.SrcIntAct @@ -4500,7 +4729,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_dint.op_src_mode.SrcExtAct @@ -4511,7 +4740,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.tag_name @@ -4522,7 +4751,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.tag_description @@ -4533,9 +4762,9 @@ 1 - + - ns=3;s=services.dummy.configuration_parameters.serv_param_bin.OSLevel + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.WQC 3 @@ -4544,9 +4773,9 @@ 1 - + - ns=3;s=services.dummy.configuration_parameters.serv_param_bin.WQC + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.OSLevel 3 @@ -4555,7 +4784,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.VOp @@ -4566,7 +4795,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.VInt @@ -4577,7 +4806,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.VExt @@ -4588,7 +4817,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.VReq @@ -4599,7 +4828,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.VOut @@ -4610,7 +4839,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.VFbk @@ -4621,7 +4850,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.VState0 @@ -4632,7 +4861,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.VState1 @@ -4643,7 +4872,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.Sync @@ -4654,7 +4883,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.op_src_mode.StateChannel @@ -4665,7 +4894,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.op_src_mode.StateOffAut @@ -4676,7 +4905,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.op_src_mode.StateOpAut @@ -4687,7 +4916,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.op_src_mode.StateAutAut @@ -4698,7 +4927,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.op_src_mode.StateOffOp @@ -4709,7 +4938,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.op_src_mode.StateOpOp @@ -4720,7 +4949,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.op_src_mode.StateAutOp @@ -4731,7 +4960,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.op_src_mode.StateOpAct @@ -4742,7 +4971,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.op_src_mode.StateAutAct @@ -4753,7 +4982,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.op_src_mode.StateOffAct @@ -4764,7 +4993,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.op_src_mode.SrcChannel @@ -4775,7 +5004,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.op_src_mode.SrcExtAut @@ -4786,7 +5015,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.op_src_mode.SrcIntOp @@ -4797,7 +5026,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.op_src_mode.SrcIntAut @@ -4808,7 +5037,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.op_src_mode.SrcExtOp @@ -4819,7 +5048,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.op_src_mode.SrcIntAct @@ -4830,7 +5059,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_bin.op_src_mode.SrcExtAct @@ -4841,7 +5070,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.tag_name @@ -4852,7 +5081,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.tag_description @@ -4863,9 +5092,9 @@ 1 - + - ns=3;s=services.dummy.configuration_parameters.serv_param_str.OSLevel + ns=3;s=services.dummy.configuration_parameters.serv_param_str.WQC 3 @@ -4874,9 +5103,9 @@ 1 - + - ns=3;s=services.dummy.configuration_parameters.serv_param_str.WQC + ns=3;s=services.dummy.configuration_parameters.serv_param_str.OSLevel 3 @@ -4885,7 +5114,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.VOp @@ -4896,7 +5125,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.VInt @@ -4907,7 +5136,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.VExt @@ -4918,7 +5147,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.VReq @@ -4929,7 +5158,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.VOut @@ -4940,7 +5169,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.VFbk @@ -4951,7 +5180,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.Sync @@ -4962,7 +5191,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.op_src_mode.StateChannel @@ -4973,7 +5202,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.op_src_mode.StateOffAut @@ -4984,7 +5213,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.op_src_mode.StateOpAut @@ -4995,7 +5224,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.op_src_mode.StateAutAut @@ -5006,7 +5235,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.op_src_mode.StateOffOp @@ -5017,7 +5246,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.op_src_mode.StateOpOp @@ -5028,7 +5257,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.op_src_mode.StateAutOp @@ -5039,7 +5268,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.op_src_mode.StateOpAct @@ -5050,7 +5279,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.op_src_mode.StateAutAct @@ -5061,7 +5290,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.op_src_mode.StateOffAct @@ -5072,7 +5301,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.op_src_mode.SrcChannel @@ -5083,7 +5312,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.op_src_mode.SrcExtAut @@ -5094,7 +5323,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.op_src_mode.SrcIntOp @@ -5105,7 +5334,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.op_src_mode.SrcIntAut @@ -5116,7 +5345,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.op_src_mode.SrcExtOp @@ -5127,7 +5356,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.op_src_mode.SrcIntAct @@ -5138,7 +5367,7 @@ 1 - + ns=3;s=services.dummy.configuration_parameters.serv_param_str.op_src_mode.SrcExtAct @@ -5149,7 +5378,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_1.tag_name @@ -5160,7 +5389,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_1.tag_description @@ -5171,7 +5400,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_1.WQC @@ -5182,7 +5411,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_1.IsSelfCompleting @@ -5193,7 +5422,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_1.ProcedureId @@ -5204,7 +5433,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_1.IsDefault @@ -5215,7 +5444,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_2.tag_name @@ -5226,7 +5455,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_2.tag_description @@ -5237,7 +5466,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_2.WQC @@ -5248,7 +5477,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_2.IsSelfCompleting @@ -5259,7 +5488,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_2.ProcedureId @@ -5270,7 +5499,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_2.IsDefault @@ -5281,7 +5510,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.tag_name @@ -5292,7 +5521,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.tag_description @@ -5303,7 +5532,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.WQC @@ -5314,7 +5543,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.IsSelfCompleting @@ -5325,7 +5554,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.ProcedureId @@ -5336,7 +5565,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.IsDefault @@ -5347,7 +5576,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.tag_name @@ -5358,7 +5587,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.tag_description @@ -5369,9 +5598,9 @@ 1 - + - ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.OSLevel + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.WQC 3 @@ -5380,9 +5609,9 @@ 1 - + - ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.WQC + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.OSLevel 3 @@ -5391,7 +5620,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.VOp @@ -5402,7 +5631,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.VInt @@ -5413,7 +5642,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.VExt @@ -5424,7 +5653,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.VReq @@ -5435,7 +5664,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.VOut @@ -5446,7 +5675,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.VFbk @@ -5457,7 +5686,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.VUnit @@ -5468,7 +5697,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.VSclMin @@ -5479,7 +5708,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.VSclMax @@ -5490,7 +5719,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.VMin @@ -5501,7 +5730,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.VMax @@ -5512,7 +5741,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.Sync @@ -5523,7 +5752,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.op_src_mode.StateChannel @@ -5534,7 +5763,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.op_src_mode.StateOffAut @@ -5545,7 +5774,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.op_src_mode.StateOpAut @@ -5556,7 +5785,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.op_src_mode.StateAutAut @@ -5567,7 +5796,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.op_src_mode.StateOffOp @@ -5578,7 +5807,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.op_src_mode.StateOpOp @@ -5589,7 +5818,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.op_src_mode.StateAutOp @@ -5600,7 +5829,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.op_src_mode.StateOpAct @@ -5611,7 +5840,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.op_src_mode.StateAutAct @@ -5622,7 +5851,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.op_src_mode.StateOffAct @@ -5633,7 +5862,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.op_src_mode.SrcChannel @@ -5644,7 +5873,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.op_src_mode.SrcExtAut @@ -5655,7 +5884,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.op_src_mode.SrcIntOp @@ -5666,7 +5895,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.op_src_mode.SrcIntAut @@ -5677,7 +5906,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.op_src_mode.SrcExtOp @@ -5688,7 +5917,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.op_src_mode.SrcIntAct @@ -5699,7 +5928,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_ana.op_src_mode.SrcExtAct @@ -5710,7 +5939,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.tag_name @@ -5721,7 +5950,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.tag_description @@ -5732,9 +5961,9 @@ 1 - + - ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.OSLevel + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.WQC 3 @@ -5743,9 +5972,9 @@ 1 - + - ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.WQC + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.OSLevel 3 @@ -5754,7 +5983,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.VOp @@ -5765,7 +5994,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.VInt @@ -5776,7 +6005,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.VExt @@ -5787,7 +6016,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.VReq @@ -5798,7 +6027,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.VOut @@ -5809,7 +6038,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.VFbk @@ -5820,7 +6049,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.VUnit @@ -5831,7 +6060,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.VSclMin @@ -5842,7 +6071,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.VSclMax @@ -5853,7 +6082,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.VMin @@ -5864,7 +6093,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.VMax @@ -5875,7 +6104,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.Sync @@ -5886,7 +6115,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.op_src_mode.StateChannel @@ -5897,7 +6126,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.op_src_mode.StateOffAut @@ -5908,7 +6137,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.op_src_mode.StateOpAut @@ -5919,7 +6148,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.op_src_mode.StateAutAut @@ -5930,7 +6159,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.op_src_mode.StateOffOp @@ -5941,7 +6170,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.op_src_mode.StateOpOp @@ -5952,7 +6181,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.op_src_mode.StateAutOp @@ -5963,7 +6192,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.op_src_mode.StateOpAct @@ -5974,7 +6203,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.op_src_mode.StateAutAct @@ -5985,7 +6214,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.op_src_mode.StateOffAct @@ -5996,7 +6225,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.op_src_mode.SrcChannel @@ -6007,7 +6236,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.op_src_mode.SrcExtAut @@ -6018,7 +6247,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.op_src_mode.SrcIntOp @@ -6029,7 +6258,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.op_src_mode.SrcIntAut @@ -6040,7 +6269,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.op_src_mode.SrcExtOp @@ -6051,7 +6280,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.op_src_mode.SrcIntAct @@ -6062,7 +6291,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_dint.op_src_mode.SrcExtAct @@ -6073,7 +6302,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.tag_name @@ -6084,7 +6313,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.tag_description @@ -6095,9 +6324,9 @@ 1 - + - ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.OSLevel + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.WQC 3 @@ -6106,9 +6335,9 @@ 1 - + - ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.WQC + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.OSLevel 3 @@ -6117,7 +6346,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.VOp @@ -6128,7 +6357,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.VInt @@ -6139,7 +6368,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.VExt @@ -6150,7 +6379,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.VReq @@ -6161,7 +6390,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.VOut @@ -6172,7 +6401,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.VFbk @@ -6183,7 +6412,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.VState0 @@ -6194,7 +6423,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.VState1 @@ -6205,7 +6434,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.Sync @@ -6216,7 +6445,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.op_src_mode.StateChannel @@ -6227,7 +6456,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.op_src_mode.StateOffAut @@ -6238,7 +6467,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.op_src_mode.StateOpAut @@ -6249,7 +6478,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.op_src_mode.StateAutAut @@ -6260,7 +6489,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.op_src_mode.StateOffOp @@ -6271,7 +6500,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.op_src_mode.StateOpOp @@ -6282,7 +6511,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.op_src_mode.StateAutOp @@ -6293,7 +6522,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.op_src_mode.StateOpAct @@ -6304,7 +6533,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.op_src_mode.StateAutAct @@ -6315,7 +6544,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.op_src_mode.StateOffAct @@ -6326,7 +6555,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.op_src_mode.SrcChannel @@ -6337,7 +6566,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.op_src_mode.SrcExtAut @@ -6348,7 +6577,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.op_src_mode.SrcIntOp @@ -6359,7 +6588,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.op_src_mode.SrcIntAut @@ -6370,7 +6599,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.op_src_mode.SrcExtOp @@ -6381,7 +6610,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.op_src_mode.SrcIntAct @@ -6392,7 +6621,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_bin.op_src_mode.SrcExtAct @@ -6403,7 +6632,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.tag_name @@ -6414,7 +6643,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.tag_description @@ -6425,9 +6654,9 @@ 1 - + - ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.OSLevel + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.WQC 3 @@ -6436,9 +6665,9 @@ 1 - + - ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.WQC + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.OSLevel 3 @@ -6447,7 +6676,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.VOp @@ -6458,7 +6687,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.VInt @@ -6469,7 +6698,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.VExt @@ -6480,7 +6709,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.VReq @@ -6491,7 +6720,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.VOut @@ -6502,7 +6731,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.VFbk @@ -6513,7 +6742,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.Sync @@ -6524,7 +6753,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.op_src_mode.StateChannel @@ -6535,7 +6764,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.op_src_mode.StateOffAut @@ -6546,7 +6775,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.op_src_mode.StateOpAut @@ -6557,7 +6786,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.op_src_mode.StateAutAut @@ -6568,7 +6797,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.op_src_mode.StateOffOp @@ -6579,7 +6808,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.op_src_mode.StateOpOp @@ -6590,7 +6819,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.op_src_mode.StateAutOp @@ -6601,7 +6830,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.op_src_mode.StateOpAct @@ -6612,7 +6841,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.op_src_mode.StateAutAct @@ -6623,7 +6852,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.op_src_mode.StateOffAct @@ -6634,7 +6863,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.op_src_mode.SrcChannel @@ -6645,7 +6874,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.op_src_mode.SrcExtAut @@ -6656,7 +6885,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.op_src_mode.SrcIntOp @@ -6667,7 +6896,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.op_src_mode.SrcIntAut @@ -6678,7 +6907,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.op_src_mode.SrcExtOp @@ -6689,7 +6918,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.op_src_mode.SrcIntAct @@ -6700,7 +6929,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.procedure_parameters.proc_param_str.op_src_mode.SrcExtAct @@ -6711,7 +6940,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_ana.tag_name @@ -6722,7 +6951,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_ana.tag_description @@ -6733,7 +6962,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_ana.OSLevel @@ -6744,7 +6973,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_ana.WQC @@ -6755,7 +6984,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_ana.V @@ -6766,7 +6995,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_ana.VUnit @@ -6777,7 +7006,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_ana.VSclMin @@ -6788,7 +7017,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_ana.VSclMax @@ -6799,7 +7028,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_dint.tag_name @@ -6810,7 +7039,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_dint.tag_description @@ -6821,7 +7050,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_dint.OSLevel @@ -6832,7 +7061,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_dint.WQC @@ -6843,7 +7072,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_dint.V @@ -6854,7 +7083,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_dint.VUnit @@ -6865,7 +7094,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_dint.VSclMin @@ -6876,7 +7105,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_dint.VSclMax @@ -6887,7 +7116,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_bin.tag_name @@ -6898,7 +7127,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_bin.tag_description @@ -6909,7 +7138,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_bin.OSLevel @@ -6920,7 +7149,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_bin.WQC @@ -6931,7 +7160,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_bin.V @@ -6942,7 +7171,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_bin.VState0 @@ -6953,7 +7182,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_bin.VState1 @@ -6964,7 +7193,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_str.tag_name @@ -6975,7 +7204,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_str.tag_description @@ -6986,7 +7215,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_str.OSLevel @@ -6997,7 +7226,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_str.WQC @@ -7008,7 +7237,7 @@ 1 - + ns=3;s=services.dummy.procedures.proc_3.report_values.proc_rv_str.V @@ -7019,141 +7248,9 @@ 1 - - - ns=3;s=services.dummy.op_src_mode.StateChannel - - - 3 - - - 1 - - - - - ns=3;s=services.dummy.op_src_mode.StateOffAut - - - 3 - - - 1 - - - - - ns=3;s=services.dummy.op_src_mode.StateOpAut - - - 3 - - - 1 - - - + - ns=3;s=services.dummy.op_src_mode.StateAutAut - - - 3 - - - 1 - - - - - ns=3;s=services.dummy.op_src_mode.StateOffOp - - - 3 - - - 1 - - - - - ns=3;s=services.dummy.op_src_mode.StateOpOp - - - 3 - - - 1 - - - - - ns=3;s=services.dummy.op_src_mode.StateAutOp - - - 3 - - - 1 - - - - - ns=3;s=services.dummy.op_src_mode.StateOpAct - - - 3 - - - 1 - - - - - ns=3;s=services.dummy.op_src_mode.StateAutAct - - - 3 - - - 1 - - - - - ns=3;s=services.dummy.op_src_mode.StateOffAct - - - 3 - - - 1 - - - - - ns=3;s=services.dummy.op_src_mode.SrcChannel - - - 3 - - - 1 - - - - - ns=3;s=services.dummy.op_src_mode.SrcExtAut - - - 3 - - - 1 - - - - - ns=3;s=services.dummy.op_src_mode.SrcIntOp + ns=3;s=services.dummy.procedure_control.ProcedureOp 3 @@ -7162,9 +7259,9 @@ 1 - + - ns=3;s=services.dummy.op_src_mode.SrcIntAut + ns=3;s=services.dummy.procedure_control.ProcedureInt 3 @@ -7173,9 +7270,9 @@ 1 - + - ns=3;s=services.dummy.op_src_mode.SrcExtOp + ns=3;s=services.dummy.procedure_control.ProcedureExt 3 @@ -7184,9 +7281,9 @@ 1 - + - ns=3;s=services.dummy.op_src_mode.SrcIntAct + ns=3;s=services.dummy.procedure_control.ProcedureCur 3 @@ -7195,9 +7292,9 @@ 1 - + - ns=3;s=services.dummy.op_src_mode.SrcExtAct + ns=3;s=services.dummy.procedure_control.ProcedureReq 3 @@ -7206,7 +7303,7 @@ 1 - + ns=3;s=services.dummy.state_machine.CommandOp @@ -7217,7 +7314,7 @@ 1 - + ns=3;s=services.dummy.state_machine.CommandInt @@ -7228,7 +7325,7 @@ 1 - + ns=3;s=services.dummy.state_machine.CommandExt @@ -7239,7 +7336,7 @@ 1 - + ns=3;s=services.dummy.state_machine.StateCur @@ -7250,7 +7347,7 @@ 1 - + ns=3;s=services.dummy.state_machine.CommandEn @@ -7261,71 +7358,16 @@ 1 - - - ns=3;s=services.dummy.procedure_control.ProcedureOp - - - 3 - - - 1 - - - - - ns=3;s=services.dummy.procedure_control.ProcedureInt - - - 3 - - - 1 - - - - - ns=3;s=services.dummy.procedure_control.ProcedureExt - - - 3 - - - 1 - - - - - ns=3;s=services.dummy.procedure_control.ProcedureCur - - - 3 - - - 1 - - - - - ns=3;s=services.dummy.procedure_control.ProcedureReq - - - 3 - - - 1 - - - - + + - vP0R-v418-SydE-YGMa-9l8n + InB6-mpWt-9yJQ-Yn2y-VqSW @@ -7333,38 +7375,38 @@ - - + + - Y9nh-ZYOj-D13L-f3Lj-kD7B + LmVk-ox94-8Nfo-fZrd-NyPq - + - Gq27-6Jon-hJdl-fk50-laR8 + GdrK-5EV1-WNOu-h9Ee-YZb2 - + - OWoA-Ipu3-LJKp-F9Ee-EJh3 + 3hLT-t2Ub-ebuy-7szi-zRQ9 - + - jAme-IlwP-HK0v-TEQU-0QHD + 40jA-EyKs-T4e3-fh2n-wdNX - + - ODP8-T3cW-ENLa-p2zC-HqMR + LBGP-5jWD-Fmus-qnTf-inJP - + - 0 + 1 False @@ -7373,13 +7415,13 @@ False - GRDh-omR5-xMme-LlJA-dDx0 + afbI-LaKY-PvmG-B31c-kx5f - + - 1 + 2 True @@ -7388,13 +7430,13 @@ True - VELF-Bx0c-xdL3-NgXS-Zm5T + EF0Y-eXga-TsEn-lV6w-auQB - + - 2 + 3 False @@ -7403,53 +7445,53 @@ True - m1IK-dmiM-l91o-VD2j-JWqQ + 5Xic-XpxI-c8wZ-f8kl-axMW - + - AgfH-Mi2s-eFEB-HrZM-2ETD + pjZa-AURp-IfsS-Enh7-OlSy - + - hADx-uTnV-DCjY-PpWj-JqmR + 6dzn-zCxM-orJC-g0zh-36rl - + - lg0V-kOfW-Wrmg-8xBV-xr6O + A83w-Kpvs-lpwj-EKh0-8MmX - + - waWs-Acno-DAz5-cJXs-x8AP + KwHW-OJDm-Dkzy-9eW5-nURv - + - kasC-KHPD-7qns-FBky-Epje + Wu4i-9Z6g-Bfcb-bypW-1QrJ - + - xtDa-fBDy-q2tX-bJE8-3pVL + 3SJ8-HXgp-VLnX-dLVW-zx9E - + - Purl-2bCJ-Vw1J-YhfX-Cnlw + SDOb-bGH8-EYCg-Y4My-oM9r - + - QsWD-eUKw-j2a6-0p4m-SkU7 + yLb7-mbNB-C6vk-bsRI-B9Cx From 82afcc52a5f96fa6333f1f9c9e4376677558b8a5 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Sun, 26 Oct 2025 16:47:44 +0100 Subject: [PATCH 59/61] included simple_pid for active_element pid --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 77eeb4a..defcd16 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ -opcua~=0.98.13 \ No newline at end of file +opcua~=0.98.13 +simple_pid~=2.0.1 \ No newline at end of file From ddb62b128ef7c91f52ae414a8a07f279c3d782da Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Sun, 26 Oct 2025 17:15:18 +0100 Subject: [PATCH 60/61] updated readme to reflect current state --- readme.md | 144 ++++++++++++++++++++++++------------------------------ 1 file changed, 65 insertions(+), 79 deletions(-) diff --git a/readme.md b/readme.md index 458e3df..0987970 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,7 @@ # MTPPy ## Modular automation + Modular Automation defined in VDI/VDE/NAMUR 2658 offers promising technologies for the process and chemical industry. It meets the requirements for greater flexibility and a shorter development cycle for production facilities by dividing the process into smaller standardized units. Key element of Modular Automation is the Module Type Package (MTP). @@ -8,20 +9,21 @@ It represents a standardized and manufacturer-independent description of the aut (PEA), and endows the plug-and-produce capability of PEAs. ## Description + The structure of the package mirrors the service interface defined in VDI/VDE/NAMUR 2658 Parts 1, 3, and 4. Each data assembly has its own class inherited parameters and methods from a higher level class. The design of the architecture is based on an assumption, that addition of new data assemblies must be possible. The top level of the application is defined by the class OPCUAServerPEA. This class represents the OPC UA server instance for a single PEA and is responsible for the event-based connection between data assembly’s attributes and the OPC UA server. -This class contains a service set in the form of a dictionary of implemented services as well as a list of active elements defined on the PEA level. +This class contains a service set in the form of a dictionary of implemented services as well as a list of indicator, operation and active elements defined on the PEA level. The class OPCUAServerPEA can also generate the MTP manifest. For this it recursively iterates through the structure of the user-defined service set and adds corresponding entities to the overall MTP structure. In the end, the method outputs the MTP manifest as an aml file, to be imported into a Process Orchestration Layer software. -Due to open-source paradigm, the MTP manifest can also be extended by other components, e.g. safety relations or energy consumption aspects that currently not defined in the MTP standard.\newline +Due to open-source paradigm, the MTP manifest can also be extended by other components, e.g. safety relations or energy consumption aspects that currently not defined in the MTP standard. The class Service represents single services according to VDI/VDE/NAMUR 2658 and provides methods to add configuration parameters and procedures to the service. Single states of the state machine, e.g. idle, starting, execute, completing, etc., are defined as abstract methods that must be further defined by the user for each concrete service. The class Service also contains further objects that are responsible for Operation and Source Modes, State Machine and Procedures. -Each of them is implemented as a corresponding class where internal logic according to the standard is defined internally, e.g. transition from Offline mode to Operator mode, CommandEn, state change for states of the state machine.\newline +Each of them is implemented as a corresponding class where internal logic according to the standard is defined internally, e.g. transition from Offline mode to Operator mode, CommandEn, state change for states of the state machine. Procedures as instances of the class Procedure can be added to a service using the corresponding method in the class Service. The procedure itself is defined by means of related procedure parameters, process value in, process value out, and report values. The class ProcedureControl serves as a manager for procedures and controls the execution of the selected procedures on the service level. @@ -30,156 +32,136 @@ CommandEnControl is another class defined on the service level as an object of t It represents an object to control state changes that can be executed by the state machine. Each time any command is received from the OPC UA server, a check is made whether the incoming command can be executed from the current state as defined in the VDI/VDE/NAMUR 2658. - Currently only selected data assemblies that are necessarily relevant for soft sensors are implemented. -This includes IndicatorElements (BinView, AnaView, DIntView, and StringView), OperationElements (BinServParam, AnaServParam, DIntServParamt, StringServParam) and ActiveElements (AnaVlv, BinVlv, AnaDrv, BinDrv, PIDCtrl), which are defined in Part 4 of VDI/VDE/NAMUR 2658. +This includes IndicatorElements (BinView, AnaView, DIntView, and StringView), OperationElements (BinMan, AnaMan, DIntMan, BinManInt, AnaManInt, DIntManInt) and ActiveElements (AnaVlv, BinVlv, AnaDrv, BinDrv, PIDCtrl), which are defined in Part 4 of VDI/VDE/NAMUR 2658. Nevertheless, the architecture of the package allows the development of further data assembly classes. - The single states run in separate threads that are managed in the class ThreadControl. This class makes sure that after each state change the thread that corresponds to the previous state will be naturally terminated, and another thread of the next state started. -Having separate threads allows the running of additional operations, such as data pre-processing or model inference parallel to the main functions of the PEA.\newline +Having separate threads allows the running of additional operations, such as data pre-processing or model inference parallel to the main functions of the PEA. Coupling of data assemblies with OPC UA variables is done by means of event-based subscriptions. Each OPC UA node that allows changes from the operator or POL via the OPC UA interface creates a subscription object with the corresponding callback method. Each time a change happens, it triggers the callback method with its new value as an input argument. For example, an incoming command to start the service will run checks first and then execute the corresponding service method named start.\newline -The architecture of MTPPy is depicted in Figure \ref{fig:class_diagram} in the form of a simplified class diagram. +The architecture of MTPPy is depicted in Figure in the res folder in the form of a simplified class diagram. ## Installation + To install the package via pip: + ```console pip3 install mtppy ``` ## Development workflow + To develop a PEA using MTPPy, the following workflow is advised. First, an object of the class OPCUAServerPEA is initialized. The server endpoint and port can be defined in the attribute named endpoint. Each service must be defined accordingly: + * Configuration parameters are defined and attached to the service. * At least one procedure must be defined within the service. The procedure might also have internal parameters that need to be also defined and added to the procedure. * The central task of the developer is to detail the business logic of each state in the service. -For this, abstract methods of the instantiated Service class objects must be defined. -Here, service configuration parameters, and procedure parameters can be easily accessed and manipulated. -For example, in the state starting the required ML model can be imported. -Further, in the state execute, data are requested, pre-processed, and fed to the prediction function. -Further, the predicted value is put into a specific report value. -It is then available for other PEAs or within the process control system. -Here, the main advantage of MTPPy is clearly visible. -The developer can seamlessly implement data analytical functionality into services and other types of data assemblies directly in the Python environment without building interfaces to separate components. + For this, abstract methods of the instantiated Service class objects must be defined. + Here, service configuration parameters, and procedure parameters can be easily accessed and manipulated. + For example, in the state starting the required ML model can be imported. + Further, in the state execute, data are requested, pre-processed, and fed to the prediction function. + Further, the predicted value is put into a specific report value. + It is then available for other PEAs or within the process control system. + Here, the main advantage of MTPPy is clearly visible. + The developer can seamlessly implement data analytical functionality into services and other types of data assemblies directly in the Python environment without building interfaces to separate components. * Service must be added to the OPCUAServerPEA instance. -* Active elements defined on the PEA level must be also added to the OPCUAServerPEA instance. +* Elements defined on the PEA level must be also added to the OPCUAServerPEA instance. After definition of the service set, the MTP file can be generated and the instance of OPCUAServerPEA can be started in one code line. ## Example +More examples can be found the the example folder. Those show: + +* minimal virtual PEA +* virtual PEA with recipe +* virtual PEA with pause hold and restart +* virtual PEA with events +* Data Objects + ### Service definition + ```python from mtppy.service import Service from mtppy.procedure import Procedure -from mtppy.operation_elements import * +from mtppy.parameter_elements import * from mtppy.indicator_elements import * +_log = logging.getLogger(__name__) + + class RandomNumberGenerator(Service): def __init__(self, tag_name, tag_description): super().__init__(tag_name, tag_description) # Procedure definition - proc_0 = Procedure(0, 'cont', is_self_completing=False, is_default=True) + proc_1 = Procedure(1, 'cont', is_self_completing=False, is_default=True) # Adding two procedure parameters - proc_0.add_procedure_parameter( + proc_1.add_procedure_parameter( DIntServParam('lower_bound', v_min=0, v_max=100, v_scl_min=0, v_scl_max=100, v_unit=23)) - proc_0.add_procedure_parameter( + proc_1.add_procedure_parameter( DIntServParam('upper_bound', v_min=0, v_max=100, v_scl_min=0, v_scl_max=100, v_unit=23)) + # optional: link op_src_mode of procedure parameters to service op_src_mode + for parameter in proc_1.procedure_parameters.values(): + self.op_src_mode.add_linked_op_src_mode(parameter.op_src_mode) + # Adding procedure report value - proc_0.add_report_value( + proc_1.add_report_value( AnaView('generated_value', v_scl_min=0, v_scl_max=100, v_unit=23), ) # Allocating procedure to the service - self.add_procedure(proc_0) - - def idle(self): - print('- Idle -') - cycle = 0 - while self.is_state('idle'): - print(f'Idle cycle {cycle}') - print('Doing nothing...') - cycle += 1 - time.sleep(1) + self.add_procedure(proc_1) def starting(self): - print('- Starting -') - print('Applying procedure parameters...') - self.state_change() + _log.info('- Starting -') + _log.info('Applying procedure parameters...') def execute(self): - print('- Execute -') + _log.info('- Execute -') cycle = 0 while self.is_state('execute'): - print('Execute cycle %i' % cycle) + _log.info('Execute cycle %i' % cycle) # Read procedure parameters - lower_bound = self.procedures[0].procedure_parameters['lower_bound'].get_v_out() - upper_bound = self.procedures[0].procedure_parameters['upper_bound'].get_v_out() + lower_bound = self.procedures[1].procedure_parameters['lower_bound'].get_v_out() + upper_bound = self.procedures[1].procedure_parameters['upper_bound'].get_v_out() # Execute random number generation generated_number = random.randint(lower_bound, upper_bound) # Return report value - self.procedures[0].report_values['generated_value'].set_v(generated_number) + self.procedures[1].report_values['generated_value'].set_v(generated_number) cycle += 1 time.sleep(0.1) def completing(self): - self.state_change() - - def completed(self): - pass + _log.info('- Completing -') - def pausing(self): - pass - - def paused(self): - pass - - def resuming(self): - pass - - def holding(self): - pass - - def held(self): - pass - - def unholding(self): - pass - - def stopping(self): - pass - - def stopped(self): - pass + # starting, execute, completing methods have to be defined for each service + # other state methods (see service documentation) can be overridden as needed, + # by default they only log state entries +``` - def aborting(self): - pass +Take into account that the **starting**, **execute** and **completing** states need to be explicitly defined, even if there are no actions. The other states log their entries by default and can be overridden to implement functionality. - def aborted(self): - pass +States must be implemented in a way that they can react to state changes by checking the `is_state("name_of_state")` function regularly. An event-based system is also possible. For more information, see the corresponding example. - def resetting(self): - print('- Resetting -') - self.state_change() -``` - -Take into account that all state need to be explicitly defined. Even if there are no actions. -In additional, active elemements that are service-independent should be defined and added to the PEA. +In addition, elements that are service-independent should be defined and added to the PEA. ### PEA definition + Now, the service with its procedure can be added to the PEA instance. + ```python from mtppy.opcua_server_pea import OPCUAServerPEA from mtppy.active_elements import PIDCtrl @@ -198,10 +180,13 @@ module.add_active_element(pid_ctrl) print('--- Start OPC UA server ---') module.run_opcua_server() ``` + The last line will start the OPC UA server. ### MTP generation + To generate an MTP manifest, instantiate an MTP generator. + ```python from mtppy.mtp_generator import MTPGenerator @@ -220,6 +205,7 @@ The manifest template can be downloaded here: https://github.com/p2o-lab/MTPPy/blob/master/manifest_files/manifest_template.xml and add it as an input argument to the PEA instance. + ```python module = OPCUAServerPEA(mtp_generator) -``` \ No newline at end of file +``` From 3f20e9dd9b91969b92f1e7440cce3a1ff6c3a5d3 Mon Sep 17 00:00:00 2001 From: Lukas Beck Date: Sun, 26 Oct 2025 18:31:29 +0100 Subject: [PATCH 61/61] example for exception handling --- examples/virtual_pea_exception_handling.py | 159 +++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 examples/virtual_pea_exception_handling.py diff --git a/examples/virtual_pea_exception_handling.py b/examples/virtual_pea_exception_handling.py new file mode 100644 index 0000000..01a8797 --- /dev/null +++ b/examples/virtual_pea_exception_handling.py @@ -0,0 +1,159 @@ +import time +import random +import logging + +from opcua import Client + +from mtppy.opcua_server_pea import OPCUAServerPEA +from mtppy.service import Service +from mtppy.procedure import Procedure +from mtppy.indicator_elements import * + +from mtppy.command_codes import CommandCodes +from mtppy.state_codes import StateCodes + +CommandCodes = CommandCodes() +StateCodes = StateCodes() + +logging.basicConfig( + format='%(asctime)s.%(msecs)03d; [%(levelname)s];' + '%(module)s.%(funcName)s: %(message)s', + datefmt='%H:%M:%S', level=logging.INFO) +logging.getLogger("opcua").setLevel(logging.ERROR) +logging.getLogger("mtppy.service").setLevel(logging.DEBUG) + +_log = logging.getLogger(__name__) + + +class ErrorException(Exception): + pass + + +class WarningException(Exception): + pass + + +class RandomNumberGenerator(Service): + def __init__(self, tag_name, tag_description): + super().__init__(tag_name, tag_description, self.exception_callback) + + # Procedure definition + proc_1 = Procedure(1, 'default', is_self_completing=False, is_default=True) + + # Adding procedure report value + proc_1.add_report_value( + AnaView('generated_value', v_scl_min=0, v_scl_max=100, v_unit=23), + ) + + # Allocating procedure to the service + self.add_procedure(proc_1) + + self.enable_hold_loop() + + self.cycle_count = 0 + + def starting(self): + _log.info('- Starting -') + _log.info('Applying procedure parameters...') + self.cycle_count = 0 + + def execute(self): + _log.info('- Execute -') + lower_bound = 0 + upper_bound = 10 + while self.is_state('execute'): + _log.info(f'Execute cycle {self.cycle_count}') + + # Execute random number generation + generated_number = random.randint(lower_bound, upper_bound) + + # Return report value + self.procedures[1].report_values['generated_value'].set_v(generated_number) + if generated_number == 5: + raise WarningException('This is a warning exception for testing.') + if generated_number == 8: + raise ErrorException('This is an error exception for testing.') + self.cycle_count += 1 + time.sleep(0.1) + + def completing(self): + _log.info('- Completing -') + + def exception_callback(self, exception: Exception): + if isinstance(exception, WarningException): + _log.warning(f'Warning exception handled in exception_callback: {str(exception)}') + # handle warning exception (e.g., set service to hold state) + self.state_machine.hold() + elif isinstance(exception, ErrorException): + _log.error(f'Error exception handled in exception_callback: {str(exception)}') + # handle error exception (e.g., set service to aborted state) + self.state_machine.abort() + else: + _log.error(f'Unknown exception handled in exception_callback: {str(exception)}') + self.state_machine.abort() + + # starting, execute, completing methods have to be defined for each service + # other state methods (see service documentation) can be overridden as needed, + # by default they only log state entries + + +if __name__ == '__main__': + + module = OPCUAServerPEA() + + # Service definition + service_1 = RandomNumberGenerator('rand_num_gen', 'This services generates random number') + module.add_service(service_1) + + # Start server + print('--- Start OPC UA server ---') + module.run_opcua_server() + + ############################################################################################### + # Test + opcua_client = Client(module.endpoint) + opcua_client.connect() + time.sleep(1) + + print('--- Set service to Operator mode ---') + opcua_client.get_node('ns=3;s=services.rand_num_gen.op_src_mode.StateOpOp').set_value(True) + time.sleep(1) + + print('--- Start service ---') + opcua_client.get_node('ns=3;s=services.rand_num_gen.state_machine.CommandOp').set_value( + CommandCodes.start) + + cycle = 0 + while cycle < 12: + if opcua_client.get_node('ns=3;s=services.rand_num_gen.state_machine.StateCur').get_value() == StateCodes.held: + print(f'--- Unholding service at cycle ---') + opcua_client.get_node('ns=3;s=services.rand_num_gen.state_machine.CommandOp').set_value( + CommandCodes.unhold) + if opcua_client.get_node('ns=3;s=services.rand_num_gen.state_machine.StateCur').get_value() == StateCodes.aborted: + print(f'--- Resetting service at cycle ---') + opcua_client.get_node('ns=3;s=services.rand_num_gen.state_machine.CommandOp').set_value( + CommandCodes.reset) + break + + time.sleep(1) + cycle += 1 + + if cycle >= 12: + print('--- Complete service ---') + opcua_client.get_node('ns=3;s=services.rand_num_gen.state_machine.CommandOp').set_value( + CommandCodes.complete) + time.sleep(1) + + print('--- Reset service ---') + opcua_client.get_node('ns=3;s=services.rand_num_gen.state_machine.CommandOp').set_value( + CommandCodes.reset) + time.sleep(1) + + time.sleep(1) + print('--- Set service to Offline mode ---') + opcua_client.get_node('ns=3;s=services.rand_num_gen.op_src_mode.StateOffOp').set_value(True) + time.sleep(1) + + print('--- Demo complete ---') + opcua_client.disconnect() + module.get_opcua_server().stop()