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,295 @@
1
+ """Implementation of MonBinDrv."""
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
9
+
10
+ from mtppy.part3.data_assembly_set.active_elements.drives.bin_drv import BinDrv
11
+ from mtppy.utils.logging import logger
12
+ import threading
13
+ from time import sleep
14
+
15
+ from mtppy.utils.data_item import DataItem
16
+ from mtppy.part3.definitions.mtp_access import MTPAccess
17
+ from mtppy.part5.definitions.w3c_xml_data_types import W3cXmlDataTypes
18
+ from mtppy.part3.data_assembly_set.base_functions.operation_mode import (
19
+ OperationMode as OM,
20
+ )
21
+
22
+
23
+ class MonBinDrvValues:
24
+ def __init__(
25
+ self, fwd_aut, fwd_op, rev_aut, rev_op, stop_aut, stop_op, reset_aut, reset_op
26
+ ):
27
+ self.fwd_aut = fwd_aut
28
+ self.fwd_op = fwd_op
29
+ self.rev_aut = rev_aut
30
+ self.rev_op = rev_op
31
+ self.stop_aut = stop_aut
32
+ self.stop_op = stop_op
33
+ self.reset_aut = reset_aut
34
+ self.reset_op = reset_op
35
+ self.lock = threading.Lock()
36
+ self.stop_event_lock = threading.Event()
37
+
38
+
39
+ class MonBinDrv(BinDrv):
40
+ # DataItem constant definitions
41
+ MON_EN = "MonEn"
42
+ MON_SAFE_POS = "MonSafePos"
43
+ MON_STAT_ERR = "MonStatErr"
44
+ MON_DYN_ERR = "MonDynErr"
45
+ MON_STAT_TI = "MonStatTi"
46
+ MON_DYN_TI = "MonDynTi"
47
+
48
+ # Class-level data items dictionary
49
+ CLASS_DATA_ITEMS: ClassVar = {
50
+ MON_EN: DataItem(MON_EN, W3cXmlDataTypes.BOOLEAN, MTPAccess.ReadAccess, None),
51
+ MON_SAFE_POS: DataItem(
52
+ MON_SAFE_POS, W3cXmlDataTypes.BOOLEAN, MTPAccess.ReadAccess, None
53
+ ),
54
+ MON_STAT_ERR: DataItem(
55
+ MON_STAT_ERR, W3cXmlDataTypes.BOOLEAN, MTPAccess.ReadAccess, None
56
+ ),
57
+ MON_DYN_ERR: DataItem(
58
+ MON_DYN_ERR, W3cXmlDataTypes.BOOLEAN, MTPAccess.ReadAccess, None
59
+ ),
60
+ MON_STAT_TI: DataItem(
61
+ MON_STAT_TI, W3cXmlDataTypes.FLOAT, MTPAccess.ReadAccess, None
62
+ ),
63
+ MON_DYN_TI: DataItem(
64
+ MON_DYN_TI, W3cXmlDataTypes.FLOAT, MTPAccess.ReadAccess, None
65
+ ),
66
+ }
67
+
68
+ monitored_values: MonBinDrvValues
69
+ monitor_static_thread: threading.Thread
70
+ monitor_dynamic_thread: threading.Thread
71
+
72
+ def __init__(
73
+ self,
74
+ tag_name: str,
75
+ tag_description: str = "",
76
+ rev_fbk_calc: bool = True,
77
+ fwd_fbk_calc: bool = True,
78
+ safe_pos: int = 0,
79
+ fwd_en: bool = True,
80
+ rev_en: bool = False,
81
+ perm_en: bool = False,
82
+ intl_en: bool = False,
83
+ prot_en: bool = False,
84
+ mon_en: bool = True,
85
+ mon_safe_pos: bool = True,
86
+ mon_stat_ti: float = 1,
87
+ mon_dyn_ti: float = 1,
88
+ ) -> None:
89
+ super().__init__(
90
+ tag_name,
91
+ tag_description,
92
+ rev_fbk_calc,
93
+ fwd_fbk_calc,
94
+ safe_pos,
95
+ fwd_en,
96
+ rev_en,
97
+ perm_en,
98
+ intl_en,
99
+ prot_en,
100
+ )
101
+ self.mtpObjectType = (
102
+ "MTPDataObjectSUCLib/DataAssembly/ActiveElement/BinDrv/MonBinDrv"
103
+ )
104
+
105
+ # Initialize data items with dynamic values (using class-defined data items)
106
+ self[MonBinDrv.MON_EN] = mon_en
107
+ self[MonBinDrv.MON_SAFE_POS] = mon_safe_pos
108
+ self[MonBinDrv.MON_STAT_ERR] = False
109
+ self[MonBinDrv.MON_DYN_ERR] = False
110
+ self[MonBinDrv.MON_STAT_TI] = mon_stat_ti
111
+ self[MonBinDrv.MON_DYN_TI] = mon_dyn_ti
112
+
113
+ self.monitored_values = MonBinDrvValues(
114
+ self[BinDrv.FWD_AUT],
115
+ self[BinDrv.FWD_OP],
116
+ self[BinDrv.REV_AUT],
117
+ self[BinDrv.REV_OP],
118
+ self[BinDrv.STOP_AUT],
119
+ self[BinDrv.STOP_OP],
120
+ self[BinDrv.RESET_AUT],
121
+ self[BinDrv.RESET_OP],
122
+ )
123
+ self.monitor_static_thread = None
124
+ self.monitor_dynamic_thread = None
125
+
126
+ def compare_states_control_signals(self, monitor_time):
127
+
128
+ self.monitored_values.lock.acquire()
129
+ fwd_fbk1 = self[BinDrv.FWD_FBK]
130
+ rev_fbk1 = self[BinDrv.REV_FBK]
131
+ fwd_op1 = self.monitored_values.fwd_op
132
+ fwd_aut1 = self.monitored_values.fwd_aut
133
+ rev_op1 = self.monitored_values.rev_op
134
+ rev_aut1 = self.monitored_values.rev_aut
135
+ stop_op1 = self.monitored_values.stop_op
136
+ stop_aut1 = self.monitored_values.stop_aut
137
+ reset_op1 = self.monitored_values.reset_op
138
+ reset_aut1 = self.monitored_values.reset_aut
139
+ self.monitored_values.lock.release()
140
+
141
+ sleep(monitor_time)
142
+
143
+ self.monitored_values.lock.acquire()
144
+ fwd_fbk2 = self[BinDrv.FWD_FBK]
145
+ rev_fbk2 = self[BinDrv.REV_FBK]
146
+ fwd_op2 = self.monitored_values.fwd_op
147
+ fwd_aut2 = self.monitored_values.fwd_aut
148
+ rev_op2 = self.monitored_values.rev_op
149
+ rev_aut2 = self.monitored_values.rev_aut
150
+ stop_op2 = self.monitored_values.stop_op
151
+ stop_aut2 = self.monitored_values.stop_aut
152
+ reset_op2 = self.monitored_values.reset_op
153
+ reset_aut2 = self.monitored_values.reset_aut
154
+ self.monitored_values.lock.release()
155
+
156
+ control_signals_comparison = [
157
+ fwd_op1 == fwd_op2,
158
+ fwd_aut1 == fwd_aut2,
159
+ rev_op1 == rev_op2,
160
+ rev_aut1 == rev_aut2,
161
+ stop_op1 == stop_op2,
162
+ stop_aut1 == stop_aut2,
163
+ reset_op1 == reset_op2,
164
+ reset_aut1 == reset_aut2,
165
+ ]
166
+ state_comparison = [fwd_fbk1 == fwd_fbk2, rev_fbk1 == rev_fbk2]
167
+ return state_comparison, control_signals_comparison
168
+
169
+ def monitor_static_error(self):
170
+ while not (self.monitored_values.stop_event_lock.is_set()):
171
+ states, control_signals = self.compare_states_control_signals(
172
+ self[MonBinDrv.MON_STAT_TI]
173
+ )
174
+
175
+ if not (all(states)) and all(control_signals):
176
+ self[MonBinDrv.MON_STAT_ERR] = True
177
+ logger.debug("Static error set to True")
178
+ self._handle_monitored_error()
179
+
180
+ logger.debug("static monitoring stopped")
181
+
182
+ def monitor_dynamic_error(self):
183
+ while not (self.monitored_values.stop_event_lock.is_set()):
184
+ states, control_signals = self.compare_states_control_signals(
185
+ self[MonBinDrv.MON_DYN_TI]
186
+ )
187
+
188
+ if all(states) and not (all(control_signals)):
189
+ self[MonBinDrv.MON_DYN_ERR] = True
190
+ logger.debug("Dynamic error set to True")
191
+ self._handle_monitored_error()
192
+
193
+ logger.debug("dynamic monitoring stopped")
194
+
195
+ def _handle_monitored_error(self):
196
+ logger.debug("set valve to safety position")
197
+ self._go_safe_pos()
198
+ self[BinDrv.SAFE_POS_ACT] = True
199
+
200
+ def start_monitor(self):
201
+ if self[MonBinDrv.MON_EN]:
202
+ self.monitor_static_thread = threading.Thread(
203
+ target=self.monitor_static_error, daemon=True
204
+ )
205
+ self.monitor_static_thread.start()
206
+ logger.debug("static monitoring start")
207
+ self.monitor_dynamic_thread = threading.Thread(
208
+ target=self.monitor_dynamic_error, daemon=True
209
+ )
210
+ self.monitor_dynamic_thread.start()
211
+ logger.debug("dynamic monitoring start")
212
+
213
+ def set_fwd_op(self, value: bool):
214
+ logger.debug(f"FwdOp set to {value}")
215
+ if self.op_mode[OM.STATE_OP_ACT] and self[BinDrv.FWD_EN]:
216
+ if value and self._run_allowed():
217
+ self._run_fwd_drv()
218
+ if self[MonBinDrv.MON_EN]:
219
+ self.monitored_values.lock.acquire()
220
+ self.monitored_values.fwd_op = value
221
+ self.monitored_values.lock.release()
222
+ self[BinDrv.FWD_OP] = False
223
+
224
+ def set_rev_op(self, value: bool):
225
+ logger.debug(f"RevOp set to {value}")
226
+ if self.op_mode[OM.STATE_OP_ACT] and self[BinDrv.REV_EN]:
227
+ if value and self._run_allowed():
228
+
229
+ if self[MonBinDrv.MON_EN]:
230
+ self.monitored_values.lock.acquire()
231
+ self.monitored_values.rev_op = value
232
+ self.monitored_values.lock.release()
233
+ self._run_rev_drv()
234
+ self[BinDrv.REV_OP] = False
235
+
236
+ def set_stop_op(self, value: bool):
237
+ logger.debug(f"StopOp set to {value}")
238
+ if self.op_mode[OM.STATE_OP_ACT] and value:
239
+ if self[MonBinDrv.MON_EN]:
240
+ self.monitored_values.lock.acquire()
241
+ self.monitored_values.stop_op = value
242
+ self.monitored_values.lock.release()
243
+ self._stop_drv()
244
+ self[BinDrv.STOP_OP] = False
245
+
246
+ def set_reset_op(self, value: bool):
247
+ logger.debug(f"ResetOp set to {value}")
248
+ if self.op_mode[OM.STATE_OP_ACT] and value:
249
+ if self[MonBinDrv.MON_EN]:
250
+ self.monitored_values.lock.acquire()
251
+ self.monitored_values.reset_op = value
252
+ self.monitored_values.lock.release()
253
+ self._reset_drv()
254
+ self[BinDrv.RESET_OP] = False
255
+
256
+ def set_fwd_aut(self, value: bool):
257
+ logger.debug(f"FwdAut set to {value}")
258
+ if self.op_mode[OM.STATE_AUT_ACT] and self[BinDrv.FWD_EN]:
259
+ if value and self._run_allowed():
260
+ if self[MonBinDrv.MON_EN]:
261
+ self.monitored_values.lock.acquire()
262
+ self.monitored_values.fwd_aut = value
263
+ self.monitored_values.lock.release()
264
+ self._run_fwd_drv()
265
+
266
+ def set_rev_aut(self, value: bool):
267
+ logger.debug(f"RevAut set to {value}")
268
+ if self.op_mode[OM.STATE_AUT_ACT] and self[BinDrv.REV_EN]:
269
+ if value and self._run_allowed():
270
+ if self[MonBinDrv.MON_EN]:
271
+ self.monitored_values.lock.acquire()
272
+ self.monitored_values.rev_aut = value
273
+ self.monitored_values.lock.release()
274
+ self._run_rev_drv()
275
+
276
+ def set_stop_aut(self, value: bool):
277
+ logger.debug(f"StopAut set to {value}")
278
+ if self.op_mode[OM.STATE_AUT_ACT] and value:
279
+ if self[MonBinDrv.MON_EN]:
280
+ self.monitored_values.lock.acquire()
281
+ self.monitored_values.stop_aut = value
282
+ self.monitored_values.lock.release()
283
+ self._stop_drv()
284
+
285
+ def set_reset_aut(self, value: bool):
286
+ logger.debug(f"ResetAut set to {value}")
287
+ if self.op_mode[OM.STATE_AUT_ACT] and value:
288
+ if self[MonBinDrv.MON_EN]:
289
+ self.monitored_values.lock.acquire()
290
+ self.monitored_values.reset_aut = value
291
+ self.monitored_values.lock.release()
292
+ self._reset_drv()
293
+
294
+ def set_stop_monitor(self):
295
+ self.monitored_values.stop_event_lock.set()
@@ -0,0 +1,265 @@
1
+ """Test Suite for MonBinDrv."""
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.utils.testing import data_object_set_modes
11
+ from mtppy.part3.data_assembly_set.active_elements.drives.mon_bin_drv import MonBinDrv
12
+
13
+
14
+ def init_mon_bin_drv(
15
+ init_op_mode="off",
16
+ init_src_mode="int",
17
+ rev_fbk_calc=True,
18
+ fwd_fbk_calc=True,
19
+ safe_pos=1,
20
+ fwd_en=True,
21
+ rev_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.5,
29
+ ) -> MonBinDrv:
30
+ instance = MonBinDrv(
31
+ "tag",
32
+ tag_description="",
33
+ rev_fbk_calc=rev_fbk_calc,
34
+ fwd_fbk_calc=fwd_fbk_calc,
35
+ safe_pos=safe_pos,
36
+ fwd_en=fwd_en,
37
+ rev_en=rev_en,
38
+ perm_en=perm_en,
39
+ intl_en=intl_en,
40
+ prot_en=prot_en,
41
+ mon_en=mon_en,
42
+ mon_safe_pos=mon_safe_pos,
43
+ mon_stat_ti=mon_stat_ti,
44
+ mon_dyn_ti=mon_dyn_ti,
45
+ )
46
+ data_object_set_modes(instance, init_op_mode, init_src_mode)
47
+ return instance
48
+
49
+
50
+ test_scenario_no_control_signals = [
51
+ ("off", "int", "set_fwd_op"),
52
+ ("off", "man", "set_fwd_op"),
53
+ ("aut", "int", "set_fwd_op"),
54
+ ("aut", "man", "set_fwd_op"),
55
+ ("off", "int", "set_fwd_aut"),
56
+ ("off", "man", "set_fwd_aut"),
57
+ ("op", "int", "set_fwd_aut"),
58
+ ("op", "man", "set_fwd_aut"),
59
+ ("off", "int", "set_rev_op"),
60
+ ("off", "man", "set_rev_op"),
61
+ ("aut", "int", "set_rev_op"),
62
+ ("aut", "man", "set_rev_op"),
63
+ ("off", "int", "set_rev_aut"),
64
+ ("off", "man", "set_rev_aut"),
65
+ ("op", "int", "set_rev_aut"),
66
+ ("op", "man", "set_rev_aut"),
67
+ ]
68
+
69
+
70
+ @pytest.mark.parametrize(
71
+ "op_mode, src_mode, set_command", test_scenario_no_control_signals
72
+ )
73
+ def test_static_error(op_mode, src_mode, set_command):
74
+ for command in [True, False]:
75
+ mon_bin_drv = init_mon_bin_drv(init_op_mode=op_mode, init_src_mode=src_mode)
76
+
77
+ mon_bin_drv.start_monitor()
78
+ try:
79
+ eval(f"mon_bin_drv.{set_command}({command})")
80
+ print(f"Scenario: mode {op_mode} {src_mode}, {set_command}")
81
+
82
+ mon_bin_drv["FwdFbk"] = True # FwdFbk becomes True w/o control signals
83
+ time.sleep(0.6)
84
+
85
+ assert mon_bin_drv["MonStatErr"] == True
86
+ assert mon_bin_drv["SafePosAct"] == True
87
+ assert mon_bin_drv["FwdFbk"] == True # safe position is clockwise rotation
88
+
89
+ mon_bin_drv["FwdFbk"] = (
90
+ False # FwdFbk becomes to False w/o control signals
91
+ )
92
+
93
+ time.sleep(0.6)
94
+
95
+ assert mon_bin_drv["MonStatErr"] == True
96
+ assert mon_bin_drv["SafePosAct"] == True
97
+ assert mon_bin_drv["FwdFbk"] == True # safe position is clockwise rotation
98
+ finally:
99
+ mon_bin_drv.set_stop_monitor()
100
+ mon_bin_drv.monitor_static_thread.join()
101
+ mon_bin_drv.monitor_dynamic_thread.join()
102
+
103
+
104
+ @pytest.mark.parametrize(
105
+ "op_mode, src_mode, set_command", test_scenario_no_control_signals
106
+ )
107
+ def test_static_error_within_monitor_time(op_mode, src_mode, set_command):
108
+ """If the required state is set on the interface before the time has
109
+ expired (wait tme < 0.5s in this test), there is no error"""
110
+
111
+ for command in [True, False]:
112
+ mon_bin_drv = init_mon_bin_drv(init_op_mode=op_mode, init_src_mode=src_mode)
113
+
114
+ mon_bin_drv.start_monitor()
115
+ try:
116
+ eval(f"mon_bin_drv.{set_command}({command})")
117
+ print(f"Scenario: mode {op_mode} {src_mode}, {set_command}")
118
+
119
+ # mimic an error situation: FwdFbk changes to True without any change in control signal
120
+ mon_bin_drv["FwdFbk"] = True
121
+ time.sleep(0.3) # monitor time has not expired (0.5s)
122
+
123
+ assert mon_bin_drv["MonStatErr"] == False
124
+ assert mon_bin_drv["MonDynErr"] == False
125
+ # assert mon_bin_drv["SafePosAct"] == False
126
+
127
+ finally:
128
+ mon_bin_drv.set_stop_monitor()
129
+ mon_bin_drv.monitor_static_thread.join()
130
+ mon_bin_drv.monitor_dynamic_thread.join()
131
+
132
+
133
+ test_scenario_control_signals_fwd = [
134
+ ("op", "int", "set_fwd_op", "set_stop_op"),
135
+ ("op", "man", "set_fwd_op", "set_stop_op"),
136
+ ("aut", "int", "set_fwd_aut", "set_stop_aut"),
137
+ ("aut", "man", "set_fwd_aut", "set_stop_aut"),
138
+ ]
139
+
140
+
141
+ @pytest.mark.parametrize(
142
+ "op_mode, src_mode, fwd_command, stop_command", test_scenario_control_signals_fwd
143
+ )
144
+ def test_dynamic_error_fwd_stop(op_mode, src_mode, fwd_command, stop_command):
145
+ mon_bin_drv = init_mon_bin_drv(init_op_mode=op_mode, init_src_mode=src_mode)
146
+
147
+ mon_bin_drv.start_monitor()
148
+ try:
149
+ eval(f"mon_bin_drv.{fwd_command}(True)")
150
+ print(f"Scenario: mode {op_mode} {src_mode}, {fwd_command}")
151
+
152
+ # FwdFbk does not change to True after control signal has changed to clockwise rotation
153
+ mon_bin_drv["FwdFbk"] = False
154
+ time.sleep(0.6)
155
+
156
+ assert mon_bin_drv["MonDynErr"] == True
157
+ assert mon_bin_drv["MonStatErr"] == False
158
+ assert mon_bin_drv["SafePosAct"] == True
159
+ assert mon_bin_drv["FwdFbk"] == True # safe position is clockwise rotation
160
+
161
+ eval(f"mon_bin_drv.{stop_command}(True)")
162
+ print(f"Scenario: mode {op_mode} {src_mode}, {stop_command}")
163
+
164
+ # FwdFbk does not change to False after control signal has changed to stop
165
+ mon_bin_drv["FwdFbk"] = True
166
+ time.sleep(0.6)
167
+
168
+ assert mon_bin_drv["MonDynErr"] == True
169
+ assert mon_bin_drv["MonStatErr"] == False
170
+ assert mon_bin_drv["SafePosAct"] == True
171
+ assert mon_bin_drv["FwdFbk"] == True # safe position is clockwise rotation
172
+ finally:
173
+ mon_bin_drv.set_stop_monitor()
174
+ mon_bin_drv.monitor_static_thread.join()
175
+ mon_bin_drv.monitor_dynamic_thread.join()
176
+
177
+
178
+ test_scenario_control_signals_rev = [
179
+ ("op", "int", "set_rev_op", "set_stop_op"),
180
+ ("op", "man", "set_rev_op", "set_stop_op"),
181
+ ("aut", "int", "set_rev_aut", "set_stop_aut"),
182
+ ("aut", "man", "set_rev_aut", "set_stop_aut"),
183
+ ]
184
+
185
+
186
+ @pytest.mark.parametrize(
187
+ "op_mode, src_mode, rev_command, stop_command", test_scenario_control_signals_rev
188
+ )
189
+ def test_dynamic_error_rev_stop(op_mode, src_mode, rev_command, stop_command):
190
+ mon_bin_drv = init_mon_bin_drv(init_op_mode=op_mode, init_src_mode=src_mode)
191
+
192
+ mon_bin_drv.start_monitor()
193
+ try:
194
+ eval(f"mon_bin_drv.{rev_command}(True)")
195
+ print(f"Scenario: mode {op_mode} {src_mode}, {rev_command}")
196
+
197
+ # RevFbk does not change to True after control signal has changed to anti-clockwise rotation
198
+ mon_bin_drv["RevFbk"] = False
199
+ time.sleep(0.6)
200
+ assert mon_bin_drv["MonDynErr"] == True
201
+ assert mon_bin_drv["MonStatErr"] == False
202
+ assert mon_bin_drv["SafePosAct"] == True
203
+ assert mon_bin_drv["FwdFbk"] == True # safe position is clockwise rotation
204
+
205
+ eval(f"mon_bin_drv.{stop_command}(True)")
206
+ print(f"Scenario: mode {op_mode} {src_mode}, {stop_command}")
207
+
208
+ # FwdFbk does not change to False after control signal has changed to stop
209
+ mon_bin_drv["FwdFbk"] = True
210
+ time.sleep(0.6)
211
+
212
+ assert mon_bin_drv["MonDynErr"] == True
213
+ assert mon_bin_drv["MonStatErr"] == False
214
+ assert mon_bin_drv["SafePosAct"] == True
215
+ assert mon_bin_drv["FwdFbk"] == True # safe position is clockwise rotation
216
+ finally:
217
+ mon_bin_drv.set_stop_monitor()
218
+ mon_bin_drv.monitor_static_thread.join()
219
+ mon_bin_drv.monitor_dynamic_thread.join()
220
+
221
+
222
+ test_scenario_control_signals_reset = [
223
+ ("op", "int", "set_reset_op"),
224
+ ("op", "man", "set_reset_op"),
225
+ ("aut", "int", "set_reset_aut"),
226
+ ("aut", "man", "set_reset_aut"),
227
+ ]
228
+
229
+
230
+ @pytest.mark.parametrize(
231
+ "op_mode, src_mode, set_command", test_scenario_control_signals_reset
232
+ )
233
+ def test_dynamic_error_reset(op_mode, src_mode, set_command):
234
+ mon_bin_drv = init_mon_bin_drv(init_op_mode=op_mode, init_src_mode=src_mode)
235
+
236
+ mon_bin_drv.start_monitor()
237
+ try:
238
+ # set valve to open
239
+ if op_mode == "op":
240
+ mon_bin_drv.set_fwd_op(True)
241
+ elif op_mode == "aut":
242
+ mon_bin_drv.set_fwd_aut(True)
243
+ time.sleep(0.6)
244
+
245
+ # reset valve
246
+ eval(f"mon_bin_drv.{set_command}(True)")
247
+ print(f"Scenario: mode {op_mode} {src_mode}, {set_command}")
248
+
249
+ # mimic an error situation: FwdFbk does not change to False after reset changes to True
250
+ mon_bin_drv["FwdFbk"] = True
251
+ time.sleep(0.6)
252
+
253
+ assert mon_bin_drv["MonDynErr"] == True
254
+ assert mon_bin_drv["MonStatErr"] == False
255
+ assert mon_bin_drv["SafePosAct"] == True
256
+ assert mon_bin_drv["FwdFbk"] == True # safe position of valve is open
257
+ finally:
258
+ mon_bin_drv.set_stop_monitor()
259
+ mon_bin_drv.monitor_static_thread.join()
260
+ mon_bin_drv.monitor_dynamic_thread.join()
261
+
262
+
263
+ if __name__ == "__main__":
264
+ # This allows running the tests directly if needed
265
+ 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"