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,189 @@
1
+ """Implementation of the Source Mode Base Function.
2
+
3
+ This module implements the Source Mode Base Function as specified in PNO MTP 2.0
4
+ Part 3, Section 8.2.2.3. The SourceMode class manages the source mode states
5
+ (SrcChannel, SrcIntOp, SrcIntAut, SrcIntAct, SrcManOp, SrcManAut, SrcManAct)
6
+ and provides methods to control the source mode transitions.
7
+ """
8
+
9
+ __copyright__ = (
10
+ "Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
11
+ )
12
+ __license__ = "MIT"
13
+
14
+ import typing
15
+ from typing import ClassVar
16
+
17
+ from mtppy.utils.logging import logger
18
+ from mtppy.utils.data_item import DataItem, DataItemCollection
19
+ from mtppy.part3.definitions.mtp_access import MTPAccess
20
+ from mtppy.part5.definitions.w3c_xml_data_types import W3cXmlDataTypes
21
+
22
+
23
+ class SourceMode(DataItemCollection):
24
+ """Implements the operation and source modes BaseFunction.
25
+
26
+ This class manages the source mode states and transitions according to
27
+ the MTP 2.0 specification. It handles the interaction between different
28
+ source mode components like channel, internal and manual modes.
29
+ """
30
+
31
+ SRC_CHANNEL = "SrcChannel"
32
+ SRC_INT_OP = "SrcIntOp"
33
+ SRC_INT_AUT = "SrcIntAut"
34
+ SRC_INT_ACT = "SrcIntAct"
35
+ SRC_MAN_OP = "SrcManOp"
36
+ SRC_MAN_AUT = "SrcManAut"
37
+ SRC_MAN_ACT = "SrcManAct"
38
+
39
+ CLASS_DATA_ITEMS: ClassVar = {
40
+ SRC_CHANNEL: DataItem(
41
+ SRC_CHANNEL,
42
+ W3cXmlDataTypes.BOOLEAN,
43
+ MTPAccess.ReadAccess,
44
+ init_value=False,
45
+ description="SourceMode channel - 0: operator (*Op) shall be used, 1: automatic (*Aut) shall be used.",
46
+ ),
47
+ SRC_INT_OP: DataItem(
48
+ SRC_INT_OP,
49
+ W3cXmlDataTypes.BOOLEAN,
50
+ MTPAccess.ReadWriteAccess,
51
+ init_value=False,
52
+ description="Request SourceMode to Internal by operator (if SrcChannel is false) - 0: no operation, 1: request internal.",
53
+ ),
54
+ SRC_INT_AUT: DataItem(
55
+ SRC_INT_AUT,
56
+ W3cXmlDataTypes.BOOLEAN,
57
+ MTPAccess.ReadAccess,
58
+ init_value=False,
59
+ description="Request SourceMode to Internal by automatic (if SrcChannel is true) - 0: no operation, 1: request internal",
60
+ ),
61
+ SRC_INT_ACT: DataItem(
62
+ SRC_INT_ACT,
63
+ W3cXmlDataTypes.BOOLEAN,
64
+ MTPAccess.ReadAccess,
65
+ init_value=False,
66
+ description="Indicator for Internal Mode - 0: internal mode inactive, 1: internal mode active.",
67
+ ),
68
+ SRC_MAN_OP: DataItem(
69
+ SRC_MAN_OP,
70
+ W3cXmlDataTypes.BOOLEAN,
71
+ MTPAccess.ReadWriteAccess,
72
+ init_value=False,
73
+ description="Request SourceMode to Manual by operator (if SrcChannel is false) - 0: no operation, 1: request manual.",
74
+ ),
75
+ SRC_MAN_AUT: DataItem(
76
+ SRC_MAN_AUT,
77
+ W3cXmlDataTypes.BOOLEAN,
78
+ MTPAccess.ReadAccess,
79
+ init_value=False,
80
+ description="Request SourceMode to Manual by automatic (if SrcChannel is true) - 0: no operation, 1: request manual.",
81
+ ),
82
+ SRC_MAN_ACT: DataItem(
83
+ SRC_MAN_ACT,
84
+ W3cXmlDataTypes.BOOLEAN,
85
+ MTPAccess.ReadAccess,
86
+ init_value=False,
87
+ ),
88
+ }
89
+
90
+ def __init__(self) -> None:
91
+ """Initialize the SourceMode instance.
92
+
93
+ The data items defined CLASS_DATA_ITEMS are set up by the
94
+ DataItemCollection. This class registers the callback functions for
95
+ automatic and manual operation mode changes.
96
+
97
+ """
98
+ super().__init__()
99
+ # add callbacks to ReadWrit-e and WriteAccess variables
100
+ self.get_item(SourceMode.SRC_INT_OP).ext_wrt_cb = self.set_src_int_op
101
+ self.get_item(SourceMode.SRC_MAN_OP).ext_wrt_cb = self.set_src_man_op
102
+
103
+ self.get_item(SourceMode.SRC_MAN_AUT).int_wrt_cb = self.set_src_man_aut
104
+ self.get_item(SourceMode.SRC_INT_AUT).int_wrt_cb = self.set_src_int_aut
105
+
106
+ logger.debug("SourceMode initialized")
107
+
108
+ def _src_to_off(self) -> None:
109
+ self[SourceMode.SRC_INT_ACT] = False
110
+ self[SourceMode.SRC_MAN_ACT] = False
111
+ logger.mtp("SourceMode is now off")
112
+
113
+ def _src_to_int(self) -> None:
114
+ self[SourceMode.SRC_INT_ACT] = True
115
+ self[SourceMode.SRC_MAN_ACT] = False
116
+ logger.mtp("SourceMode is now int")
117
+
118
+ def _src_to_man(self) -> None:
119
+ self[SourceMode.SRC_INT_ACT] = False
120
+ self[SourceMode.SRC_MAN_ACT] = True
121
+ logger.mtp("SourceMode is now man")
122
+
123
+ @typing.override
124
+ def set_src_channel(self, value: bool) -> None:
125
+ """Set the source channel value.
126
+
127
+ Args:
128
+ value: The boolean value to set for the source channel.
129
+
130
+ """
131
+ self[SourceMode.SRC_CHANNEL] = value
132
+ logger.mtp(
133
+ f"SrcChannel is now {self[SourceMode.SRC_CHANNEL]}",
134
+ )
135
+
136
+ def set_src_man_aut(self, value: bool) -> None:
137
+ """Set the manual automatic mode.
138
+
139
+ If the source channel is active and the value is True, transitions to
140
+ manual mode.
141
+
142
+ Args:
143
+ value: The boolean value to set for the manual automatic mode.
144
+
145
+ """
146
+ if self[SourceMode.SRC_CHANNEL] and value:
147
+ self._src_to_man()
148
+
149
+ def set_src_man_op(self, value: bool) -> None:
150
+ """Set the manual operation mode.
151
+
152
+ If the source channel is not active and the value is True, transitions to
153
+ manual mode. Also resets the manual operation mode to False.
154
+
155
+ Args:
156
+ value: The boolean value to set for the manual operation mode.
157
+
158
+ """
159
+ if not self[SourceMode.SRC_CHANNEL] and value:
160
+ self._src_to_man()
161
+ self[SourceMode.SRC_MAN_OP] = False
162
+
163
+ def set_src_int_aut(self, value: bool) -> None:
164
+ """Set the internal automatic mode.
165
+
166
+ If the source channel is active and the value is True, transitions to
167
+ internal mode.
168
+
169
+ Args:
170
+ value: The boolean value to set for the internal automatic mode.
171
+
172
+ """
173
+ if self[SourceMode.SRC_CHANNEL] and value:
174
+ self._src_to_int()
175
+
176
+ def set_src_int_op(self, value: bool) -> None:
177
+ """Set the internal operation mode.
178
+
179
+ If the source channel is not active and the value is True, transitions to
180
+ internal mode. Also resets the internal operation mode to False to prevent
181
+ infinite loops.
182
+
183
+ Args:
184
+ value: The boolean value to set for the internal operation mode.
185
+
186
+ """
187
+ if not self[SourceMode.SRC_CHANNEL] and value:
188
+ self._src_to_int() # already done, setting again results in infinite loop
189
+ self[SourceMode.SRC_INT_OP] = False
@@ -0,0 +1,112 @@
1
+ """Test Suite for SourceMode."""
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.part3.data_assembly_set.base_functions.source_mode import SourceMode
11
+
12
+
13
+ def init_source_mode(init_mode="int", src_channel=False):
14
+ src_mode = SourceMode()
15
+ src_mode.set_src_channel(src_channel)
16
+
17
+ if init_mode == "man":
18
+ src_mode[SourceMode.SRC_MAN_ACT] = True
19
+ elif init_mode == "int":
20
+ src_mode[SourceMode.SRC_INT_ACT] = True
21
+ else:
22
+ raise ValueError(f"Source mode {init_mode} is unknown")
23
+
24
+ return src_mode
25
+
26
+
27
+ def source_mode_is_in_specific_mode(
28
+ src_mode, source_channel_assertion: bool, mode_assertion: str
29
+ ):
30
+ return all(
31
+ [
32
+ src_mode[SourceMode.SRC_CHANNEL] == source_channel_assertion,
33
+ src_mode[SourceMode.SRC_MAN_AUT] == False,
34
+ src_mode[SourceMode.SRC_INT_OP] == False,
35
+ src_mode[SourceMode.SRC_INT_AUT] == False,
36
+ src_mode[SourceMode.SRC_MAN_OP] == False,
37
+ src_mode[SourceMode.SRC_INT_ACT] == (mode_assertion == "int"),
38
+ src_mode[SourceMode.SRC_MAN_ACT] == (mode_assertion == "man"),
39
+ ]
40
+ )
41
+
42
+
43
+ def test_initial_state():
44
+ # Initial mode
45
+ src_mode = init_source_mode(init_mode="int", src_channel=False)
46
+ assert source_mode_is_in_specific_mode(src_mode, False, "int") == True
47
+
48
+
49
+ def test_source_channel_change():
50
+ # Initial mode
51
+ src_mode = init_source_mode()
52
+ # Trigger channel change
53
+ src_mode[SourceMode.SRC_CHANNEL] = True
54
+ assert source_mode_is_in_specific_mode(src_mode, True, "int") == True
55
+ # Trigger channel change back
56
+ src_mode[SourceMode.SRC_CHANNEL] = False
57
+ assert source_mode_is_in_specific_mode(src_mode, False, "int") == True
58
+
59
+
60
+ def test_int_to_ext_src_channel_false():
61
+ # Initial mode
62
+ src_mode = init_source_mode()
63
+ assert source_mode_is_in_specific_mode(src_mode, False, "int") == True
64
+ # Trigger mode transition
65
+ src_mode.get_item(SourceMode.SRC_MAN_OP).ext_wrt_cb(True)
66
+ assert source_mode_is_in_specific_mode(src_mode, False, "man") == True
67
+
68
+
69
+ def test_int_to_ext_src_channel_true():
70
+ # Initial mode
71
+ src_mode = init_source_mode(src_channel=True)
72
+ assert source_mode_is_in_specific_mode(src_mode, True, "int") == True
73
+ # Trigger mode transition
74
+ src_mode.SrcManAut = True
75
+ assert source_mode_is_in_specific_mode(src_mode, True, "man") == True
76
+
77
+
78
+ def test_ext_to_int_src_channel_false():
79
+ # Initial mode
80
+ src_mode = init_source_mode(init_mode="man", src_channel=False)
81
+ assert source_mode_is_in_specific_mode(src_mode, False, "man") == True
82
+ # Trigger mode transition
83
+ src_mode.get_item(SourceMode.SRC_INT_OP).ext_wrt_cb(True)
84
+ assert source_mode_is_in_specific_mode(src_mode, False, "int") == True
85
+
86
+
87
+ def test_ext_to_int_src_channel_true():
88
+ # Initial mode
89
+ src_mode = init_source_mode(init_mode="man", src_channel=True)
90
+ assert source_mode_is_in_specific_mode(src_mode, True, "man") == True
91
+ # Trigger mode transition
92
+ src_mode.SrcIntAut = True
93
+ assert source_mode_is_in_specific_mode(src_mode, True, "int") == True
94
+
95
+
96
+ @pytest.mark.parametrize(
97
+ "state_request",
98
+ [SourceMode.SRC_INT_OP, SourceMode.SRC_MAN_OP],
99
+ )
100
+ def test_external_state_request_handshake(state_request: str) -> None:
101
+ src_mode = init_source_mode()
102
+
103
+ callback = src_mode.get_item(state_request).ext_wrt_cb
104
+ assert callback is not None
105
+ callback(True)
106
+
107
+ assert src_mode[state_request] is False
108
+
109
+
110
+ if __name__ == "__main__":
111
+ # This allows running the tests directly if needed
112
+ pytest.main([__file__, "-v"])
@@ -0,0 +1,75 @@
1
+ """Implementation of the WQC Base Function (PNO MTP 2.0 Part 3, Section 8.3.2).
2
+
3
+ The WQC (Worst Quality Control) base function provides a foundation for
4
+ write quality control operations. The base function provides the setter
5
+ `set_wqc` for internal writes that simply sets the value. This setter is
6
+ meant to be overwritten by derived classes with something semantically meaningful.
7
+
8
+ Note:
9
+ If no quality code is available 255 (=unspecified WQC) shall be used.
10
+
11
+ Example:
12
+ ```python
13
+ wqc = WQCBaseFunction()
14
+ wqc.set_wqc(10)
15
+ ```
16
+
17
+ Attributes:
18
+ WQC_: Class constant representing the WQC alias of the PNO MTP 2.0 spec.
19
+ """
20
+
21
+ __copyright__ = (
22
+ "Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
23
+ )
24
+ __license__ = "MIT"
25
+
26
+ from typing import ClassVar
27
+
28
+ import utils.type_conversion as tc
29
+ from mtppy.utils.data_item import DataItem, DataItemCollection
30
+ from mtppy.part3.definitions.mtp_access import MTPAccess
31
+ from mtppy.part5.definitions.w3c_xml_data_types import W3cXmlDataTypes
32
+
33
+
34
+ class WQCBaseFunction(DataItemCollection):
35
+ """Base function for Write Quality Control operations.
36
+
37
+ This class implements the basic WQC functionality that can be extended
38
+ by derived classes to provide specific write quality control behaviors.
39
+
40
+ Defines:
41
+ WQC_: Class constant representing the WQC data item name.
42
+ """
43
+
44
+ WQC_ = "WQC"
45
+
46
+ CLASS_DATA_ITEMS: ClassVar = {
47
+ WQC_: DataItem(
48
+ WQC_,
49
+ W3cXmlDataTypes.UNSIGNED_BYTE,
50
+ MTPAccess.ReadAccess,
51
+ init_value=255,
52
+ description="provides information about the conditions under \
53
+ which a value was determined.",
54
+ ),
55
+ }
56
+
57
+ def __init__(self) -> None:
58
+ """Initialize the WQC BaseFunction.
59
+
60
+ Sets up the WQC data item with the appropriate write callback.
61
+ """
62
+ super().__init__()
63
+
64
+ self.get_item(WQCBaseFunction.WQC_).int_wrt_cb = self.set_wqc
65
+
66
+ def set_wqc(self, value: tc.ANY_TYPE) -> None:
67
+ """Set the WQC value.
68
+
69
+ This method is intended to be overridden by derived classes to
70
+ provide specific write quality control behavior.
71
+
72
+ Args:
73
+ value: The value to set for the WQC data item.
74
+ """
75
+ self[WQCBaseFunction.WQC_] = value
@@ -0,0 +1,86 @@
1
+ """Test suite for the WQC (Work Quality Control) module."""
2
+
3
+ __copyright__ = (
4
+ "Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
5
+ )
6
+ __license__ = "MIT"
7
+
8
+ import sys
9
+ import os
10
+ import pytest
11
+
12
+ # Add the src directory to the Python path to import modules
13
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src"))
14
+
15
+
16
+ def test_wqc_module_import():
17
+ """Test that the WQC module can be imported successfully."""
18
+ try:
19
+ from mtppy.part3.data_assembly_set.base_functions.wqc import WQCBaseFunction
20
+
21
+ assert True
22
+ except ImportError as e:
23
+ assert False, f"Failed to import WQC module: {e}"
24
+
25
+
26
+ def test_wqc_base_function_exists():
27
+ """Test that WQCBaseFunction class exists."""
28
+ try:
29
+ from mtppy.part3.data_assembly_set.base_functions.wqc import WQCBaseFunction
30
+
31
+ assert WQCBaseFunction is not None
32
+ assert callable(WQCBaseFunction)
33
+ except ImportError as e:
34
+ pytest.fail(f"Failed to import WQCBaseFunction: {e}")
35
+
36
+
37
+ def test_wqc_base_function_inheritance():
38
+ """Test that WQCBaseFunction has correct inheritance."""
39
+ try:
40
+ from mtppy.part3.data_assembly_set.base_functions.wqc import WQCBaseFunction
41
+
42
+ # Check if it's a class
43
+ assert isinstance(WQCBaseFunction, type)
44
+
45
+ # Check that it has expected attributes
46
+ assert hasattr(WQCBaseFunction, "__doc__")
47
+
48
+ except ImportError as e:
49
+ pytest.fail(f"Failed to test WQCBaseFunction inheritance: {e}")
50
+
51
+
52
+ def test_wqc_functionality():
53
+ """Test basic WQC functionality."""
54
+ try:
55
+ from mtppy.part3.data_assembly_set.base_functions.wqc import WQCBaseFunction
56
+
57
+ # Try to instantiate the class
58
+ wqc_instance = WQCBaseFunction()
59
+
60
+ # Basic assertions
61
+ assert wqc_instance is not None
62
+ assert hasattr(wqc_instance, "__class__")
63
+
64
+ except Exception as e:
65
+ pytest.fail(f"Failed to test WQC functionality: {e}")
66
+
67
+
68
+ def test_wqc_snake_case_api():
69
+ """Test basic WQC functionality."""
70
+ try:
71
+ from mtppy.part3.data_assembly_set.base_functions.wqc import WQCBaseFunction
72
+
73
+ # Try to instantiate the class
74
+ wqc_instance = WQCBaseFunction()
75
+
76
+ for wqc in range(0, 100, 10):
77
+ wqc_instance.WQC = wqc
78
+ assert wqc_instance.WQC == wqc
79
+
80
+ except Exception as e:
81
+ pytest.fail(f"Failed to test WQC functionality: {e}")
82
+
83
+
84
+ if __name__ == "__main__":
85
+ # This allows running the tests directly if needed
86
+ pytest.main([__file__, "-v"])
@@ -0,0 +1,65 @@
1
+ """Implementation of the DataAssembly (PNO MTP 2.0 Part 1, 5.3)."""
2
+
3
+ __copyright__ = (
4
+ "Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
5
+ )
6
+ __license__ = "MIT"
7
+
8
+ from typing import ClassVar
9
+
10
+ from mtppy.utils.data_item import DataItem, DataItemCollection
11
+ from mtppy.part3.definitions.mtp_access import MTPAccess
12
+ from mtppy.part5.definitions.w3c_xml_data_types import W3cXmlDataTypes
13
+
14
+
15
+ class DataAssembly(DataItemCollection):
16
+ """DataAssembly is the base class for all the DataAssemblies that
17
+ serve as interface between POL and PEA and are specified in
18
+ PNO MTP 2.0 Part 3 (Library) and PNO MTP 2.0 Part 4 (Services).
19
+
20
+ It defines the aliases ``TagName`` and ``TagDescription``
21
+
22
+ DataAssembly itself is quite simple, the heavy lifting of providing
23
+ simple pythonic access paths (dict, attribute, setter/getter)
24
+ to the elements and managing the callback logic on change requests is
25
+ done by the baseclass [DataItemCollection][mtppy.utils.data_item.DataItemCollection].
26
+ """
27
+
28
+ TAG_NAME = "TagName"
29
+ TAG_DESCRIPTION = "TagDescription"
30
+
31
+ CLASS_DATA_ITEMS: ClassVar = {
32
+ TAG_NAME: DataItem(
33
+ TAG_NAME,
34
+ W3cXmlDataTypes.STRING,
35
+ MTPAccess.ReadAccess,
36
+ init_value="untagged",
37
+ ),
38
+ TAG_DESCRIPTION: DataItem(
39
+ TAG_DESCRIPTION,
40
+ W3cXmlDataTypes.STRING,
41
+ MTPAccess.ReadAccess,
42
+ init_value="undescribed",
43
+ ),
44
+ }
45
+
46
+ # class attributes
47
+ mtpObjectType: str
48
+ tag_name: str
49
+ tag_description: str
50
+
51
+ def __init__(self, tag_name: str, tag_description: str = "") -> None:
52
+ """Initialize the DataAssembly.
53
+
54
+ Arguments:
55
+ tag_name: value for ``TagName`` (internal variable tag_name)
56
+ tag_description: value for ``TagDescription`` (internal variable
57
+ tag_description)
58
+ """
59
+ super().__init__()
60
+ self.mtpObjectType = "MTPDataObjectSUCLib/DataAssembly"
61
+ self[DataAssembly.TAG_NAME] = tag_name
62
+ self[DataAssembly.TAG_DESCRIPTION] = tag_description
63
+
64
+ self.tag_name = tag_name
65
+ self.tag_description = tag_description
@@ -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,19 @@
1
+ """Base Class for Diagnostic Elements."""
2
+
3
+ __copyright__ = (
4
+ "Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
5
+ )
6
+ __license__ = "MIT"
7
+ from mtppy.part3.data_assembly_set.data_assembly import DataAssembly
8
+ from mtppy.part3.data_assembly_set.base_functions.wqc import WQCBaseFunction
9
+
10
+
11
+ class DiagnosticElement(DataAssembly):
12
+
13
+ wqc: WQCBaseFunction
14
+
15
+ def __init__(self, tag_name: str, tag_description: str):
16
+ super().__init__(tag_name, tag_description)
17
+ self.mtpObjectType = "MTPDataObjectSUCLib/DataAssembly/DiagnosticElement"
18
+
19
+ self.wqc = WQCBaseFunction()
@@ -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,22 @@
1
+ """Base Class for Indicators."""
2
+
3
+ __copyright__ = (
4
+ "Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
5
+ )
6
+ __license__ = "MIT"
7
+ from mtppy.part3.data_assembly_set.data_assembly import DataAssembly
8
+ from mtppy.part3.data_assembly_set.base_functions.oslevel import OSLevelBaseFunction
9
+ from mtppy.part3.data_assembly_set.base_functions.wqc import WQCBaseFunction
10
+
11
+
12
+ class IndicatorElement(DataAssembly):
13
+
14
+ os_level: OSLevelBaseFunction
15
+ wqc: WQCBaseFunction
16
+
17
+ def __init__(self, tag_name: str, tag_description: str):
18
+ super().__init__(tag_name, tag_description)
19
+ self.mtpObjectType = "MTPDataAssemblySUCLib/DataAssembly/IndicatorElement"
20
+
21
+ self.os_level = OSLevelBaseFunction()
22
+ self.wqc = WQCBaseFunction()
@@ -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,68 @@
1
+ """Implementation of AnaView"""
2
+
3
+ __copyright__ = (
4
+ "Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
5
+ )
6
+ __license__ = "MIT"
7
+
8
+ from typing import ClassVar
9
+
10
+ from mtppy.utils.logging import logger
11
+ from mtppy.utils.data_item import DataItem
12
+ from mtppy.part3.definitions.mtp_access import MTPAccess
13
+ from mtppy.part5.definitions.w3c_xml_data_types import W3cXmlDataTypes
14
+ from mtppy.part3.data_assembly_set.indicator_elements.indicator_element import (
15
+ IndicatorElement,
16
+ )
17
+
18
+
19
+ class AnaView(IndicatorElement):
20
+ # DataItem constant definitions
21
+ V_ = "V"
22
+ V_UNIT = "VUnit"
23
+ VSCL_MIN = "VSclMin"
24
+ VSCL_MAX = "VSclMax"
25
+
26
+ # Class-level data items dictionary
27
+ CLASS_DATA_ITEMS: ClassVar = {
28
+ V_: DataItem(V_, W3cXmlDataTypes.FLOAT, MTPAccess.ReadAccess, 0),
29
+ V_UNIT: DataItem(V_UNIT, W3cXmlDataTypes.SHORT, MTPAccess.ReadAccess, None),
30
+ VSCL_MIN: DataItem(VSCL_MIN, W3cXmlDataTypes.FLOAT, MTPAccess.ReadAccess, None),
31
+ VSCL_MAX: DataItem(VSCL_MAX, W3cXmlDataTypes.FLOAT, MTPAccess.ReadAccess, None),
32
+ }
33
+
34
+ v_scl_min: float
35
+ v_scl_max: float
36
+ v_unit: int
37
+
38
+ def __init__(
39
+ self,
40
+ tag_name: str,
41
+ tag_description: str = "",
42
+ v_scl_min: float = 0,
43
+ v_scl_max: float = 100,
44
+ v_unit: int = 1998,
45
+ ):
46
+ """
47
+ Analog View (AnaView).
48
+ """
49
+ super().__init__(tag_name, tag_description)
50
+ self.mtpObjectType = (
51
+ "MTPDataAssemblySUCLib/DataAssembly/IndicatorElement/AnaView"
52
+ )
53
+
54
+ self.v_scl_min = v_scl_min
55
+ self.v_scl_max = v_scl_max
56
+ self.v_unit = v_unit
57
+
58
+ # Initialize data items with dynamic values (using class-defined data items)
59
+ self[AnaView.V_] = 0
60
+ self[AnaView.V_UNIT] = self.v_unit
61
+ self[AnaView.VSCL_MIN] = self.v_scl_min
62
+ self[AnaView.VSCL_MAX] = self.v_scl_max
63
+
64
+ # Add callbacks for ReadWrite Variables
65
+
66
+ def set_v(self, value):
67
+ self[AnaView.V_] = value
68
+ logger.mtp(f"V set to {value}")