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,473 @@
1
+ """Implementation of Service Control as specified in PNO MTP 2.0, Section x."""
2
+
3
+ __copyright__ = (
4
+ "Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
5
+ )
6
+ __license__ = "MIT"
7
+
8
+ from collections.abc import Callable
9
+ from typing import ClassVar
10
+
11
+ from mtppy.utils.data_item import DataItem
12
+ from mtppy.part3.definitions.mtp_access import MTPAccess
13
+ from mtppy.part3.data_assembly_set.base_functions.oslevel import OSLevelBaseFunction
14
+ from mtppy.part4.automation_services.base_functions.service_operation_mode import (
15
+ ServiceOperationMode,
16
+ )
17
+ from mtppy.part4.automation_services.base_functions.service_operator_interaction import (
18
+ ServiceOperatorInteraction,
19
+ )
20
+ from mtppy.part4.automation_services.base_functions.service_source_mode import (
21
+ ServiceSourceMode,
22
+ )
23
+ from mtppy.part4.automation_services.service_elements.service_element import (
24
+ ServiceElement,
25
+ )
26
+ from mtppy.part5.definitions.w3c_xml_data_types import W3cXmlDataTypes
27
+ from mtppy.utils.logging import logger
28
+
29
+
30
+ class ServiceControl(ServiceElement):
31
+ # DataItem constant definitions
32
+ COMMAND_EN = "CommandEn"
33
+ COMMAND_OP = "CommandOp"
34
+ COMMAND_INT = "CommandInt"
35
+ COMMAND_EXT = "CommandExt"
36
+ STATE_CUR = "StateCur"
37
+ PROCEDURE_OP = "ProcedureOp"
38
+ PROCEDURE_INT = "ProcedureInt"
39
+ PROCEDURE_EXT = "ProcedureExt"
40
+ PROCEDURE_CUR = "ProcedureCur"
41
+ PROCEDURE_REQ = "ProcedureReq"
42
+ PROC_PARAM_APPLY_EN = "ProcParamApplyEn"
43
+ PROC_PARAM_APPLY_EXT = "ProcParamApplyExt"
44
+ PROC_PARAM_APPLY_OP = "ProcParamApplyOp"
45
+ PROC_PARAM_APPLY_INT = "ProcParamApplyInt"
46
+ CONFIG_PARAM_APPLY_EN = "ConfigParamApplyEn"
47
+ CONFIG_PARAM_APPLY_EXT = "ConfigParamApplyExt"
48
+ CONFIG_PARAM_APPLY_OP = "ConfigParamApplyOp"
49
+ CONFIG_PARAM_APPLY_INT = "ConfigParamApplyInt"
50
+ REPORT_VALUE_FREEZE = "ReportValueFreeze"
51
+
52
+ # Class-level data items dictionary
53
+ CLASS_DATA_ITEMS: ClassVar = {
54
+ COMMAND_EN: DataItem(
55
+ COMMAND_EN,
56
+ W3cXmlDataTypes.UNSIGNED_SHORT,
57
+ MTPAccess.ReadAccess,
58
+ 0,
59
+ ),
60
+ COMMAND_OP: DataItem(
61
+ COMMAND_OP,
62
+ W3cXmlDataTypes.UNSIGNED_SHORT,
63
+ MTPAccess.ReadWriteAccess,
64
+ 0,
65
+ ),
66
+ COMMAND_INT: DataItem(
67
+ COMMAND_INT,
68
+ W3cXmlDataTypes.UNSIGNED_SHORT,
69
+ MTPAccess.ReadAccess,
70
+ 0,
71
+ ),
72
+ COMMAND_EXT: DataItem(
73
+ COMMAND_EXT,
74
+ W3cXmlDataTypes.UNSIGNED_SHORT,
75
+ MTPAccess.ReadWriteAccess,
76
+ 0,
77
+ ),
78
+ STATE_CUR: DataItem(
79
+ STATE_CUR,
80
+ W3cXmlDataTypes.UNSIGNED_SHORT,
81
+ MTPAccess.ReadAccess,
82
+ 0,
83
+ ),
84
+ PROCEDURE_OP: DataItem(
85
+ PROCEDURE_OP,
86
+ W3cXmlDataTypes.UNSIGNED_SHORT,
87
+ MTPAccess.ReadWriteAccess,
88
+ 0,
89
+ ),
90
+ PROCEDURE_INT: DataItem(
91
+ PROCEDURE_INT,
92
+ W3cXmlDataTypes.UNSIGNED_SHORT,
93
+ MTPAccess.ReadAccess,
94
+ 0,
95
+ ),
96
+ PROCEDURE_EXT: DataItem(
97
+ PROCEDURE_EXT,
98
+ W3cXmlDataTypes.UNSIGNED_SHORT,
99
+ MTPAccess.ReadWriteAccess,
100
+ 0,
101
+ ),
102
+ PROCEDURE_CUR: DataItem(
103
+ PROCEDURE_CUR,
104
+ W3cXmlDataTypes.UNSIGNED_SHORT,
105
+ MTPAccess.ReadAccess,
106
+ 0,
107
+ ),
108
+ PROCEDURE_REQ: DataItem(
109
+ PROCEDURE_REQ,
110
+ W3cXmlDataTypes.UNSIGNED_SHORT,
111
+ MTPAccess.ReadAccess,
112
+ 0,
113
+ ),
114
+ PROC_PARAM_APPLY_EN: DataItem(
115
+ PROC_PARAM_APPLY_EN,
116
+ W3cXmlDataTypes.BOOLEAN,
117
+ MTPAccess.ReadAccess,
118
+ 0,
119
+ ),
120
+ PROC_PARAM_APPLY_EXT: DataItem(
121
+ PROC_PARAM_APPLY_EXT,
122
+ W3cXmlDataTypes.BOOLEAN,
123
+ MTPAccess.ReadWriteAccess,
124
+ 0,
125
+ ),
126
+ PROC_PARAM_APPLY_OP: DataItem(
127
+ PROC_PARAM_APPLY_OP,
128
+ W3cXmlDataTypes.BOOLEAN,
129
+ MTPAccess.ReadWriteAccess,
130
+ 0,
131
+ ),
132
+ PROC_PARAM_APPLY_INT: DataItem(
133
+ PROC_PARAM_APPLY_INT,
134
+ W3cXmlDataTypes.BOOLEAN,
135
+ MTPAccess.ReadAccess,
136
+ 0,
137
+ ),
138
+ CONFIG_PARAM_APPLY_EN: DataItem(
139
+ CONFIG_PARAM_APPLY_EN,
140
+ W3cXmlDataTypes.BOOLEAN,
141
+ MTPAccess.ReadAccess,
142
+ 0,
143
+ ),
144
+ CONFIG_PARAM_APPLY_EXT: DataItem(
145
+ CONFIG_PARAM_APPLY_EXT,
146
+ W3cXmlDataTypes.BOOLEAN,
147
+ MTPAccess.ReadWriteAccess,
148
+ 0,
149
+ ),
150
+ CONFIG_PARAM_APPLY_OP: DataItem(
151
+ CONFIG_PARAM_APPLY_OP,
152
+ W3cXmlDataTypes.BOOLEAN,
153
+ MTPAccess.ReadWriteAccess,
154
+ 0,
155
+ ),
156
+ CONFIG_PARAM_APPLY_INT: DataItem(
157
+ CONFIG_PARAM_APPLY_INT,
158
+ W3cXmlDataTypes.BOOLEAN,
159
+ MTPAccess.ReadAccess,
160
+ 0,
161
+ ),
162
+ REPORT_VALUE_FREEZE: DataItem(
163
+ REPORT_VALUE_FREEZE,
164
+ W3cXmlDataTypes.BOOLEAN,
165
+ MTPAccess.ReadWriteAccess,
166
+ 0,
167
+ ),
168
+ }
169
+
170
+ os_level: OSLevelBaseFunction
171
+ service_op_mode: ServiceOperationMode
172
+ service_src_mode: ServiceSourceMode
173
+ service_operator_interaction: ServiceOperatorInteraction
174
+ procedure_keys: list[int]
175
+ execution_procedure: Callable
176
+
177
+ def __init__(
178
+ self,
179
+ tag_name: str,
180
+ tag_description: str = "",
181
+ procedure_keys: list[int] | None = None,
182
+ execution_procedure: Callable | None = None,
183
+ ):
184
+ super().__init__(tag_name, tag_description)
185
+ self.mtpObjectType = (
186
+ "MTPDataAssemblySUCLib/DataAssembly/ServiceElement/ServiceControl"
187
+ )
188
+
189
+ self.os_level = OSLevelBaseFunction()
190
+ self.service_op_mode = ServiceOperationMode()
191
+ self.service_src_mode = ServiceSourceMode()
192
+ self.service_operator_interaction = ServiceOperatorInteraction()
193
+
194
+ self.procedure_keys: list[int] = (
195
+ procedure_keys if procedure_keys is not None else []
196
+ )
197
+
198
+ # Initialize data items with dynamic values (using class-defined data items)
199
+ self.get_item(ServiceControl.COMMAND_OP).init_value = 0
200
+ self.get_item(ServiceControl.COMMAND_EXT).init_value = 0
201
+ self.get_item(ServiceControl.PROCEDURE_OP).init_value = 0
202
+ self.get_item(ServiceControl.PROCEDURE_EXT).init_value = 0
203
+ self.get_item(ServiceControl.PROC_PARAM_APPLY_EXT).init_value = 0
204
+ self.get_item(ServiceControl.PROC_PARAM_APPLY_OP).init_value = 0
205
+ self.get_item(ServiceControl.CONFIG_PARAM_APPLY_EXT).init_value = 0
206
+ self.get_item(ServiceControl.CONFIG_PARAM_APPLY_OP).init_value = 0
207
+ self.get_item(ServiceControl.REPORT_VALUE_FREEZE).init_value = 0
208
+
209
+ self.get_item(ServiceControl.COMMAND_OP).ext_wrt_cb = self.set_command_op
210
+ self.get_item(ServiceControl.COMMAND_EXT).ext_wrt_cb = self.set_command_ext
211
+ self.get_item(ServiceControl.PROCEDURE_OP).ext_wrt_cb = self.set_procedure_op
212
+ self.get_item(ServiceControl.PROCEDURE_EXT).ext_wrt_cb = self.set_procedure_ext
213
+ self.get_item(ServiceControl.PROC_PARAM_APPLY_EXT).ext_wrt_cb = (
214
+ self.set_proc_param_apply_ext
215
+ )
216
+ self.get_item(ServiceControl.PROC_PARAM_APPLY_OP).ext_wrt_cb = (
217
+ self.set_proc_param_apply_op
218
+ )
219
+ self.get_item(ServiceControl.CONFIG_PARAM_APPLY_EXT).ext_wrt_cb = (
220
+ self.set_config_param_apply_ext
221
+ )
222
+ self.get_item(ServiceControl.CONFIG_PARAM_APPLY_OP).ext_wrt_cb = (
223
+ self.set_config_param_apply_op
224
+ )
225
+ self.get_item(ServiceControl.REPORT_VALUE_FREEZE).ext_wrt_cb = (
226
+ self.set_report_value_freeze
227
+ )
228
+
229
+ self.execution_procedure = execution_procedure or self.dummy_procedure
230
+
231
+ def dummy_procedure(self, value: int) -> None:
232
+ """A dummy procedure for testing purposes.
233
+
234
+ Args:
235
+ value: Value to log during the dummy procedure call.
236
+ """
237
+ logger.debug(f"calling self.dummy_procedure with value {value}")
238
+
239
+ def add_procedure_key(self, value: int) -> None:
240
+ """Add a procedure key to the list of valid keys.
241
+
242
+ Args:
243
+ value: The procedure key to add.
244
+
245
+ """
246
+ if value != 0 and value not in self.procedure_keys:
247
+ self.procedure_keys.append(value)
248
+
249
+ def remove_procedure_key(self, value: int) -> None:
250
+ """Remove a procedure key from the list of valid keys.
251
+
252
+ Args:
253
+ value: The procedure key to remove.
254
+
255
+ """
256
+ if value != 0 and value in self.procedure_keys:
257
+ self.procedure_keys.remove(value)
258
+
259
+ def set_command_op(self, value: int) -> None:
260
+ """Set the operator command.
261
+
262
+ Args:
263
+ value: The value to set for the operator command.
264
+
265
+ """
266
+ logger.debug(f"call {self.execution_procedure} with value {value}")
267
+ self.execution_procedure(value)
268
+ # Handshake
269
+ self[ServiceControl.COMMAND_OP] = 0
270
+ # self.get_item(ServiceControl.COMMAND_OP)._value = 0
271
+ # self.get_item(ServiceControl.COMMAND_OP)._notify_external_subscribers()
272
+
273
+ def set_command_ext(self, value: int) -> None:
274
+ """Set the external command.
275
+
276
+ Args:
277
+ value: The value to set for the external command.
278
+
279
+ """
280
+ # self[ServiceControl.COMMAND_EXT] = value
281
+ self.execution_procedure(value)
282
+ self[ServiceControl.COMMAND_EXT] = 0
283
+
284
+ def set_command_int(self, value: int) -> None:
285
+ """Set the internal command.
286
+
287
+ Args:
288
+ value: The value to set for the internal command.
289
+
290
+ """
291
+ self[ServiceControl.COMMAND_INT] = value
292
+
293
+ def set_proc_param_apply_en(self, value: bool) -> None:
294
+ """Set the procedure parameter apply enable.
295
+
296
+ Args:
297
+ value: The boolean value to set for the procedure parameter apply enable.
298
+
299
+ """
300
+ self[ServiceControl.PROC_PARAM_APPLY_EN] = value
301
+
302
+ def set_proc_param_apply_ext(self, value: bool) -> None:
303
+ """Set the external procedure parameter apply signal.
304
+
305
+ Args:
306
+ value: The boolean value to set for the external procedure parameter apply.
307
+
308
+ """
309
+ self[ServiceControl.PROC_PARAM_APPLY_EXT] = value
310
+
311
+ def set_proc_param_apply_op(self, value: bool) -> None:
312
+ """Set the operator procedure parameter apply signal.
313
+
314
+ Args:
315
+ value: The boolean value to set for the operator procedure parameter apply.
316
+
317
+ """
318
+ self[ServiceControl.PROC_PARAM_APPLY_OP] = value
319
+
320
+ def set_proc_param_apply_int(self, value: bool) -> None:
321
+ """Set the internal procedure parameter apply signal.
322
+
323
+ Args:
324
+ value: The boolean value to set for the internal procedure parameter apply.
325
+
326
+ """
327
+ self[ServiceControl.PROC_PARAM_APPLY_INT] = value
328
+
329
+ def apply_proc_param(self) -> None:
330
+ """Apply procedure parameters if enabled."""
331
+ if self[ServiceControl.PROC_PARAM_APPLY_EN]:
332
+ # Get ProcCur, Resolve Procedure and fire callback to update Procedures
333
+ self[ServiceControl.CONFIG_PARAM_APPLY_EXT] = False
334
+ self[ServiceControl.CONFIG_PARAM_APPLY_INT] = False
335
+ self[ServiceControl.CONFIG_PARAM_APPLY_OP] = False
336
+
337
+ def set_config_param_apply_en(self, value: bool) -> None:
338
+ """Set the configuration parameter apply enable.
339
+
340
+ Args:
341
+ value: The boolean value to set for the configuration parameter apply enable.
342
+
343
+ """
344
+ self[ServiceControl.CONFIG_PARAM_APPLY_EN] = value
345
+
346
+ def set_config_param_apply_ext(self, value: bool) -> None:
347
+ """Set the external configuration parameter apply signal.
348
+
349
+ Args:
350
+ value: The boolean value to set for the external configuration parameter apply.
351
+
352
+ """
353
+ self[ServiceControl.CONFIG_PARAM_APPLY_EXT] = value
354
+
355
+ def set_config_param_apply_op(self, value: bool) -> None:
356
+ """Set the operator configuration parameter apply signal.
357
+
358
+ Args:
359
+ value: The boolean value to set for the operator configuration parameter apply.
360
+
361
+ """
362
+ self[ServiceControl.CONFIG_PARAM_APPLY_OP] = value
363
+
364
+ def set_config_param_apply_int(self, value: bool) -> None:
365
+ """Set the internal configuration parameter apply signal.
366
+
367
+ Args:
368
+ value: The boolean value to set for the internal configuration parameter apply.
369
+
370
+ """
371
+ self[ServiceControl.CONFIG_PARAM_APPLY_INT] = value
372
+
373
+ def apply_config_param(self) -> None:
374
+ """Apply configuration parameters if enabled."""
375
+ if self[ServiceControl.CONFIG_PARAM_APPLY_EN]:
376
+ # Get ProcCur, Resolve Procedure and fire callback to update Procedures
377
+ self[ServiceControl.CONFIG_PARAM_APPLY_EXT] = False
378
+ self[ServiceControl.CONFIG_PARAM_APPLY_INT] = False
379
+ self[ServiceControl.CONFIG_PARAM_APPLY_OP] = False
380
+
381
+ def set_report_value_freeze(self, value: bool) -> None:
382
+ """Set the report value freeze control.
383
+
384
+ Args:
385
+ value: The boolean value to set for the report value freeze.
386
+
387
+ """
388
+ self[ServiceControl.REPORT_VALUE_FREEZE] = value
389
+
390
+ def set_procedure_op(self, value: int) -> None:
391
+ """Set the operator procedure.
392
+
393
+ Args:
394
+ value: The value to set for the operator procedure.
395
+
396
+ """
397
+ if self.service_op_mode[ServiceOperationMode.STATE_OP_ACT]:
398
+ self.set_procedure_req(value)
399
+ logger.info(
400
+ f"ProcedureOp arg={value}, actual={self[ServiceControl.PROCEDURE_OP]}",
401
+ )
402
+
403
+ def set_procedure_int(self, value: int) -> None:
404
+ """Set the internal procedure.
405
+
406
+ Args:
407
+ value: The value to set for the internal procedure.
408
+ """
409
+ logger.info(f"ProcedureInt set to {value}")
410
+ if (
411
+ self.service_op_mode[ServiceOperationMode.STATE_AUT_ACT]
412
+ and self.service_src_mode[ServiceSourceMode.SRC_INT_ACT]
413
+ ):
414
+ self.set_procedure_req(value)
415
+
416
+ def set_procedure_ext(self, value: int) -> None:
417
+ """Set the external procedure.
418
+
419
+ Args:
420
+ value: The value to set for the external procedure.
421
+ """
422
+ logger.info(f"ProcedureExt set to {value}")
423
+ if (
424
+ self.service_op_mode[ServiceOperationMode.STATE_AUT_ACT]
425
+ and self.service_src_mode[ServiceSourceMode.SRC_EXT_ACT]
426
+ ):
427
+ self.set_procedure_req(value)
428
+
429
+ def valid_value(self, value: int) -> bool:
430
+ """Check if a value is a valid procedure id
431
+
432
+ Args:
433
+ value: The value to check.
434
+
435
+ Returns:
436
+ True if value is in list of procedure keys, else False
437
+
438
+ """
439
+ return not (value not in self.procedure_keys and value != 0)
440
+
441
+ def set_procedure_req(self, value: int) -> None:
442
+ """Set the requested procedure if it is a valid procedure id,
443
+ else set the requested procedure parameter to 0 to indicate to
444
+ the POL that the request was invalid.
445
+
446
+ Args:
447
+ value: The value to set for the requested procedure.
448
+
449
+ """
450
+ if self.valid_value(value):
451
+ self[ServiceControl.PROCEDURE_REQ] = value
452
+ else:
453
+ self[ServiceControl.PROCEDURE_REQ] = 0
454
+ logger.info(f"args={value}, result={self[ServiceControl.PROCEDURE_REQ]}")
455
+
456
+ def set_procedure_cur(self) -> None:
457
+ """Set the current procedure based on the requested procedure."""
458
+ procedure_req = self[ServiceControl.PROCEDURE_REQ]
459
+ if self.valid_value(procedure_req):
460
+ self[ServiceControl.PROCEDURE_CUR] = procedure_req
461
+ # PNO MTP2.0 Part 4 requires resetting ProcedureOP and ProcedureExt (Section 8.2.4.4)
462
+ self[ServiceControl.PROCEDURE_OP] = 0
463
+ self[ServiceControl.PROCEDURE_EXT] = 0
464
+ logger.info(f"args={procedure_req}, result=self[ServiceControl.PROCEDURE_CUR]")
465
+
466
+ def get_procedure_cur(self) -> int:
467
+ """Get the current procedure value.
468
+
469
+ Returns:
470
+ The current procedure value.
471
+
472
+ """
473
+ return self[ServiceControl.PROCEDURE_CUR]
@@ -0,0 +1,150 @@
1
+ """test suite for service control."""
2
+
3
+ __copyright__ = (
4
+ "Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
5
+ )
6
+ __license__ = "MIT"
7
+
8
+ import pytest
9
+
10
+ from mtppy.part4.automation_services.base_functions.service_operation_mode import (
11
+ ServiceOperationMode as SOM,
12
+ )
13
+ from mtppy.part4.automation_services.base_functions.service_source_mode import (
14
+ ServiceSourceMode as ssm,
15
+ )
16
+ from mtppy.part4.automation_services.service_elements.service_control import (
17
+ ServiceControl as sc,
18
+ )
19
+
20
+
21
+ def init_service_control(service_op_mode: str = "off", service_src_mode: str = "int"):
22
+
23
+ service_control = sc("Dummy", procedure_keys=[1, 2, 3, 4])
24
+
25
+ if service_op_mode == "off":
26
+ service_control.service_op_mode[SOM.STATE_OFF_ACT] = True
27
+ service_control.service_op_mode[SOM.STATE_OFF_AUT] = True
28
+ service_control.service_op_mode[SOM.STATE_OFF_OP] = True
29
+ elif service_op_mode == "op":
30
+ service_control.service_op_mode[SOM.STATE_AUT_ACT] = False
31
+ service_control.service_op_mode[SOM.STATE_OFF_ACT] = False
32
+ service_control.service_op_mode[SOM.STATE_OP_ACT] = True
33
+ elif service_op_mode == "aut":
34
+ service_control.service_op_mode[SOM.STATE_AUT_ACT] = True
35
+ service_control.service_op_mode[SOM.STATE_OFF_ACT] = False
36
+ service_control.service_op_mode[SOM.STATE_OP_ACT] = False
37
+ else:
38
+ raise ValueError(f"ServiceOperationMode {service_op_mode} is unknown")
39
+
40
+ if service_src_mode == "int":
41
+ service_control.service_src_mode[ssm.SRC_INT_ACT] = True
42
+ elif service_src_mode == "ext":
43
+ service_control.service_src_mode[ssm.SRC_EXT_ACT] = True
44
+ else:
45
+ raise ValueError(f"ServiceSourceMode {service_src_mode} is unknown")
46
+
47
+ return service_control
48
+
49
+
50
+ def check_valid_states(service_control, set_function, change_allowed=False):
51
+ for proc_id in range(4):
52
+ set_function(proc_id)
53
+ if change_allowed:
54
+ assert service_control[sc.PROCEDURE_REQ] == proc_id
55
+ else:
56
+ assert service_control[sc.PROCEDURE_REQ] == 0
57
+
58
+
59
+ def check_invalid_states(service_control, set_function):
60
+ set_function(0)
61
+ set_function(10)
62
+ assert service_control[sc.PROCEDURE_REQ] == 0
63
+
64
+
65
+ @pytest.mark.parametrize(
66
+ "service_control_mode,change_allowed",
67
+ [
68
+ ("off", False),
69
+ ("op", True),
70
+ ("aut", False),
71
+ ],
72
+ )
73
+ def test_set_procedure_op(service_control_mode, change_allowed):
74
+ service_control = init_service_control(service_control_mode)
75
+ check_valid_states(
76
+ service_control, service_control.set_procedure_op, change_allowed=change_allowed
77
+ )
78
+ check_invalid_states(service_control, service_control.set_procedure_op)
79
+
80
+
81
+ @pytest.mark.parametrize(
82
+ "service_op_mode,service_src_mode,change_allowed",
83
+ [
84
+ ("off", "ext", False),
85
+ ("off", "int", False),
86
+ ("op", "ext", False),
87
+ ("op", "int", False),
88
+ ("aut", "ext", False),
89
+ ("aut", "int", True),
90
+ ],
91
+ )
92
+ def test_set_procedure_int(service_op_mode, service_src_mode, change_allowed):
93
+ service_control = init_service_control(service_op_mode, service_src_mode)
94
+ check_valid_states(
95
+ service_control,
96
+ service_control.set_procedure_int,
97
+ change_allowed=change_allowed,
98
+ )
99
+ check_invalid_states(service_control, service_control.set_procedure_int)
100
+
101
+
102
+ @pytest.mark.parametrize(
103
+ "service_op_mode,service_src_mode,change_allowed",
104
+ [
105
+ ("off", "ext", False),
106
+ ("off", "int", False),
107
+ ("op", "ext", False),
108
+ ("op", "int", False),
109
+ ("aut", "ext", True),
110
+ ("aut", "int", False),
111
+ ],
112
+ )
113
+ def test_set_procedure_ext(service_op_mode, service_src_mode, change_allowed):
114
+ service_control = init_service_control(service_op_mode, service_src_mode)
115
+ check_valid_states(
116
+ service_control,
117
+ service_control.set_procedure_ext,
118
+ change_allowed=change_allowed,
119
+ )
120
+ check_invalid_states(service_control, service_control.set_procedure_ext)
121
+
122
+
123
+ def test_set_procedure_cur():
124
+ service_control = init_service_control("op")
125
+ for proc_id in range(4):
126
+ service_control.set_procedure_op(proc_id)
127
+ service_control.set_procedure_cur()
128
+ assert service_control[sc.PROCEDURE_CUR] == proc_id
129
+
130
+ service_control.set_procedure_op(0)
131
+ service_control.set_procedure_cur()
132
+ service_control.set_procedure_op(10)
133
+ service_control.set_procedure_cur()
134
+ assert service_control[sc.PROCEDURE_CUR] == 0
135
+
136
+
137
+ @pytest.mark.parametrize("command", [sc.COMMAND_OP, sc.COMMAND_EXT])
138
+ def test_command_handshake(command: str) -> None:
139
+ received_values: list[int] = []
140
+ service_control = sc(
141
+ "Dummy",
142
+ execution_procedure=received_values.append,
143
+ )
144
+
145
+ callback = service_control.get_item(command).ext_wrt_cb
146
+ assert callback is not None
147
+ callback(42)
148
+
149
+ assert received_values == [42]
150
+ assert service_control[command] == 0
@@ -0,0 +1,20 @@
1
+ """Base Class for Service Elements."""
2
+
3
+ __copyright__ = (
4
+ "Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
5
+ )
6
+ __license__ = "MIT"
7
+
8
+ from mtppy.part3.data_assembly_set.data_assembly import DataAssembly
9
+ from mtppy.part3.data_assembly_set.base_functions.wqc import WQCBaseFunction
10
+
11
+
12
+ class ServiceElement(DataAssembly):
13
+
14
+ wqc: WQCBaseFunction
15
+
16
+ def __init__(self, tag_name: str, tag_description: str):
17
+ super().__init__(tag_name, tag_description)
18
+ self.mtpObjectType = "MTPDataAssemblySUCLib/DataAssembly/ServiceElement"
19
+
20
+ self.wqc = WQCBaseFunction()