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,138 @@
1
+ """Implementation of the Service Source (Selection) Mode as specified in
2
+ PNO MTP 2.0 Part 4, Section 8.2.2.3.
3
+ """
4
+
5
+ __copyright__ = (
6
+ "Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
7
+ )
8
+ __license__ = "MIT"
9
+
10
+ import typing
11
+ from typing import ClassVar
12
+
13
+ from mtppy.utils.logging import logger
14
+ from mtppy.utils.data_item import DataItem, DataItemCollection
15
+ from mtppy.part3.definitions.mtp_access import MTPAccess
16
+ from mtppy.part5.definitions.w3c_xml_data_types import W3cXmlDataTypes
17
+
18
+
19
+ class ServiceSourceMode(DataItemCollection):
20
+ """The Service Source Selection Mode is a concept that allows the service to select
21
+ its source of operation based on the state of the service and the configuration
22
+ parameters.
23
+
24
+ It is used to determine whether the service should operate in internal or external
25
+ mode, and whether it should be in automatic or manual mode.
26
+
27
+ - Manual Operations: Operator actions are conveyed via the `Op` variables.
28
+ - Automatic Operation: PEA-internal actions made visible via the `Aut` variables.
29
+ - Actual Operation: The actual state of the service is shown in the `Act`
30
+ variables.
31
+
32
+ Note: The State Variables StateChannel, StateIntOp, a.s.o. are implemented in class
33
+ ServiceOperationMode.
34
+ """
35
+
36
+ SRC_CHANNEL = "SrcChannel"
37
+ SRC_EXT_AUT = "SrcExtAut"
38
+ SRC_INT_OP = "SrcIntOp"
39
+ SRC_INT_AUT = "SrcIntAut"
40
+ SRC_EXT_OP = "SrcExtOp"
41
+ SRC_INT_ACT = "SrcIntAct"
42
+ SRC_EXT_ACT = "SrcExtAct"
43
+
44
+ CLASS_DATA_ITEMS: ClassVar = {
45
+ SRC_CHANNEL: DataItem(
46
+ SRC_CHANNEL,
47
+ W3cXmlDataTypes.BOOLEAN,
48
+ MTPAccess.ReadAccess,
49
+ init_value=False,
50
+ ),
51
+ SRC_EXT_AUT: DataItem(
52
+ SRC_EXT_AUT,
53
+ W3cXmlDataTypes.BOOLEAN,
54
+ MTPAccess.ReadAccess,
55
+ init_value=False,
56
+ ),
57
+ SRC_INT_OP: DataItem(
58
+ SRC_INT_OP,
59
+ W3cXmlDataTypes.BOOLEAN,
60
+ MTPAccess.ReadWriteAccess,
61
+ init_value=False,
62
+ ),
63
+ SRC_INT_AUT: DataItem(
64
+ SRC_INT_AUT,
65
+ W3cXmlDataTypes.BOOLEAN,
66
+ MTPAccess.ReadAccess,
67
+ init_value=False,
68
+ ),
69
+ SRC_EXT_OP: DataItem(
70
+ SRC_EXT_OP,
71
+ W3cXmlDataTypes.BOOLEAN,
72
+ MTPAccess.ReadWriteAccess,
73
+ init_value=False,
74
+ ),
75
+ SRC_INT_ACT: DataItem(
76
+ SRC_INT_ACT,
77
+ W3cXmlDataTypes.BOOLEAN,
78
+ MTPAccess.ReadAccess,
79
+ init_value=False,
80
+ ),
81
+ SRC_EXT_ACT: DataItem(
82
+ SRC_EXT_ACT,
83
+ W3cXmlDataTypes.BOOLEAN,
84
+ MTPAccess.ReadAccess,
85
+ init_value=False,
86
+ ),
87
+ }
88
+
89
+ def __init__(self) -> None:
90
+ """Assign callback functions after parent had set up internal structures."""
91
+ super().__init__()
92
+ self.get_item(ServiceSourceMode.SRC_INT_OP).ext_wrt_cb = self.set_src_int_op
93
+ self.get_item(ServiceSourceMode.SRC_EXT_OP).ext_wrt_cb = self.set_src_ext_op
94
+
95
+ self.get_item(ServiceSourceMode.SRC_INT_AUT).int_wrt_cb = self.set_src_int_aut
96
+ self.get_item(ServiceSourceMode.SRC_EXT_AUT).int_wrt_cb = self.set_src_ext_aut
97
+
98
+ def _src_to_off(self) -> None:
99
+ self[ServiceSourceMode.SRC_INT_ACT] = False
100
+ self[ServiceSourceMode.SRC_EXT_ACT] = False
101
+ logger.mtp("ServiceSourceMode is now off")
102
+
103
+ def _src_to_int(self) -> None:
104
+ self[ServiceSourceMode.SRC_INT_ACT] = True
105
+ self[ServiceSourceMode.SRC_EXT_ACT] = False
106
+ logger.mtp("ServiceSourceMode is now int")
107
+
108
+ def _src_to_ext(self) -> None:
109
+ self[ServiceSourceMode.SRC_INT_ACT] = False
110
+ self[ServiceSourceMode.SRC_EXT_ACT] = True
111
+ logger.mtp("ServiceSourceMode is now ext")
112
+
113
+ @typing.override
114
+ def set_src_channel(self, value: bool) -> None:
115
+ self[ServiceSourceMode.SRC_CHANNEL] = value
116
+ logger.mtp(f"ServiceSourceMode SrcChannel is now {value}")
117
+
118
+ @typing.override
119
+ def set_src_ext_aut(self, value: bool) -> None:
120
+ if self[ServiceSourceMode.SRC_CHANNEL] and value:
121
+ self._src_to_ext()
122
+
123
+ @typing.override
124
+ def set_src_ext_op(self, value: bool) -> None:
125
+ if not self[ServiceSourceMode.SRC_CHANNEL] and value:
126
+ self._src_to_ext()
127
+ self[ServiceSourceMode.SRC_EXT_OP] = False
128
+
129
+ @typing.override
130
+ def set_src_int_aut(self, value: bool) -> None:
131
+ if self[ServiceSourceMode.SRC_CHANNEL] and value:
132
+ self._src_to_int()
133
+
134
+ @typing.override
135
+ def set_src_int_op(self, value: bool) -> None:
136
+ if not self[ServiceSourceMode.SRC_CHANNEL] and value:
137
+ self._src_to_int()
138
+ self[ServiceSourceMode.SRC_INT_OP] = False
@@ -0,0 +1,179 @@
1
+ """Test Suite for ServiceSourceMode."""
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_source_mode import (
11
+ ServiceSourceMode,
12
+ )
13
+
14
+
15
+ def init_service_source_mode(init_mode="int", src_channel=False):
16
+ service_src_mode = ServiceSourceMode()
17
+ service_src_mode.set_src_channel(src_channel)
18
+
19
+ if init_mode == "int":
20
+ service_src_mode[ServiceSourceMode.SRC_INT_ACT] = True
21
+ service_src_mode[ServiceSourceMode.SRC_EXT_ACT] = False
22
+ elif init_mode == "ext":
23
+ service_src_mode[ServiceSourceMode.SRC_INT_ACT] = False
24
+ service_src_mode[ServiceSourceMode.SRC_EXT_ACT] = True
25
+ else:
26
+ raise ValueError(f"ServiceSourceMode {init_mode} is unknown")
27
+
28
+ return service_src_mode
29
+
30
+
31
+ def service_source_mode_is_in_specific_mode(
32
+ service_src_mode, source_channel_assertion: bool, mode_assertion: str
33
+ ):
34
+ mode = lambda m: (mode_assertion == m)
35
+ return all(
36
+ [
37
+ service_src_mode[ServiceSourceMode.SRC_CHANNEL] == source_channel_assertion,
38
+ service_src_mode[ServiceSourceMode.SRC_EXT_AUT] == False,
39
+ service_src_mode[ServiceSourceMode.SRC_INT_OP] == False,
40
+ service_src_mode[ServiceSourceMode.SRC_INT_AUT] == False,
41
+ service_src_mode[ServiceSourceMode.SRC_EXT_OP] == False,
42
+ service_src_mode[ServiceSourceMode.SRC_INT_ACT] == mode("int"),
43
+ service_src_mode[ServiceSourceMode.SRC_EXT_ACT] == mode("ext"),
44
+ ]
45
+ )
46
+
47
+
48
+ def test_initial_state():
49
+ # Initial mode
50
+ service_src_mode = init_service_source_mode(init_mode="int", src_channel=False)
51
+ assert service_source_mode_is_in_specific_mode(service_src_mode, False, "int")
52
+
53
+
54
+ def test_source_channel_change():
55
+ # Initial mode
56
+ service_src_mode = init_service_source_mode()
57
+ assert service_source_mode_is_in_specific_mode(service_src_mode, False, "int")
58
+ # Trigger channel change
59
+ service_src_mode[ServiceSourceMode.SRC_CHANNEL] = True
60
+ assert service_source_mode_is_in_specific_mode(service_src_mode, True, "int")
61
+
62
+ # Trigger channel change back
63
+ service_src_mode[ServiceSourceMode.SRC_CHANNEL] = False
64
+ assert service_source_mode_is_in_specific_mode(service_src_mode, False, "int")
65
+
66
+
67
+ def test_int_to_ext_src_channel_false():
68
+ # Initial mode
69
+ service_src_mode = init_service_source_mode(init_mode="int", src_channel=False)
70
+ assert (
71
+ service_source_mode_is_in_specific_mode(service_src_mode, False, "int") == True
72
+ )
73
+ # Trigger mode transition
74
+ service_src_mode.get_item(ServiceSourceMode.SRC_EXT_OP).ext_wrt_cb(True)
75
+ assert (
76
+ service_source_mode_is_in_specific_mode(service_src_mode, False, "ext") == True
77
+ )
78
+
79
+
80
+ def test_int_to_ext_src_channel_true():
81
+ # Initial mode
82
+ service_src_mode = init_service_source_mode(init_mode="int", src_channel=True)
83
+ assert (
84
+ service_source_mode_is_in_specific_mode(service_src_mode, True, "int") == True
85
+ )
86
+ # Trigger mode transition
87
+ service_src_mode.set_src_ext_aut(True)
88
+ assert (
89
+ service_source_mode_is_in_specific_mode(service_src_mode, True, "ext") == True
90
+ )
91
+
92
+
93
+ def test_ext_to_int_src_channel_false():
94
+ # Initial mode
95
+ service_src_mode = init_service_source_mode(init_mode="ext", src_channel=False)
96
+ assert (
97
+ service_source_mode_is_in_specific_mode(service_src_mode, False, "ext") == True
98
+ )
99
+ # Trigger mode transition
100
+ service_src_mode.get_item(ServiceSourceMode.SRC_INT_OP).ext_wrt_cb(True)
101
+ assert (
102
+ service_source_mode_is_in_specific_mode(service_src_mode, False, "int") == True
103
+ )
104
+
105
+
106
+ def test_ext_to_int_src_channel_true():
107
+ # Initial mode
108
+ service_src_mode = init_service_source_mode(init_mode="ext", src_channel=True)
109
+ assert (
110
+ service_source_mode_is_in_specific_mode(service_src_mode, True, "ext") == True
111
+ )
112
+ # Trigger mode transition
113
+ service_src_mode.set_src_int_aut(True)
114
+ assert (
115
+ service_source_mode_is_in_specific_mode(service_src_mode, True, "int") == True
116
+ )
117
+
118
+
119
+ def test_source_mode_can_switch_off():
120
+ service_src_mode = init_service_source_mode(init_mode="ext")
121
+
122
+ service_src_mode._src_to_off()
123
+
124
+ assert service_source_mode_is_in_specific_mode(service_src_mode, False, "off")
125
+
126
+
127
+ @pytest.mark.parametrize(
128
+ ("method_name", "requested_value", "active_state"),
129
+ [
130
+ ("set_src_ext_aut", True, "int"),
131
+ ("set_src_int_aut", True, "ext"),
132
+ ],
133
+ )
134
+ def test_automatic_source_request_is_ignored_without_source_channel(
135
+ method_name, requested_value, active_state
136
+ ):
137
+ service_src_mode = init_service_source_mode(
138
+ init_mode=active_state, src_channel=False
139
+ )
140
+
141
+ getattr(service_src_mode, method_name)(requested_value)
142
+
143
+ assert service_source_mode_is_in_specific_mode(
144
+ service_src_mode, False, active_state
145
+ )
146
+
147
+
148
+ @pytest.mark.parametrize(
149
+ ("method_name", "active_state"),
150
+ [
151
+ ("set_src_ext_op", "int"),
152
+ ("set_src_int_op", "ext"),
153
+ ],
154
+ )
155
+ def test_operator_source_request_is_ignored_with_source_channel(
156
+ method_name, active_state
157
+ ):
158
+ service_src_mode = init_service_source_mode(
159
+ init_mode=active_state, src_channel=True
160
+ )
161
+
162
+ getattr(service_src_mode, method_name)(True)
163
+
164
+ assert service_source_mode_is_in_specific_mode(service_src_mode, True, active_state)
165
+
166
+
167
+ def test_operator_source_requests_are_reset():
168
+ service_src_mode = init_service_source_mode()
169
+
170
+ service_src_mode.set_src_ext_op(False)
171
+ service_src_mode.set_src_int_op(False)
172
+
173
+ assert service_src_mode[ServiceSourceMode.SRC_EXT_OP] is False
174
+ assert service_src_mode[ServiceSourceMode.SRC_INT_OP] is False
175
+
176
+
177
+ def test_invalid_initial_source_mode_is_rejected():
178
+ with pytest.raises(ValueError, match="ServiceSourceMode invalid is unknown"):
179
+ init_service_source_mode(init_mode="invalid")
@@ -0,0 +1,4 @@
1
+ __copyright__ = (
2
+ "Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
3
+ )
4
+ __license__ = "MIT"
@@ -0,0 +1,330 @@
1
+ """CommandEnControl."""
2
+
3
+ __copyright__ = (
4
+ "Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
5
+ )
6
+ __license__ = "MIT"
7
+
8
+ import logging
9
+
10
+
11
+ class CommandEnControl:
12
+ def __init__(self) -> None:
13
+ """Instantiate Command enable control."""
14
+ self.command_en = {
15
+ "undefined": {"default": False, "value": False, "bit_no": 0, "int": 1},
16
+ "reset": {"default": False, "value": False, "bit_no": 1, "int": 2},
17
+ "start": {"default": True, "value": True, "bit_no": 2, "int": 4},
18
+ "stop": {"default": True, "value": True, "bit_no": 3, "int": 8},
19
+ "hold": {"default": False, "value": False, "bit_no": 4, "int": 16},
20
+ "unhold": {"default": False, "value": False, "bit_no": 5, "int": 32},
21
+ "pause": {"default": False, "value": False, "bit_no": 6, "int": 64},
22
+ "resume": {"default": False, "value": False, "bit_no": 7, "int": 128},
23
+ "abort": {"default": True, "value": True, "bit_no": 8, "int": 256},
24
+ "restart": {"default": False, "value": False, "bit_no": 9, "int": 512},
25
+ "complete": {"default": False, "value": False, "bit_no": 10, "int": 1024},
26
+ }
27
+ self.hold_enabled = True
28
+ self.pause_enabled = True
29
+ self.restart_enabled = True
30
+
31
+ def set_default(self) -> None:
32
+ """Apply default values to all command enable flags."""
33
+ for command_en in self.command_en:
34
+ self.command_en[command_en]["value"] = self.command_en[command_en][
35
+ "default"
36
+ ]
37
+
38
+ def disable_all(self) -> None:
39
+ """Set False to all command enable flags."""
40
+ for command_en in self.command_en:
41
+ self.command_en[command_en]["value"] = False
42
+
43
+ def is_enabled(self, cmd: str) -> bool | None:
44
+ """Check if a given command is enabled right now.
45
+
46
+ Args:
47
+ cmd: Command to check.
48
+
49
+ Returns:
50
+ Command enable flag state or None.
51
+ """
52
+ if cmd in self.command_en.keys():
53
+ return self.command_en[cmd]["value"]
54
+ else:
55
+ return None
56
+
57
+ def get_command_en(self) -> int:
58
+ """Calculates a sum of all command enable flags.
59
+
60
+ Returns:
61
+ Sum of all command enable flags.
62
+ """
63
+ command_en_sum = 0
64
+ for command in self.command_en.values():
65
+ if command["value"]:
66
+ command_en_sum += command["int"]
67
+ return command_en_sum
68
+
69
+ def set_command_en(self, cmd: str, value: bool):
70
+ """
71
+ Sets a specific command enable to a given value.
72
+
73
+ Args:
74
+ cmd: Command enable flag.
75
+ value: Value to be set.
76
+ """
77
+ if cmd in self.command_en.keys():
78
+ self.command_en[cmd]["value"] = value
79
+
80
+ def enable_pause_loop(self, value: bool):
81
+ """Enable pause loop.
82
+
83
+ Args:
84
+ value: True if the pause loop is shall be enabled and False if disabled.
85
+ """
86
+ self.pause_enabled = value
87
+
88
+ def enable_restart(self, value: bool):
89
+ """Enable restart loop.
90
+
91
+ Args:
92
+ value: True if the restart command shall be enabled and False if disabled.
93
+ """
94
+ self.restart_enabled = value
95
+
96
+ def execute(self, state: str):
97
+ """Execute command enable changes required for a certain state.
98
+
99
+ Args:
100
+ state: Given state.
101
+ """
102
+ logging.debug(f"CommandEn changed to correspond {state}")
103
+ exec(f"self._execute_{state}()")
104
+
105
+ def _execute_undefined(self):
106
+ self.disable_all()
107
+
108
+ def _execute_idle(self):
109
+ self.set_command_en("reset", False)
110
+ self.set_command_en("start", True)
111
+ self.set_command_en("stop", True)
112
+ self.set_command_en("hold", False)
113
+ self.set_command_en("unhold", False)
114
+ if self.pause_enabled:
115
+ self.set_command_en("pause", False)
116
+ self.set_command_en("resume", False)
117
+ self.set_command_en("abort", True)
118
+ if self.restart_enabled:
119
+ self.set_command_en("restart", False)
120
+ self.set_command_en("complete", False)
121
+
122
+ def _execute_starting(self):
123
+ self.set_command_en("reset", False)
124
+ self.set_command_en("start", False)
125
+ self.set_command_en("stop", True)
126
+ self.set_command_en("hold", True)
127
+ self.set_command_en("unhold", False)
128
+ if self.pause_enabled:
129
+ self.set_command_en("pause", False)
130
+ self.set_command_en("resume", False)
131
+ self.set_command_en("abort", True)
132
+ if self.restart_enabled:
133
+ self.set_command_en("restart", False)
134
+ self.set_command_en("complete", True)
135
+
136
+ def _execute_execute(self):
137
+ self.set_command_en("reset", False)
138
+ self.set_command_en("start", False)
139
+ self.set_command_en("stop", True)
140
+ self.set_command_en("hold", True)
141
+ self.set_command_en("unhold", False)
142
+ if self.pause_enabled:
143
+ self.set_command_en("pause", True)
144
+ self.set_command_en("resume", False)
145
+ self.set_command_en("abort", True)
146
+ if self.restart_enabled:
147
+ self.set_command_en("restart", True)
148
+ self.set_command_en("complete", True)
149
+
150
+ def _execute_completing(self):
151
+ self.set_command_en("reset", False)
152
+ self.set_command_en("start", False)
153
+ self.set_command_en("stop", True)
154
+ self.set_command_en("hold", True)
155
+ self.set_command_en("unhold", False)
156
+ if self.pause_enabled:
157
+ self.set_command_en("pause", False)
158
+ self.set_command_en("resume", False)
159
+ self.set_command_en("abort", True)
160
+ if self.restart_enabled:
161
+ self.set_command_en("restart", False)
162
+ self.set_command_en("complete", False)
163
+
164
+ def _execute_completed(self):
165
+ self.set_command_en("reset", True)
166
+ self.set_command_en("start", False)
167
+ self.set_command_en("stop", True)
168
+ self.set_command_en("hold", False)
169
+ self.set_command_en("unhold", False)
170
+ if self.pause_enabled:
171
+ self.set_command_en("pause", False)
172
+ self.set_command_en("resume", False)
173
+ self.set_command_en("abort", True)
174
+ if self.restart_enabled:
175
+ self.set_command_en("restart", False)
176
+ self.set_command_en("complete", False)
177
+
178
+ def _execute_resuming(self):
179
+ self.set_command_en("reset", False)
180
+ self.set_command_en("start", False)
181
+ self.set_command_en("stop", True)
182
+ self.set_command_en("hold", True)
183
+ self.set_command_en("unhold", False)
184
+ if self.pause_enabled:
185
+ self.set_command_en("pause", False)
186
+ self.set_command_en("resume", False)
187
+ self.set_command_en("abort", True)
188
+ if self.restart_enabled:
189
+ self.set_command_en("restart", False)
190
+ self.set_command_en("complete", True)
191
+
192
+ def _execute_paused(self):
193
+ self.set_command_en("reset", False)
194
+ self.set_command_en("start", False)
195
+ self.set_command_en("stop", True)
196
+ self.set_command_en("hold", True)
197
+ self.set_command_en("unhold", False)
198
+ if self.pause_enabled:
199
+ self.set_command_en("pause", False)
200
+ self.set_command_en("resume", True)
201
+ self.set_command_en("abort", True)
202
+ if self.restart_enabled:
203
+ self.set_command_en("restart", False)
204
+ self.set_command_en("complete", True)
205
+
206
+ def _execute_pausing(self):
207
+ self.set_command_en("reset", False)
208
+ self.set_command_en("start", False)
209
+ self.set_command_en("stop", True)
210
+ self.set_command_en("hold", True)
211
+ self.set_command_en("unhold", False)
212
+ if self.pause_enabled:
213
+ self.set_command_en("pause", False)
214
+ self.set_command_en("resume", False)
215
+ self.set_command_en("abort", True)
216
+ if self.restart_enabled:
217
+ self.set_command_en("restart", False)
218
+ self.set_command_en("complete", True)
219
+
220
+ def _execute_holding(self):
221
+ self.set_command_en("reset", False)
222
+ self.set_command_en("start", False)
223
+ self.set_command_en("stop", True)
224
+ self.set_command_en("hold", False)
225
+ self.set_command_en("unhold", False)
226
+ if self.pause_enabled:
227
+ self.set_command_en("pause", False)
228
+ self.set_command_en("resume", False)
229
+ self.set_command_en("abort", True)
230
+ if self.restart_enabled:
231
+ self.set_command_en("restart", False)
232
+ self.set_command_en("complete", False)
233
+
234
+ def _execute_held(self):
235
+ self.set_command_en("reset", False)
236
+ self.set_command_en("start", False)
237
+ self.set_command_en("stop", True)
238
+ self.set_command_en("hold", False)
239
+ self.set_command_en("unhold", True)
240
+ if self.pause_enabled:
241
+ self.set_command_en("pause", False)
242
+ self.set_command_en("resume", False)
243
+ self.set_command_en("abort", True)
244
+ if self.restart_enabled:
245
+ self.set_command_en("restart", False)
246
+ self.set_command_en("complete", False)
247
+
248
+ def _execute_unholding(self):
249
+ self.set_command_en("reset", False)
250
+ self.set_command_en("start", False)
251
+ self.set_command_en("stop", True)
252
+ self.set_command_en("hold", True)
253
+ self.set_command_en("unhold", False)
254
+ if self.pause_enabled:
255
+ self.set_command_en("pause", False)
256
+ self.set_command_en("resume", False)
257
+ self.set_command_en("abort", True)
258
+ if self.restart_enabled:
259
+ self.set_command_en("restart", False)
260
+ self.set_command_en("complete", True)
261
+
262
+ def _execute_stopping(self):
263
+ self.set_command_en("reset", False)
264
+ self.set_command_en("start", False)
265
+ self.set_command_en("stop", False)
266
+ self.set_command_en("hold", False)
267
+ self.set_command_en("unhold", False)
268
+ if self.pause_enabled:
269
+ self.set_command_en("pause", False)
270
+ self.set_command_en("resume", False)
271
+ self.set_command_en("abort", True)
272
+ if self.restart_enabled:
273
+ self.set_command_en("restart", False)
274
+ self.set_command_en("complete", False)
275
+
276
+ def _execute_stopped(self):
277
+ self.set_command_en("reset", True)
278
+ self.set_command_en("start", False)
279
+ self.set_command_en("stop", False)
280
+ self.set_command_en("hold", False)
281
+ self.set_command_en("unhold", False)
282
+ if self.pause_enabled:
283
+ self.set_command_en("pause", False)
284
+ self.set_command_en("resume", False)
285
+ self.set_command_en("abort", True)
286
+ if self.restart_enabled:
287
+ self.set_command_en("restart", False)
288
+ self.set_command_en("complete", False)
289
+
290
+ def _execute_aborting(self):
291
+ self.set_command_en("reset", False)
292
+ self.set_command_en("start", False)
293
+ self.set_command_en("stop", False)
294
+ self.set_command_en("hold", False)
295
+ self.set_command_en("unhold", False)
296
+ if self.pause_enabled:
297
+ self.set_command_en("pause", False)
298
+ self.set_command_en("resume", False)
299
+ self.set_command_en("abort", False)
300
+ if self.restart_enabled:
301
+ self.set_command_en("restart", False)
302
+ self.set_command_en("complete", False)
303
+
304
+ def _execute_aborted(self):
305
+ self.set_command_en("reset", True)
306
+ self.set_command_en("start", False)
307
+ self.set_command_en("stop", False)
308
+ self.set_command_en("hold", False)
309
+ self.set_command_en("unhold", False)
310
+ if self.pause_enabled:
311
+ self.set_command_en("pause", False)
312
+ self.set_command_en("resume", False)
313
+ self.set_command_en("abort", False)
314
+ if self.restart_enabled:
315
+ self.set_command_en("restart", False)
316
+ self.set_command_en("complete", False)
317
+
318
+ def _execute_resetting(self):
319
+ self.set_command_en("reset", False)
320
+ self.set_command_en("start", False)
321
+ self.set_command_en("stop", True)
322
+ self.set_command_en("hold", False)
323
+ self.set_command_en("unhold", False)
324
+ if self.pause_enabled:
325
+ self.set_command_en("pause", False)
326
+ self.set_command_en("resume", False)
327
+ self.set_command_en("abort", True)
328
+ if self.restart_enabled:
329
+ self.set_command_en("restart", False)
330
+ self.set_command_en("complete", False)