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,302 @@
1
+ """Test Suite for CommandEnControl."""
2
+
3
+ __copyright__ = (
4
+ "Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
5
+ )
6
+ __license__ = "MIT"
7
+
8
+ from mtppy.part4.automation_services.helpers.command_en_control import CommandEnControl
9
+ import itertools
10
+
11
+ hold_enabled = True
12
+ pause_enabled = True
13
+ restart_enabled = True
14
+
15
+ test_cases = {
16
+ "undefined": {
17
+ "undefined": False,
18
+ "reset": False,
19
+ "start": False,
20
+ "stop": False,
21
+ "hold": False,
22
+ "unhold": False,
23
+ "pause": False,
24
+ "resume": False,
25
+ "abort": False,
26
+ "restart": False,
27
+ "complete": False,
28
+ },
29
+ "idle": {
30
+ "undefined": False,
31
+ "reset": False,
32
+ "start": True,
33
+ "stop": True,
34
+ "hold": False,
35
+ "unhold": False,
36
+ "pause": False,
37
+ "resume": False,
38
+ "abort": True,
39
+ "restart": False,
40
+ "complete": False,
41
+ },
42
+ "starting": {
43
+ "undefined": False,
44
+ "reset": False,
45
+ "start": False,
46
+ "stop": True,
47
+ "hold": hold_enabled,
48
+ "unhold": False,
49
+ "pause": False,
50
+ "resume": False,
51
+ "abort": True,
52
+ "restart": False,
53
+ "complete": True,
54
+ },
55
+ "execute": {
56
+ "undefined": False,
57
+ "reset": False,
58
+ "start": False,
59
+ "stop": True,
60
+ "hold": hold_enabled,
61
+ "unhold": False,
62
+ "pause": pause_enabled,
63
+ "resume": False,
64
+ "abort": True,
65
+ "restart": restart_enabled,
66
+ "complete": True,
67
+ },
68
+ "completing": {
69
+ "undefined": False,
70
+ "reset": False,
71
+ "start": False,
72
+ "stop": True,
73
+ "hold": hold_enabled,
74
+ "unhold": False,
75
+ "pause": False,
76
+ "resume": False,
77
+ "abort": True,
78
+ "restart": False,
79
+ "complete": False,
80
+ },
81
+ "completed": {
82
+ "undefined": False,
83
+ "reset": True,
84
+ "start": False,
85
+ "stop": True,
86
+ "hold": False,
87
+ "unhold": False,
88
+ "pause": False,
89
+ "resume": False,
90
+ "abort": True,
91
+ "restart": False,
92
+ "complete": False,
93
+ },
94
+ "resuming": {
95
+ "undefined": False,
96
+ "reset": False,
97
+ "start": False,
98
+ "stop": True,
99
+ "hold": hold_enabled,
100
+ "unhold": False,
101
+ "pause": False,
102
+ "resume": False,
103
+ "abort": True,
104
+ "restart": False,
105
+ "complete": True,
106
+ },
107
+ "paused": {
108
+ "undefined": False,
109
+ "reset": False,
110
+ "start": False,
111
+ "stop": True,
112
+ "hold": hold_enabled,
113
+ "unhold": False,
114
+ "pause": False,
115
+ "resume": True,
116
+ "abort": True,
117
+ "restart": False,
118
+ "complete": True,
119
+ },
120
+ "pausing": {
121
+ "undefined": False,
122
+ "reset": False,
123
+ "start": False,
124
+ "stop": True,
125
+ "hold": hold_enabled,
126
+ "unhold": False,
127
+ "pause": False,
128
+ "resume": False,
129
+ "abort": True,
130
+ "restart": False,
131
+ "complete": True,
132
+ },
133
+ "holding": {
134
+ "undefined": False,
135
+ "reset": False,
136
+ "start": False,
137
+ "stop": True,
138
+ "hold": False,
139
+ "unhold": False,
140
+ "pause": False,
141
+ "resume": False,
142
+ "abort": True,
143
+ "restart": False,
144
+ "complete": False,
145
+ },
146
+ "held": {
147
+ "undefined": False,
148
+ "reset": False,
149
+ "start": False,
150
+ "stop": True,
151
+ "hold": False,
152
+ "unhold": True,
153
+ "pause": False,
154
+ "resume": False,
155
+ "abort": True,
156
+ "restart": False,
157
+ "complete": False,
158
+ },
159
+ "unholding": {
160
+ "undefined": False,
161
+ "reset": False,
162
+ "start": False,
163
+ "stop": True,
164
+ "hold": hold_enabled,
165
+ "unhold": False,
166
+ "pause": False,
167
+ "resume": False,
168
+ "abort": True,
169
+ "restart": False,
170
+ "complete": True,
171
+ },
172
+ "stopping": {
173
+ "undefined": False,
174
+ "reset": False,
175
+ "start": False,
176
+ "stop": False,
177
+ "hold": False,
178
+ "unhold": False,
179
+ "pause": False,
180
+ "resume": False,
181
+ "abort": True,
182
+ "restart": False,
183
+ "complete": False,
184
+ },
185
+ "stopped": {
186
+ "undefined": False,
187
+ "reset": True,
188
+ "start": False,
189
+ "stop": False,
190
+ "hold": False,
191
+ "unhold": False,
192
+ "pause": False,
193
+ "resume": False,
194
+ "abort": True,
195
+ "restart": False,
196
+ "complete": False,
197
+ },
198
+ "aborting": {
199
+ "undefined": False,
200
+ "reset": False,
201
+ "start": False,
202
+ "stop": False,
203
+ "hold": False,
204
+ "unhold": False,
205
+ "pause": False,
206
+ "resume": False,
207
+ "abort": False,
208
+ "restart": False,
209
+ "complete": False,
210
+ },
211
+ "aborted": {
212
+ "undefined": False,
213
+ "reset": True,
214
+ "start": False,
215
+ "stop": False,
216
+ "hold": False,
217
+ "unhold": False,
218
+ "pause": False,
219
+ "resume": False,
220
+ "abort": False,
221
+ "restart": False,
222
+ "complete": False,
223
+ },
224
+ "resetting": {
225
+ "undefined": False,
226
+ "reset": False,
227
+ "start": False,
228
+ "stop": True,
229
+ "hold": False,
230
+ "unhold": False,
231
+ "pause": False,
232
+ "resume": False,
233
+ "abort": True,
234
+ "restart": False,
235
+ "complete": False,
236
+ },
237
+ }
238
+
239
+
240
+ def init_state(state="idle", pause_en=True, restart_en=True):
241
+ command_en_ctrl = CommandEnControl()
242
+ command_en_ctrl.execute(state)
243
+ command_en_ctrl.enable_pause_loop(pause_en)
244
+ command_en_ctrl.enable_restart(restart_en)
245
+ return command_en_ctrl
246
+
247
+
248
+ def test_states():
249
+ for state in test_cases:
250
+ for loop_en_flags in itertools.product([False, True], repeat=3):
251
+ pause_en = loop_en_flags[0]
252
+ restart_en = loop_en_flags[0]
253
+ command_en_ctrl = init_state(
254
+ state=state, pause_en=pause_en, restart_en=restart_en
255
+ )
256
+ command_en_ctrl.execute(state)
257
+ for command, command_en in test_cases[state].items():
258
+ response = command_en_ctrl.command_en[command]["value"]
259
+ print(
260
+ f"State: {state}, command: {command}, pause_en: {pause_en}, restart_en: {restart_en}, expected: {command_en}, received: {response}"
261
+ )
262
+ assert response == command_en
263
+
264
+
265
+ def test_set_default():
266
+ command_en_ctrl = init_state()
267
+ command_en_ctrl.set_default()
268
+ for command_en in command_en_ctrl.command_en:
269
+ assert (
270
+ command_en_ctrl.command_en[command_en]["value"]
271
+ == command_en_ctrl.command_en[command_en]["default"]
272
+ )
273
+
274
+
275
+ def test_disable_all():
276
+ command_en_ctrl = init_state()
277
+ command_en_ctrl.disable_all()
278
+ for command_en in command_en_ctrl.command_en:
279
+ assert command_en_ctrl.command_en[command_en]["value"] == False
280
+
281
+
282
+ def test_get_command():
283
+ command_en_ctrl = init_state()
284
+ for command_en in command_en_ctrl.command_en:
285
+ assert (
286
+ command_en_ctrl.is_enabled(command_en)
287
+ == command_en_ctrl.command_en[command_en]["value"]
288
+ )
289
+
290
+ assert command_en_ctrl.is_enabled("not_exist") is None
291
+
292
+
293
+ def test_set_command():
294
+ command_en_ctrl = init_state()
295
+ for command_en in command_en_ctrl.command_en:
296
+ command_en_ctrl.set_command_en(command_en, True)
297
+ command_en_ctrl.set_command_en("not_exist", False)
298
+ assert command_en_ctrl.command_en[command_en]["value"] == True
299
+
300
+ command_en_ctrl.set_command_en(command_en, False)
301
+ command_en_ctrl.set_command_en("not_exist", True)
302
+ assert command_en_ctrl.command_en[command_en]["value"] == False
@@ -0,0 +1,167 @@
1
+ """
2
+ Implementation of the State Machine concept as specified in PNO MTP 2.0 Part 4, Section 8.2.3.1
3
+ """
4
+
5
+ __copyright__ = (
6
+ "Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
7
+ )
8
+ __license__ = "MIT"
9
+
10
+ from collections.abc import Callable
11
+
12
+ from mtppy.utils.logging import logger
13
+ from mtppy.part4.definitions.state_codes import StateCodes
14
+ from mtppy.part4.definitions.command_codes import CommandCodes
15
+ from mtppy.part4.automation_services.helpers.command_en_control import CommandEnControl
16
+ from mtppy.part4.automation_services.service_elements.service_control import ServiceControl
17
+
18
+
19
+ class StateMachine:
20
+ def __init__(self, service_control: ServiceControl, execution_routine: Callable):
21
+ """
22
+ Initialize the state machine for a service.
23
+
24
+ Args:
25
+ service_control: ServiceControl.
26
+ execution_routine: Execution routine for state changing.
27
+
28
+ """
29
+
30
+ self.service_control = service_control
31
+ self.service_op_mode = service_control.service_op_mode
32
+ self.service_src_mode = service_control.service_src_mode
33
+
34
+ self.execution_routine = execution_routine
35
+ self.command_en_ctrl = CommandEnControl()
36
+
37
+ self.act_state: int = StateCodes.UNDEFINED.code
38
+ self.prev_state: int = StateCodes.UNDEFINED.code
39
+
40
+ self.is_active = False
41
+
42
+ def activate(self):
43
+ # should happen on ServiceOpMode Off --> *
44
+ if self.act_state == StateCodes.UNDEFINED.code:
45
+ self._change_state_to(StateCodes.IDLE.code)
46
+ self.is_active = True
47
+ self.command_en_ctrl.execute(StateCodes.from_code(self.act_state))
48
+
49
+ def deactivate(self):
50
+ # should happen on ServiceOpMode * --> Off while in IDLE
51
+ if self.act_state == StateCodes.IDLE.code:
52
+ self._change_state_to(StateCodes.UNDEFINED.code)
53
+ self.is_active = False
54
+ self.command_en_ctrl.execute(StateCodes.from_code(self.act_state))
55
+
56
+ def set_command_op(self, value: int):
57
+ if self.service_op_mode["StateOpAct"]:
58
+ self.command_execution(value)
59
+
60
+ def set_command_int(self, value: int):
61
+ if self.service_op_mode["StateAutAct"] and self.service_src_mode["SrcIntAct"]:
62
+ self.command_execution(value)
63
+
64
+ def set_command_ext(self, value: int):
65
+ if self.service_op_mode["StateAutAct"] and self.service_src_mode["SrcExtAct"]:
66
+ self.command_execution(value)
67
+
68
+ def command_execution(self, command_code: int):
69
+ if self.is_active:
70
+ logger.debug(
71
+ f"command code {command_code} received for {self.service_control.tag_name}"
72
+ )
73
+ if command_code not in CommandCodes.list_of_codes():
74
+ logger.debug(f"Command Code {command_code} does not exist")
75
+ return
76
+
77
+ cmd_str = CommandCodes.from_code(command_code)
78
+ if not self.command_en_ctrl.is_enabled(cmd_str):
79
+ logger.debug(
80
+ f"CommandEn does not permit to execute {cmd_str} from state {self.get_current_state_str()}"
81
+ )
82
+ return
83
+ else:
84
+ logger.debug(f"CommandEn permits to execute {cmd_str}")
85
+
86
+ eval(f"self.{cmd_str}()")
87
+
88
+ def start(self):
89
+ if self.command_en_ctrl.is_enabled("start"):
90
+ self.service_control.set_procedure_cur()
91
+ # applying the procedure is job of the Service callback
92
+ self._change_state_to(StateCodes.STARTING.code)
93
+
94
+ def restart(self):
95
+ if self.command_en_ctrl.is_enabled("restart"):
96
+ self._change_state_to(StateCodes.STARTING.code)
97
+
98
+ def complete(self):
99
+ if self.command_en_ctrl.is_enabled("complete"):
100
+ self._change_state_to(StateCodes.COMPLETING.code)
101
+
102
+ def pause(self):
103
+ if self.command_en_ctrl.is_enabled("pause"):
104
+ self._change_state_to(StateCodes.PAUSING.code)
105
+
106
+ def resume(self):
107
+ if self.command_en_ctrl.is_enabled("resume"):
108
+ self._change_state_to(StateCodes.RESUMING.code)
109
+
110
+ def reset(self):
111
+ if self.command_en_ctrl.is_enabled("reset"):
112
+ self._change_state_to(StateCodes.RESETTING.code)
113
+
114
+ def hold(self):
115
+ if self.command_en_ctrl.is_enabled("hold"):
116
+ self._change_state_to(StateCodes.HOLDING.code)
117
+
118
+ def unhold(self):
119
+ if self.command_en_ctrl.is_enabled("unhold"):
120
+ self._change_state_to(StateCodes.UNHOLDING.code)
121
+
122
+ def stop(self):
123
+ if self.command_en_ctrl.is_enabled("stop"):
124
+ self._change_state_to(StateCodes.STOPPING.code)
125
+
126
+ def abort(self):
127
+ if self.command_en_ctrl.is_enabled("abort"):
128
+ self._change_state_to(StateCodes.ABORTING.code)
129
+
130
+ def state_change(self):
131
+ logger.debug(
132
+ f"get possible state_change transitions for state {self.act_state}"
133
+ )
134
+ if self.act_state == StateCodes.STARTING.code:
135
+ self._change_state_to(StateCodes.EXECUTE.code)
136
+ elif self.act_state == StateCodes.STARTING.code:
137
+ self._change_state_to(StateCodes.EXECUTE.code)
138
+ elif self.act_state == StateCodes.COMPLETING.code:
139
+ self._change_state_to(StateCodes.COMPLETED.code)
140
+ elif self.act_state == StateCodes.PAUSING.code:
141
+ self._change_state_to(StateCodes.PAUSED.code)
142
+ elif self.act_state == StateCodes.RESUMING.code:
143
+ self._change_state_to(StateCodes.EXECUTE.code)
144
+ elif self.act_state == StateCodes.RESETTING.code:
145
+ self._change_state_to(StateCodes.IDLE.code)
146
+ elif self.act_state == StateCodes.HOLDING.code:
147
+ self._change_state_to(StateCodes.HELD.code)
148
+ elif self.act_state == StateCodes.UNHOLDING.code:
149
+ self._change_state_to(StateCodes.EXECUTE.code)
150
+ elif self.act_state == StateCodes.STOPPING.code:
151
+ self._change_state_to(StateCodes.STOPPED.code)
152
+ elif self.act_state == StateCodes.HOLDING.code:
153
+ self._change_state_to(StateCodes.ABORTING.code)
154
+
155
+ def _change_state_to(self, new_state: int) -> None:
156
+ logger.debug(f"Service state switching requested {new_state}")
157
+ self.act_state = new_state
158
+ self.service_control["StateCur"] = new_state
159
+ new_state_str = StateCodes.from_code(new_state)
160
+ self.command_en_ctrl.execute(new_state_str)
161
+ self.service_control["CommandEn"] = self.command_en_ctrl.get_command_en()
162
+ self.execution_routine()
163
+ logger.debug(f"Service state changed to {new_state}")
164
+
165
+ def get_current_state_str(self) -> str:
166
+ logger.debug(f"current state {self.act_state}")
167
+ return StateCodes.from_code(self.act_state)