MTPPy2.0 0.1.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. examples/__init__.py +0 -0
  2. examples/minimal+pid.py +82 -0
  3. examples/minimal.py +32 -0
  4. examples/minimal_recipe.py +117 -0
  5. examples/pea_alarm_base.py +47 -0
  6. examples/pea_minimal.py +258 -0
  7. mtppy/__init__.py +1 -0
  8. mtppy/part1/__init__.py +4 -0
  9. mtppy/part1/empty_folder_test.py +7 -0
  10. mtppy/part3/__init__.py +4 -0
  11. mtppy/part3/data_assembly_set/__init__.py +4 -0
  12. mtppy/part3/data_assembly_set/active_elements/__init__.py +4 -0
  13. mtppy/part3/data_assembly_set/active_elements/active_element.py +22 -0
  14. mtppy/part3/data_assembly_set/active_elements/drives/__init__.py +4 -0
  15. mtppy/part3/data_assembly_set/active_elements/drives/ana_drv.py +446 -0
  16. mtppy/part3/data_assembly_set/active_elements/drives/ana_drv_test.py +567 -0
  17. mtppy/part3/data_assembly_set/active_elements/drives/bin_drv.py +365 -0
  18. mtppy/part3/data_assembly_set/active_elements/drives/bin_drv_test.py +503 -0
  19. mtppy/part3/data_assembly_set/active_elements/drives/mon_ana_drv.py +469 -0
  20. mtppy/part3/data_assembly_set/active_elements/drives/mon_ana_drv_test.py +418 -0
  21. mtppy/part3/data_assembly_set/active_elements/drives/mon_bin_drv.py +295 -0
  22. mtppy/part3/data_assembly_set/active_elements/drives/mon_bin_drv_test.py +265 -0
  23. mtppy/part3/data_assembly_set/active_elements/pid/__init__.py +4 -0
  24. mtppy/part3/data_assembly_set/active_elements/pid/pid_ctrl.py +333 -0
  25. mtppy/part3/data_assembly_set/active_elements/pid/pid_ctrl_test.py +156 -0
  26. mtppy/part3/data_assembly_set/active_elements/valves/__init__.py +4 -0
  27. mtppy/part3/data_assembly_set/active_elements/valves/ana_vlv.py +402 -0
  28. mtppy/part3/data_assembly_set/active_elements/valves/ana_vlv_test.py +732 -0
  29. mtppy/part3/data_assembly_set/active_elements/valves/bin_vlv.py +279 -0
  30. mtppy/part3/data_assembly_set/active_elements/valves/bin_vlv_test.py +585 -0
  31. mtppy/part3/data_assembly_set/active_elements/valves/mon_ana_vlv.py +402 -0
  32. mtppy/part3/data_assembly_set/active_elements/valves/mon_ana_vlv_test.py +270 -0
  33. mtppy/part3/data_assembly_set/active_elements/valves/mon_bin_vlv.py +286 -0
  34. mtppy/part3/data_assembly_set/active_elements/valves/mon_bin_vlv_test.py +224 -0
  35. mtppy/part3/data_assembly_set/base_functions/__init__.py +4 -0
  36. mtppy/part3/data_assembly_set/base_functions/operation_mode.py +252 -0
  37. mtppy/part3/data_assembly_set/base_functions/operation_mode_test.py +260 -0
  38. mtppy/part3/data_assembly_set/base_functions/oslevel.py +35 -0
  39. mtppy/part3/data_assembly_set/base_functions/source_mode.py +189 -0
  40. mtppy/part3/data_assembly_set/base_functions/source_mode_test.py +112 -0
  41. mtppy/part3/data_assembly_set/base_functions/wqc.py +75 -0
  42. mtppy/part3/data_assembly_set/base_functions/wqc_test.py +86 -0
  43. mtppy/part3/data_assembly_set/data_assembly.py +65 -0
  44. mtppy/part3/data_assembly_set/diagnostic_elements/__init__.py +4 -0
  45. mtppy/part3/data_assembly_set/diagnostic_elements/diagnostic_element.py +19 -0
  46. mtppy/part3/data_assembly_set/indicator_elements/__init__.py +4 -0
  47. mtppy/part3/data_assembly_set/indicator_elements/indicator_element.py +22 -0
  48. mtppy/part3/data_assembly_set/indicator_elements/views/__init__.py +4 -0
  49. mtppy/part3/data_assembly_set/indicator_elements/views/ana_view.py +68 -0
  50. mtppy/part3/data_assembly_set/indicator_elements/views/bin_view.py +73 -0
  51. mtppy/part3/data_assembly_set/indicator_elements/views/dint_view.py +76 -0
  52. mtppy/part3/data_assembly_set/indicator_elements/views/string_view.py +55 -0
  53. mtppy/part3/data_assembly_set/operation_elements/__init__.py +4 -0
  54. mtppy/part3/data_assembly_set/operation_elements/operation_element.py +19 -0
  55. mtppy/part3/data_assembly_set/pea_elements/__init__.py +4 -0
  56. mtppy/part3/data_assembly_set/pea_elements/pea_element.py +28 -0
  57. mtppy/part3/data_assembly_set/pea_elements/pea_information_label/__init__.py +4 -0
  58. mtppy/part3/data_assembly_set/pea_elements/pea_information_label/pea_information_label.py +231 -0
  59. mtppy/part3/data_assembly_set/pea_elements/web_server_url_info/__init__.py +4 -0
  60. mtppy/part3/data_assembly_set/pea_elements/web_server_url_info/web_server_url_info.py +42 -0
  61. mtppy/part3/definitions/__init__.py +4 -0
  62. mtppy/part3/definitions/mtp_access.py +15 -0
  63. mtppy/part4/__init__.py +4 -0
  64. mtppy/part4/automation_services/__init__.py +4 -0
  65. mtppy/part4/automation_services/base_functions/__init__.py +4 -0
  66. mtppy/part4/automation_services/base_functions/service_operation_mode.py +368 -0
  67. mtppy/part4/automation_services/base_functions/service_operation_mode_test.py +242 -0
  68. mtppy/part4/automation_services/base_functions/service_operator_interaction.py +62 -0
  69. mtppy/part4/automation_services/base_functions/service_source_mode.py +138 -0
  70. mtppy/part4/automation_services/base_functions/service_source_mode_test.py +179 -0
  71. mtppy/part4/automation_services/helpers/__init__.py +4 -0
  72. mtppy/part4/automation_services/helpers/command_en_control.py +330 -0
  73. mtppy/part4/automation_services/helpers/command_en_control_test.py +302 -0
  74. mtppy/part4/automation_services/helpers/state_machine.py +167 -0
  75. mtppy/part4/automation_services/helpers/state_machine_test.py +294 -0
  76. mtppy/part4/automation_services/helpers/thread_control.py +39 -0
  77. mtppy/part4/automation_services/helpers/thread_control_test.py +80 -0
  78. mtppy/part4/automation_services/procedure.py +111 -0
  79. mtppy/part4/automation_services/procedure_test.py +185 -0
  80. mtppy/part4/automation_services/service.py +463 -0
  81. mtppy/part4/automation_services/service_elements/__init__.py +4 -0
  82. mtppy/part4/automation_services/service_elements/parameter_elements/__init__.py +4 -0
  83. mtppy/part4/automation_services/service_elements/parameter_elements/ana_serv_param.py +108 -0
  84. mtppy/part4/automation_services/service_elements/parameter_elements/ana_serv_param_test.py +59 -0
  85. mtppy/part4/automation_services/service_elements/parameter_elements/bin_serv_param.py +70 -0
  86. mtppy/part4/automation_services/service_elements/parameter_elements/bin_serv_param_test.py +39 -0
  87. mtppy/part4/automation_services/service_elements/parameter_elements/common_serv_param.py +111 -0
  88. mtppy/part4/automation_services/service_elements/parameter_elements/dint_serv_param.py +77 -0
  89. mtppy/part4/automation_services/service_elements/parameter_elements/dint_serv_param_test.py +58 -0
  90. mtppy/part4/automation_services/service_elements/parameter_elements/parameter_element.py +175 -0
  91. mtppy/part4/automation_services/service_elements/parameter_elements/parameter_element_test.py +246 -0
  92. mtppy/part4/automation_services/service_elements/parameter_elements/string_serv_param.py +48 -0
  93. mtppy/part4/automation_services/service_elements/parameter_elements/string_serv_param_test.py +37 -0
  94. mtppy/part4/automation_services/service_elements/procedure_health_view.py +36 -0
  95. mtppy/part4/automation_services/service_elements/service_control.py +473 -0
  96. mtppy/part4/automation_services/service_elements/service_control_test.py +150 -0
  97. mtppy/part4/automation_services/service_elements/service_element.py +20 -0
  98. mtppy/part4/automation_services/service_test.py +407 -0
  99. mtppy/part4/definitions/__init__.py +4 -0
  100. mtppy/part4/definitions/command_codes.py +58 -0
  101. mtppy/part4/definitions/command_codes_test.py +44 -0
  102. mtppy/part4/definitions/state_codes.py +65 -0
  103. mtppy/part4/definitions/state_codes_test.py +44 -0
  104. mtppy/part4/process_values/__init__.py +4 -0
  105. mtppy/part4/process_values/base_functions/__init__.py +4 -0
  106. mtppy/part4/process_values/base_functions/vqc.py +48 -0
  107. mtppy/part4/process_values/input_elements/__init__.py +4 -0
  108. mtppy/part4/process_values/input_elements/ana_process_value_in.py +160 -0
  109. mtppy/part4/process_values/input_elements/bin_process_value_in.py +83 -0
  110. mtppy/part4/process_values/input_elements/dint_process_value_in.py +159 -0
  111. mtppy/part4/process_values/input_elements/input_element.py +40 -0
  112. mtppy/part4/process_values/input_elements/string_process_value_in.py +45 -0
  113. mtppy/part5/__init__.py +4 -0
  114. mtppy/part5/communication_object.py +99 -0
  115. mtppy/part5/communication_object_test.py +59 -0
  116. mtppy/part5/definitions/__init__.py +4 -0
  117. mtppy/part5/definitions/iec61131_data_types.py +31 -0
  118. mtppy/part5/definitions/iec61131_data_types_test.py +82 -0
  119. mtppy/part5/definitions/opcua_data_types.py +36 -0
  120. mtppy/part5/definitions/opcua_data_types_test.py +90 -0
  121. mtppy/part5/definitions/w3c_xml_data_types.py +177 -0
  122. mtppy/part5/definitions/w3c_xml_data_types_test.py +143 -0
  123. mtppy/part5/server_assembly_set/__init__.py +5 -0
  124. mtppy/part6/__init__.py +4 -0
  125. mtppy/part6/alarm_set/__init__.py +4 -0
  126. mtppy/part6/alarm_set/alarm.py +132 -0
  127. mtppy/part6/alarm_set/alarm_test.py +265 -0
  128. mtppy/pea/__init__.py +19 -0
  129. mtppy/pea/data_assembly_factory.py +124 -0
  130. mtppy/pea/data_assembly_factory_test.py +229 -0
  131. mtppy/pea/mtp_generator/__init__.py +4 -0
  132. mtppy/pea/mtp_generator/mtp_generator.py +1183 -0
  133. mtppy/pea/mtp_generator/mtp_generator_test.py +518 -0
  134. mtppy/pea/pea.py +139 -0
  135. mtppy/pea/pea_opcua_server/__init__.py +4 -0
  136. mtppy/pea/pea_opcua_server/marshalling.py +60 -0
  137. mtppy/pea/pea_opcua_server/opcua_options_provider/__init__.py +4 -0
  138. mtppy/pea/pea_opcua_server/opcua_options_provider/instances/basic_opcua_options_provider.py +50 -0
  139. mtppy/pea/pea_opcua_server/opcua_options_provider/opcua_options_provider.py +103 -0
  140. mtppy/pea/pea_opcua_server/pea_opcua_server.py +492 -0
  141. mtppy/pea/pea_opcua_server/subscription_list.py +55 -0
  142. mtppy/pea/pea_test.py +59 -0
  143. mtppy/pea/rmtp_pea.py +155 -0
  144. mtppy/pea/rmtp_pea_test.py +27 -0
  145. mtppy/pea/service_factory.py +178 -0
  146. mtppy/pea/service_factory_test.py +156 -0
  147. mtppy/pea/user_logic/__init__.py +4 -0
  148. mtppy/pea/user_logic/user_logic_pea.py +72 -0
  149. mtppy/pea/user_logic/user_logic_registry.py +106 -0
  150. mtppy/pea/user_logic/user_logic_registry_test.py +167 -0
  151. mtppy/pea/user_logic/user_logic_service.py +228 -0
  152. mtppy/pea/user_logic/user_logic_service_test.py +289 -0
  153. mtppy/utils/__init__.py +4 -0
  154. mtppy/utils/data_assembly_dict.py +61 -0
  155. mtppy/utils/data_assembly_dict_test.py +107 -0
  156. mtppy/utils/data_item.py +446 -0
  157. mtppy/utils/data_item_com_test.py +82 -0
  158. mtppy/utils/data_item_test.py +325 -0
  159. mtppy/utils/logging.py +23 -0
  160. mtppy/utils/logging_test.py +26 -0
  161. mtppy/utils/mtp_file_handling/__init__.py +4 -0
  162. mtppy/utils/mtp_file_handling/extract_manifest_from_mtp_file.py +22 -0
  163. mtppy/utils/mtp_file_handling/extract_mtp_file.py +22 -0
  164. mtppy/utils/mtp_file_handling/is_valid_mtp.py +22 -0
  165. mtppy/utils/mtp_file_handling/is_valid_path_for_manifest.py +15 -0
  166. mtppy/utils/mtp_file_handling/is_valid_path_to_manifest.py +17 -0
  167. mtppy/utils/mtp_file_handling/is_valid_path_to_mtp.py +17 -0
  168. mtppy/utils/mtp_file_handling/load_manifest_file.py +24 -0
  169. mtppy/utils/mtp_file_handling/mtp_reader/__init__.py +4 -0
  170. mtppy/utils/mtp_file_handling/mtp_reader/mtp_reader.py +103 -0
  171. mtppy/utils/mtp_file_handling/mtp_reader/mtp_reader_test.py +96 -0
  172. mtppy/utils/mtp_file_handling/mtp_reader/mtp_xml.py +226 -0
  173. mtppy/utils/mtp_file_handling/mtp_reader/mtp_xml_test.py +108 -0
  174. mtppy/utils/mtp_file_handling/mtp_writer/__init__.py +4 -0
  175. mtppy/utils/mtp_file_handling/mtp_writer/mtp_writer.py +77 -0
  176. mtppy/utils/mtp_file_handling/mtp_writer/ressources/get_content_types.py +10 -0
  177. mtppy/utils/mtp_file_handling/mtp_writer/ressources/get_rels.py +11 -0
  178. mtppy/utils/mtp_file_handling/mtp_writer/test_mtp_writer.py +25 -0
  179. mtppy/utils/mtp_file_handling/write_manifest_file.py +29 -0
  180. mtppy/utils/mtp_file_handling/write_manifest_file_test.py +22 -0
  181. mtppy/utils/mtp_file_handling/write_mtp_file.py +43 -0
  182. mtppy/utils/mtp_file_handling/write_mtp_file_test.py +22 -0
  183. mtppy/utils/testing.py +31 -0
  184. mtppy2_0-0.1.5.dist-info/METADATA +75 -0
  185. mtppy2_0-0.1.5.dist-info/RECORD +214 -0
  186. mtppy2_0-0.1.5.dist-info/WHEEL +5 -0
  187. mtppy2_0-0.1.5.dist-info/entry_points.txt +3 -0
  188. mtppy2_0-0.1.5.dist-info/licenses/LICENSE +21 -0
  189. mtppy2_0-0.1.5.dist-info/top_level.txt +4 -0
  190. tools/__init__.py +0 -0
  191. tools/create_bugreport.py +150 -0
  192. tools/inspect_mtp.py +110 -0
  193. utils/__init__.py +1 -0
  194. utils/file_handling/__init__.py +4 -0
  195. utils/file_handling/cleanup_folder.py +9 -0
  196. utils/file_handling/get_utc_foldername_with_ms.py +15 -0
  197. utils/file_handling/get_work_dir.py +9 -0
  198. utils/file_handling/is_valid_path_for_json.py +11 -0
  199. utils/file_handling/is_valid_path_to_json.py +17 -0
  200. utils/file_handling/load_json_file.py +18 -0
  201. utils/file_handling/read_file_from_file_archive.py +43 -0
  202. utils/file_handling/read_file_string.py +10 -0
  203. utils/file_handling/validate_json_data.py +28 -0
  204. utils/file_handling/write_json_file.py +23 -0
  205. utils/get_uuid.py +18 -0
  206. utils/get_uuid_test.py +17 -0
  207. utils/logging.py +100 -0
  208. utils/logging_test.py +67 -0
  209. utils/opcua/check_node.py +79 -0
  210. utils/resolve_unit/resolve_unit.py +11 -0
  211. utils/resolve_unit/unit_lib.py +2101 -0
  212. utils/resolve_unit/unit_lib_test.py +36 -0
  213. utils/type_conversion.py +236 -0
  214. utils/type_conversion_test.py +114 -0
@@ -0,0 +1,286 @@
1
+ """Implementation of MonBinVlv."""
2
+
3
+ __copyright__ = (
4
+ "Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
5
+ )
6
+ __license__ = "MIT"
7
+
8
+ from typing import ClassVar, SupportsFloat, SupportsIndex
9
+
10
+ from mtppy.utils.logging import logger
11
+ import threading
12
+ from time import sleep
13
+
14
+ from mtppy.utils.data_item import DataItem
15
+ from mtppy.part3.definitions.mtp_access import MTPAccess
16
+ from mtppy.part5.definitions.w3c_xml_data_types import W3cXmlDataTypes
17
+ from mtppy.part3.data_assembly_set.active_elements.valves.bin_vlv import BinVlv
18
+
19
+
20
+ class MonBinVlvValues:
21
+ def __init__(self, open_aut, open_op, close_aut, close_op, reset_aut, reset_op):
22
+ self.open_aut = open_aut
23
+ self.open_op = open_op
24
+ self.close_aut = close_aut
25
+ self.close_op = close_op
26
+ self.reset_aut = reset_aut
27
+ self.reset_op = reset_op
28
+ self.lock = threading.Lock()
29
+ self.stop_event_lock = threading.Event()
30
+
31
+
32
+ class MonBinVlv(BinVlv):
33
+ # DataItem constant definitions
34
+ MON_EN = "MonEn"
35
+ MON_SAFE_POS = "MonSafePos"
36
+ MON_STAT_ERR = "MonStatErr"
37
+ MON_DYN_ERR = "MonDynErr"
38
+ MON_STAT_TI = "MonStatTi"
39
+ MON_DYN_TI = "MonDynTi"
40
+
41
+ # Class-level data items dictionary
42
+ CLASS_DATA_ITEMS: ClassVar = {
43
+ MON_EN: DataItem(MON_EN, W3cXmlDataTypes.BOOLEAN, MTPAccess.ReadAccess, None),
44
+ MON_SAFE_POS: DataItem(
45
+ MON_SAFE_POS, W3cXmlDataTypes.BOOLEAN, MTPAccess.ReadAccess, None
46
+ ),
47
+ MON_STAT_ERR: DataItem(
48
+ MON_STAT_ERR, W3cXmlDataTypes.BOOLEAN, MTPAccess.ReadAccess, None
49
+ ),
50
+ MON_DYN_ERR: DataItem(
51
+ MON_DYN_ERR, W3cXmlDataTypes.BOOLEAN, MTPAccess.ReadAccess, None
52
+ ),
53
+ MON_STAT_TI: DataItem(
54
+ MON_STAT_TI, W3cXmlDataTypes.FLOAT, MTPAccess.ReadAccess, None
55
+ ),
56
+ MON_DYN_TI: DataItem(
57
+ MON_DYN_TI, W3cXmlDataTypes.FLOAT, MTPAccess.ReadAccess, None
58
+ ),
59
+ }
60
+
61
+ monitored_values: MonBinVlvValues
62
+ monitor_static_thread = threading.Thread
63
+ monitor_dynamic_thread = threading.Thread
64
+
65
+ def __init__(
66
+ self,
67
+ tag_name,
68
+ tag_description="",
69
+ open_fbk_calc=True,
70
+ close_fbk_calc=True,
71
+ safe_pos=0,
72
+ safe_pos_en=False,
73
+ perm_en=False,
74
+ intl_en=False,
75
+ prot_en=False,
76
+ mon_en=True,
77
+ mon_safe_pos=True,
78
+ mon_stat_ti=1,
79
+ mon_dyn_ti=1,
80
+ ):
81
+ super().__init__(
82
+ tag_name,
83
+ tag_description,
84
+ open_fbk_calc,
85
+ close_fbk_calc,
86
+ safe_pos,
87
+ safe_pos_en,
88
+ perm_en,
89
+ intl_en,
90
+ prot_en,
91
+ )
92
+ self.mtpObjectType = (
93
+ "MTPDataObjectSUCLib/DataAssembly/ActiveElement/BinVlv/MonBinVlv"
94
+ )
95
+
96
+ # Initialize data items with dynamic values (using class-defined data items)
97
+ self[MonBinVlv.MON_EN] = mon_en
98
+ self[MonBinVlv.MON_SAFE_POS] = mon_safe_pos
99
+ self[MonBinVlv.MON_STAT_ERR] = False
100
+ self[MonBinVlv.MON_DYN_ERR] = False
101
+ self[MonBinVlv.MON_STAT_TI] = mon_stat_ti
102
+ self[MonBinVlv.MON_DYN_TI] = mon_dyn_ti
103
+
104
+ self.monitored_values = MonBinVlvValues(
105
+ self[BinVlv.OPEN_AUT],
106
+ self[BinVlv.OPEN_OP],
107
+ self[BinVlv.CLOSE_AUT],
108
+ self[BinVlv.CLOSE_OP],
109
+ self[BinVlv.RESET_AUT],
110
+ self[BinVlv.RESET_OP],
111
+ )
112
+ self.monitor_static_thread = None
113
+ self.monitor_dynamic_thread = None
114
+
115
+ def compare_states_control_signals(
116
+ self,
117
+ monitor_time: SupportsFloat | SupportsIndex,
118
+ ) -> tuple[list[bool], list[bool]]:
119
+ """
120
+ compare states of valve and control signals
121
+
122
+ Arguments:
123
+ monitor_time: MonStatTi or MonDynTi times
124
+
125
+ Returns:
126
+ results of states and control signals comparisons (a list including True or False)
127
+ """
128
+ self.monitored_values.lock.acquire()
129
+ open_fbk1 = self[BinVlv.OPEN_FBK]
130
+ close_fbk1 = self[BinVlv.CLOSE_FBK]
131
+ open_op1 = self.monitored_values.open_op
132
+ open_aut1 = self.monitored_values.open_aut
133
+ close_op1 = self.monitored_values.close_op
134
+ close_aut1 = self.monitored_values.close_aut
135
+ reset_op1 = self.monitored_values.reset_op
136
+ reset_aut1 = self.monitored_values.reset_aut
137
+ self.monitored_values.lock.release()
138
+
139
+ sleep(monitor_time)
140
+
141
+ self.monitored_values.lock.acquire()
142
+ open_fbk2 = self[BinVlv.OPEN_FBK]
143
+ close_fbk2 = self[BinVlv.CLOSE_FBK]
144
+ open_op2 = self.monitored_values.open_op
145
+ open_aut2 = self.monitored_values.open_aut
146
+ close_op2 = self.monitored_values.close_op
147
+ close_aut2 = self.monitored_values.close_aut
148
+ reset_op2 = self.monitored_values.reset_op
149
+ reset_aut2 = self.monitored_values.reset_aut
150
+ self.monitored_values.lock.release()
151
+
152
+ control_signals_comparison = [
153
+ open_op1 == open_op2,
154
+ open_aut1 == open_aut2,
155
+ close_op1 == close_op2,
156
+ close_aut1 == close_aut2,
157
+ reset_op1 == reset_op2,
158
+ reset_aut1 == reset_aut2,
159
+ ]
160
+ state_comparison = [open_fbk1 == open_fbk2, close_fbk1 == close_fbk2]
161
+ return state_comparison, control_signals_comparison
162
+
163
+ def monitor_static_error(self):
164
+ """
165
+ monitor static error
166
+ """
167
+ while True:
168
+ if self.monitored_values.stop_event_lock.is_set():
169
+ logger.debug("static monitoring stopped")
170
+ break
171
+ states, control_signals = self.compare_states_control_signals(
172
+ self[MonBinVlv.MON_STAT_TI]
173
+ )
174
+
175
+ if not all(states): # if the states of valve changed
176
+ if all(control_signals): # but the control signals did not change
177
+ self[MonBinVlv.MON_STAT_ERR] = True
178
+ logger.debug("Static error set to True")
179
+ self._handle_monitored_error()
180
+
181
+ def monitor_dynamic_error(self):
182
+ """
183
+ monitor dynamic error
184
+ """
185
+ while True:
186
+ if self.monitored_values.stop_event_lock.is_set():
187
+ logger.debug("dynamic monitoring stopped")
188
+ break
189
+ states, control_signals = self.compare_states_control_signals(
190
+ self[MonBinVlv.MON_DYN_TI]
191
+ )
192
+
193
+ if all(states): # if the states of valve did not change
194
+ if not all(control_signals): # but a control command is executed
195
+ self[MonBinVlv.MON_DYN_ERR] = True
196
+ logger.debug("Dynamic error set to True")
197
+ self._handle_monitored_error()
198
+
199
+ def _handle_monitored_error(self):
200
+ if self[MonBinVlv.MON_SAFE_POS]:
201
+ logger.debug("set valve to safety position")
202
+ if self[BinVlv.SAFE_POS_EN]:
203
+ self._go_safe_pos()
204
+ else:
205
+ logger.debug("Device has no safe position")
206
+ self[BinVlv.SAFE_POS_ACT] = True
207
+
208
+ def start_monitor(self):
209
+ """
210
+ start static and dynamic error monitoring thread
211
+ """
212
+ if self["MonEn"]:
213
+ self.monitor_static_thread = threading.Thread(
214
+ target=self.monitor_static_error, daemon=True
215
+ )
216
+ self.monitor_static_thread.start()
217
+ logger.debug("static monitoring start")
218
+ self.monitor_dynamic_thread = threading.Thread(
219
+ target=self.monitor_dynamic_error, daemon=True
220
+ )
221
+ self.monitor_dynamic_thread.start()
222
+ logger.debug("dynamic monitoring start")
223
+
224
+ def set_open_aut(self, value: bool):
225
+ logger.debug(f"OpenAut set to {value}")
226
+ if self.op_mode["StateAutAct"]:
227
+ if value and self._run_allowed():
228
+ if self["MonEn"]:
229
+ self.monitored_values.lock.acquire()
230
+ self.monitored_values.open_aut = value
231
+ self.monitored_values.lock.release()
232
+ self._run_open_vlv()
233
+
234
+ def set_close_aut(self, value: bool):
235
+ if self.op_mode["StateAutAct"]:
236
+ if value and self._run_allowed():
237
+ if self["MonEn"]:
238
+ self.monitored_values.lock.acquire()
239
+ self.monitored_values.close_aut = value
240
+ self.monitored_values.lock.release()
241
+ logger.debug(f"CloseAut set to {value}")
242
+ self._run_close_vlv()
243
+
244
+ def set_reset_aut(self, value: bool):
245
+ logger.debug(f"ResetAut set to {value}")
246
+ if self.op_mode["StateAutAct"] and value:
247
+ if self["MonEn"]:
248
+ self.monitored_values.lock.acquire()
249
+ self.monitored_values.reset_aut = value
250
+ self.monitored_values.lock.release()
251
+ self._reset_vlv()
252
+
253
+ def set_open_op(self, value: bool):
254
+ logger.debug(f"OpenOp set to {value}")
255
+ if self.op_mode["StateOpAct"]:
256
+ if value and self._run_allowed():
257
+ if self["MonEn"]:
258
+ self.monitored_values.lock.acquire()
259
+ self.monitored_values.open_op = value
260
+ self.monitored_values.lock.release()
261
+ self._run_open_vlv()
262
+ self["OpenOp"] = False
263
+
264
+ def set_close_op(self, value: bool):
265
+ logger.debug(f"CloseOp set to {value}")
266
+ if self.op_mode["StateOpAct"]:
267
+ if value and self._run_allowed():
268
+ if self["MonEn"]:
269
+ self.monitored_values.lock.acquire()
270
+ self.monitored_values.close_op = value
271
+ self.monitored_values.lock.release()
272
+ self._run_close_vlv()
273
+ self["CloseOp"] = False
274
+
275
+ def set_reset_op(self, value: bool):
276
+ logger.debug(f"ResetOp set to {value}")
277
+ if self.op_mode["StateOpAct"] and value:
278
+ if self["MonEn"]:
279
+ self.monitored_values.lock.acquire()
280
+ self.monitored_values.reset_op = value
281
+ self.monitored_values.lock.release()
282
+ self._reset_vlv()
283
+ self["ResetOp"] = False
284
+
285
+ def set_stop_monitor(self):
286
+ self.monitored_values.stop_event_lock.set()
@@ -0,0 +1,224 @@
1
+ """Test Suite for MonBinValve."""
2
+
3
+ __copyright__ = (
4
+ "Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
5
+ )
6
+ __license__ = "MIT"
7
+ import pytest
8
+ import time
9
+
10
+ from mtppy.part3.data_assembly_set.active_elements.valves.bin_vlv import BinVlv
11
+ from mtppy.part3.data_assembly_set.active_elements.valves.mon_bin_vlv import MonBinVlv
12
+ from mtppy.utils.testing import data_object_set_modes
13
+
14
+
15
+ def init_mon_bin_vlv(
16
+ init_op_mode="off",
17
+ init_src_mode="int",
18
+ open_fbk_calc=True,
19
+ close_fbk_calc=True,
20
+ safe_pos=1,
21
+ safe_pos_en=True,
22
+ perm_en=False,
23
+ intl_en=False,
24
+ prot_en=False,
25
+ mon_en=True,
26
+ mon_safe_pos=1,
27
+ mon_stat_ti=0.5,
28
+ mon_dyn_ti=0.4,
29
+ ):
30
+ instance = MonBinVlv(
31
+ "tag",
32
+ tag_description="",
33
+ open_fbk_calc=open_fbk_calc,
34
+ close_fbk_calc=close_fbk_calc,
35
+ safe_pos=safe_pos,
36
+ safe_pos_en=safe_pos_en,
37
+ perm_en=perm_en,
38
+ intl_en=intl_en,
39
+ prot_en=prot_en,
40
+ mon_en=mon_en,
41
+ mon_safe_pos=mon_safe_pos,
42
+ mon_stat_ti=mon_stat_ti,
43
+ mon_dyn_ti=mon_dyn_ti,
44
+ )
45
+ data_object_set_modes(instance, init_op_mode, init_src_mode)
46
+ return instance
47
+
48
+
49
+ test_scenario_no_control_signals = [
50
+ ("off", "int", "set_open_op"),
51
+ ("off", "man", "set_open_op"),
52
+ ("aut", "int", "set_open_op"),
53
+ ("aut", "man", "set_open_op"),
54
+ ("off", "int", "set_open_aut"),
55
+ ("off", "man", "set_open_aut"),
56
+ ("op", "int", "set_open_aut"),
57
+ ("op", "man", "set_open_aut"),
58
+ ("off", "int", "set_close_op"),
59
+ ("off", "man", "set_close_op"),
60
+ ("aut", "int", "set_close_op"),
61
+ ("aut", "man", "set_close_op"),
62
+ ("off", "int", "set_close_aut"),
63
+ ("off", "man", "set_close_aut"),
64
+ ("op", "int", "set_close_aut"),
65
+ ("op", "man", "set_close_aut"),
66
+ ]
67
+
68
+
69
+ @pytest.mark.parametrize(
70
+ "op_mode, src_mode, set_command", test_scenario_no_control_signals
71
+ )
72
+ def test_static_error(op_mode, src_mode, set_command):
73
+ for command in [True, False]:
74
+ mon_bin_vlv = init_mon_bin_vlv(init_op_mode=op_mode, init_src_mode=src_mode)
75
+ try:
76
+ mon_bin_vlv.start_monitor()
77
+ eval(f"mon_bin_vlv.{set_command}({command})")
78
+ print(f"Scenario: mode {op_mode} {src_mode}, {set_command}")
79
+
80
+ # mimic an error situation: OpenFbk changes to True without any change in control signal
81
+ mon_bin_vlv[BinVlv.OPEN_FBK] = True
82
+ time.sleep(mon_bin_vlv[MonBinVlv.MON_STAT_TI] * 2)
83
+
84
+ assert mon_bin_vlv[MonBinVlv.MON_STAT_ERR] == True
85
+ assert mon_bin_vlv[MonBinVlv.MON_DYN_ERR] == False
86
+ assert mon_bin_vlv[BinVlv.SAFE_POS_ACT] == True
87
+ assert (
88
+ mon_bin_vlv[BinVlv.OPEN_FBK] == True
89
+ ) # safe position of valve is open
90
+
91
+ time.sleep(mon_bin_vlv[MonBinVlv.MON_STAT_TI])
92
+
93
+ # after valve is set to safe position (open), the OpenFbk becomes to False --> another static error
94
+ mon_bin_vlv[BinVlv.OPEN_FBK] = False
95
+ time.sleep(mon_bin_vlv[MonBinVlv.MON_STAT_TI] * 1.5)
96
+
97
+ assert mon_bin_vlv[MonBinVlv.MON_STAT_ERR] == True
98
+ assert mon_bin_vlv[MonBinVlv.MON_DYN_ERR] == False
99
+ assert mon_bin_vlv[BinVlv.SAFE_POS_ACT] == True
100
+ assert (
101
+ mon_bin_vlv[BinVlv.OPEN_FBK] == True
102
+ ) # safe position of valve is open
103
+ finally:
104
+ mon_bin_vlv.set_stop_monitor()
105
+ mon_bin_vlv.monitor_static_thread.join()
106
+ mon_bin_vlv.monitor_dynamic_thread.join()
107
+
108
+
109
+ @pytest.mark.parametrize(
110
+ "op_mode, src_mode, set_command", test_scenario_no_control_signals
111
+ )
112
+ def test_static_error_within_monitor_time(op_mode, src_mode, set_command):
113
+ """If the required state is set on the interface before the time has expired (wait tme < 0.5s in this test),
114
+ there is no error"""
115
+
116
+ for command in [True, False]:
117
+ mon_bin_vlv = init_mon_bin_vlv(init_op_mode=op_mode, init_src_mode=src_mode)
118
+ try:
119
+ mon_bin_vlv.start_monitor()
120
+ eval(f"mon_bin_vlv.{set_command}({command})")
121
+ print(f"Scenario: mode {op_mode} {src_mode}, {set_command}")
122
+
123
+ # mimic an error situation: OpenFbk changes to True without any change in control signal
124
+ mon_bin_vlv[BinVlv.OPEN_FBK] = True
125
+ time.sleep(
126
+ mon_bin_vlv[MonBinVlv.MON_STAT_TI] * 0.5
127
+ ) # monitor time has not expired (0.5s)
128
+
129
+ assert mon_bin_vlv[MonBinVlv.MON_STAT_ERR] == False
130
+ assert mon_bin_vlv[MonBinVlv.MON_DYN_ERR] == False
131
+ assert mon_bin_vlv[BinVlv.SAFE_POS_ACT] == False
132
+ finally:
133
+ mon_bin_vlv.set_stop_monitor()
134
+ mon_bin_vlv.monitor_static_thread.join()
135
+ mon_bin_vlv.monitor_dynamic_thread.join()
136
+
137
+
138
+ test_scenario_control_signals = [
139
+ ("op", "int", "set_open_op", "set_close_op"),
140
+ ("op", "man", "set_open_op", "set_close_op"),
141
+ ("aut", "int", "set_open_aut", "set_close_aut"),
142
+ ("aut", "man", "set_open_aut", "set_close_aut"),
143
+ ]
144
+
145
+
146
+ @pytest.mark.parametrize(
147
+ "op_mode, src_mode, open_command, close_command", test_scenario_control_signals
148
+ )
149
+ def test_dynamic_error_open_close(op_mode, src_mode, open_command, close_command):
150
+ mon_bin_vlv = init_mon_bin_vlv(init_op_mode=op_mode, init_src_mode=src_mode)
151
+ try:
152
+ mon_bin_vlv.start_monitor()
153
+ eval(f"mon_bin_vlv.{open_command}(True)")
154
+ print(f"Scenario: mode {op_mode} {src_mode}, {open_command}")
155
+
156
+ # mimic an error situation: OpenFbk does not change to True after control signal has changed to open
157
+ mon_bin_vlv[BinVlv.OPEN_FBK] = False
158
+ time.sleep(mon_bin_vlv[MonBinVlv.MON_DYN_TI] * 2)
159
+
160
+ assert mon_bin_vlv[MonBinVlv.MON_DYN_ERR] == True
161
+ assert mon_bin_vlv[MonBinVlv.MON_STAT_ERR] == False
162
+ assert mon_bin_vlv[BinVlv.SAFE_POS_ACT] == True
163
+ assert mon_bin_vlv[BinVlv.OPEN_FBK] == True # safe position of valve is open
164
+
165
+ eval(f"mon_bin_vlv.{close_command}(True)")
166
+ print(f"Scenario: mode {op_mode} {src_mode}, {close_command}")
167
+
168
+ # mimic an error situation: OpenFbk does not change to False after control signal has changed to close
169
+ mon_bin_vlv[BinVlv.OPEN_FBK] = True
170
+ time.sleep(mon_bin_vlv[MonBinVlv.MON_DYN_TI] * 2)
171
+
172
+ assert mon_bin_vlv["MonDynErr"] == True
173
+ assert mon_bin_vlv["MonStatErr"] == False
174
+ assert mon_bin_vlv["SafePosAct"] == True
175
+ assert mon_bin_vlv["OpenFbk"] == True # safe position of valve is open
176
+ finally:
177
+ mon_bin_vlv.set_stop_monitor()
178
+ mon_bin_vlv.monitor_static_thread.join()
179
+ mon_bin_vlv.monitor_dynamic_thread.join()
180
+
181
+
182
+ test_scenario_control_signals_reset = [
183
+ ("op", "int", "set_reset_op"),
184
+ ("op", "man", "set_reset_op"),
185
+ ("aut", "int", "set_reset_aut"),
186
+ ("aut", "man", "set_reset_aut"),
187
+ ]
188
+
189
+
190
+ @pytest.mark.parametrize(
191
+ "op_mode, src_mode, set_command", test_scenario_control_signals_reset
192
+ )
193
+ def test_dynamic_error_reset(op_mode, src_mode, set_command):
194
+ mon_bin_vlv = init_mon_bin_vlv(init_op_mode=op_mode, init_src_mode=src_mode)
195
+ try:
196
+ mon_bin_vlv.start_monitor()
197
+ # set valve to open
198
+ if op_mode == "op":
199
+ mon_bin_vlv.set_open_op(True)
200
+ elif op_mode == "aut":
201
+ mon_bin_vlv.set_open_aut(True)
202
+ time.sleep(mon_bin_vlv[MonBinVlv.MON_DYN_TI] * 1)
203
+
204
+ # reset valve
205
+ eval(f"mon_bin_vlv.{set_command}(True)")
206
+ print(f"Scenario: mode {op_mode} {src_mode}, {set_command}")
207
+
208
+ # mimic an error situation: OpenFbk does not change to False after reset changes to True
209
+ mon_bin_vlv[BinVlv.OPEN_FBK] = True
210
+ time.sleep(mon_bin_vlv[MonBinVlv.MON_DYN_TI] * 3)
211
+
212
+ assert mon_bin_vlv[MonBinVlv.MON_DYN_ERR] == True
213
+ assert mon_bin_vlv[MonBinVlv.MON_STAT_ERR] == False
214
+ assert mon_bin_vlv[BinVlv.SAFE_POS_ACT] == True
215
+ assert mon_bin_vlv[BinVlv.OPEN_FBK] == True # safe position of valve is open
216
+ finally:
217
+ mon_bin_vlv.set_stop_monitor()
218
+ mon_bin_vlv.monitor_static_thread.join()
219
+ mon_bin_vlv.monitor_dynamic_thread.join()
220
+
221
+
222
+ if __name__ == "__main__":
223
+ # This allows running the tests directly if needed
224
+ pytest.main([__file__, "-v"])
@@ -0,0 +1,4 @@
1
+ __copyright__ = (
2
+ "Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
3
+ )
4
+ __license__ = "MIT"