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.
- examples/__init__.py +0 -0
- examples/minimal+pid.py +82 -0
- examples/minimal.py +32 -0
- examples/minimal_recipe.py +117 -0
- examples/pea_alarm_base.py +47 -0
- examples/pea_minimal.py +258 -0
- mtppy/__init__.py +1 -0
- mtppy/part1/__init__.py +4 -0
- mtppy/part1/empty_folder_test.py +7 -0
- mtppy/part3/__init__.py +4 -0
- mtppy/part3/data_assembly_set/__init__.py +4 -0
- mtppy/part3/data_assembly_set/active_elements/__init__.py +4 -0
- mtppy/part3/data_assembly_set/active_elements/active_element.py +22 -0
- mtppy/part3/data_assembly_set/active_elements/drives/__init__.py +4 -0
- mtppy/part3/data_assembly_set/active_elements/drives/ana_drv.py +446 -0
- mtppy/part3/data_assembly_set/active_elements/drives/ana_drv_test.py +567 -0
- mtppy/part3/data_assembly_set/active_elements/drives/bin_drv.py +365 -0
- mtppy/part3/data_assembly_set/active_elements/drives/bin_drv_test.py +503 -0
- mtppy/part3/data_assembly_set/active_elements/drives/mon_ana_drv.py +469 -0
- mtppy/part3/data_assembly_set/active_elements/drives/mon_ana_drv_test.py +418 -0
- mtppy/part3/data_assembly_set/active_elements/drives/mon_bin_drv.py +295 -0
- mtppy/part3/data_assembly_set/active_elements/drives/mon_bin_drv_test.py +265 -0
- mtppy/part3/data_assembly_set/active_elements/pid/__init__.py +4 -0
- mtppy/part3/data_assembly_set/active_elements/pid/pid_ctrl.py +333 -0
- mtppy/part3/data_assembly_set/active_elements/pid/pid_ctrl_test.py +156 -0
- mtppy/part3/data_assembly_set/active_elements/valves/__init__.py +4 -0
- mtppy/part3/data_assembly_set/active_elements/valves/ana_vlv.py +402 -0
- mtppy/part3/data_assembly_set/active_elements/valves/ana_vlv_test.py +732 -0
- mtppy/part3/data_assembly_set/active_elements/valves/bin_vlv.py +279 -0
- mtppy/part3/data_assembly_set/active_elements/valves/bin_vlv_test.py +585 -0
- mtppy/part3/data_assembly_set/active_elements/valves/mon_ana_vlv.py +402 -0
- mtppy/part3/data_assembly_set/active_elements/valves/mon_ana_vlv_test.py +270 -0
- mtppy/part3/data_assembly_set/active_elements/valves/mon_bin_vlv.py +286 -0
- mtppy/part3/data_assembly_set/active_elements/valves/mon_bin_vlv_test.py +224 -0
- mtppy/part3/data_assembly_set/base_functions/__init__.py +4 -0
- mtppy/part3/data_assembly_set/base_functions/operation_mode.py +252 -0
- mtppy/part3/data_assembly_set/base_functions/operation_mode_test.py +260 -0
- mtppy/part3/data_assembly_set/base_functions/oslevel.py +35 -0
- mtppy/part3/data_assembly_set/base_functions/source_mode.py +189 -0
- mtppy/part3/data_assembly_set/base_functions/source_mode_test.py +112 -0
- mtppy/part3/data_assembly_set/base_functions/wqc.py +75 -0
- mtppy/part3/data_assembly_set/base_functions/wqc_test.py +86 -0
- mtppy/part3/data_assembly_set/data_assembly.py +65 -0
- mtppy/part3/data_assembly_set/diagnostic_elements/__init__.py +4 -0
- mtppy/part3/data_assembly_set/diagnostic_elements/diagnostic_element.py +19 -0
- mtppy/part3/data_assembly_set/indicator_elements/__init__.py +4 -0
- mtppy/part3/data_assembly_set/indicator_elements/indicator_element.py +22 -0
- mtppy/part3/data_assembly_set/indicator_elements/views/__init__.py +4 -0
- mtppy/part3/data_assembly_set/indicator_elements/views/ana_view.py +68 -0
- mtppy/part3/data_assembly_set/indicator_elements/views/bin_view.py +73 -0
- mtppy/part3/data_assembly_set/indicator_elements/views/dint_view.py +76 -0
- mtppy/part3/data_assembly_set/indicator_elements/views/string_view.py +55 -0
- mtppy/part3/data_assembly_set/operation_elements/__init__.py +4 -0
- mtppy/part3/data_assembly_set/operation_elements/operation_element.py +19 -0
- mtppy/part3/data_assembly_set/pea_elements/__init__.py +4 -0
- mtppy/part3/data_assembly_set/pea_elements/pea_element.py +28 -0
- mtppy/part3/data_assembly_set/pea_elements/pea_information_label/__init__.py +4 -0
- mtppy/part3/data_assembly_set/pea_elements/pea_information_label/pea_information_label.py +231 -0
- mtppy/part3/data_assembly_set/pea_elements/web_server_url_info/__init__.py +4 -0
- mtppy/part3/data_assembly_set/pea_elements/web_server_url_info/web_server_url_info.py +42 -0
- mtppy/part3/definitions/__init__.py +4 -0
- mtppy/part3/definitions/mtp_access.py +15 -0
- mtppy/part4/__init__.py +4 -0
- mtppy/part4/automation_services/__init__.py +4 -0
- mtppy/part4/automation_services/base_functions/__init__.py +4 -0
- mtppy/part4/automation_services/base_functions/service_operation_mode.py +368 -0
- mtppy/part4/automation_services/base_functions/service_operation_mode_test.py +242 -0
- mtppy/part4/automation_services/base_functions/service_operator_interaction.py +62 -0
- mtppy/part4/automation_services/base_functions/service_source_mode.py +138 -0
- mtppy/part4/automation_services/base_functions/service_source_mode_test.py +179 -0
- mtppy/part4/automation_services/helpers/__init__.py +4 -0
- mtppy/part4/automation_services/helpers/command_en_control.py +330 -0
- mtppy/part4/automation_services/helpers/command_en_control_test.py +302 -0
- mtppy/part4/automation_services/helpers/state_machine.py +167 -0
- mtppy/part4/automation_services/helpers/state_machine_test.py +294 -0
- mtppy/part4/automation_services/helpers/thread_control.py +39 -0
- mtppy/part4/automation_services/helpers/thread_control_test.py +80 -0
- mtppy/part4/automation_services/procedure.py +111 -0
- mtppy/part4/automation_services/procedure_test.py +185 -0
- mtppy/part4/automation_services/service.py +463 -0
- mtppy/part4/automation_services/service_elements/__init__.py +4 -0
- mtppy/part4/automation_services/service_elements/parameter_elements/__init__.py +4 -0
- mtppy/part4/automation_services/service_elements/parameter_elements/ana_serv_param.py +108 -0
- mtppy/part4/automation_services/service_elements/parameter_elements/ana_serv_param_test.py +59 -0
- mtppy/part4/automation_services/service_elements/parameter_elements/bin_serv_param.py +70 -0
- mtppy/part4/automation_services/service_elements/parameter_elements/bin_serv_param_test.py +39 -0
- mtppy/part4/automation_services/service_elements/parameter_elements/common_serv_param.py +111 -0
- mtppy/part4/automation_services/service_elements/parameter_elements/dint_serv_param.py +77 -0
- mtppy/part4/automation_services/service_elements/parameter_elements/dint_serv_param_test.py +58 -0
- mtppy/part4/automation_services/service_elements/parameter_elements/parameter_element.py +175 -0
- mtppy/part4/automation_services/service_elements/parameter_elements/parameter_element_test.py +246 -0
- mtppy/part4/automation_services/service_elements/parameter_elements/string_serv_param.py +48 -0
- mtppy/part4/automation_services/service_elements/parameter_elements/string_serv_param_test.py +37 -0
- mtppy/part4/automation_services/service_elements/procedure_health_view.py +36 -0
- mtppy/part4/automation_services/service_elements/service_control.py +473 -0
- mtppy/part4/automation_services/service_elements/service_control_test.py +150 -0
- mtppy/part4/automation_services/service_elements/service_element.py +20 -0
- mtppy/part4/automation_services/service_test.py +407 -0
- mtppy/part4/definitions/__init__.py +4 -0
- mtppy/part4/definitions/command_codes.py +58 -0
- mtppy/part4/definitions/command_codes_test.py +44 -0
- mtppy/part4/definitions/state_codes.py +65 -0
- mtppy/part4/definitions/state_codes_test.py +44 -0
- mtppy/part4/process_values/__init__.py +4 -0
- mtppy/part4/process_values/base_functions/__init__.py +4 -0
- mtppy/part4/process_values/base_functions/vqc.py +48 -0
- mtppy/part4/process_values/input_elements/__init__.py +4 -0
- mtppy/part4/process_values/input_elements/ana_process_value_in.py +160 -0
- mtppy/part4/process_values/input_elements/bin_process_value_in.py +83 -0
- mtppy/part4/process_values/input_elements/dint_process_value_in.py +159 -0
- mtppy/part4/process_values/input_elements/input_element.py +40 -0
- mtppy/part4/process_values/input_elements/string_process_value_in.py +45 -0
- mtppy/part5/__init__.py +4 -0
- mtppy/part5/communication_object.py +99 -0
- mtppy/part5/communication_object_test.py +59 -0
- mtppy/part5/definitions/__init__.py +4 -0
- mtppy/part5/definitions/iec61131_data_types.py +31 -0
- mtppy/part5/definitions/iec61131_data_types_test.py +82 -0
- mtppy/part5/definitions/opcua_data_types.py +36 -0
- mtppy/part5/definitions/opcua_data_types_test.py +90 -0
- mtppy/part5/definitions/w3c_xml_data_types.py +177 -0
- mtppy/part5/definitions/w3c_xml_data_types_test.py +143 -0
- mtppy/part5/server_assembly_set/__init__.py +5 -0
- mtppy/part6/__init__.py +4 -0
- mtppy/part6/alarm_set/__init__.py +4 -0
- mtppy/part6/alarm_set/alarm.py +132 -0
- mtppy/part6/alarm_set/alarm_test.py +265 -0
- mtppy/pea/__init__.py +19 -0
- mtppy/pea/data_assembly_factory.py +124 -0
- mtppy/pea/data_assembly_factory_test.py +229 -0
- mtppy/pea/mtp_generator/__init__.py +4 -0
- mtppy/pea/mtp_generator/mtp_generator.py +1183 -0
- mtppy/pea/mtp_generator/mtp_generator_test.py +518 -0
- mtppy/pea/pea.py +139 -0
- mtppy/pea/pea_opcua_server/__init__.py +4 -0
- mtppy/pea/pea_opcua_server/marshalling.py +60 -0
- mtppy/pea/pea_opcua_server/opcua_options_provider/__init__.py +4 -0
- mtppy/pea/pea_opcua_server/opcua_options_provider/instances/basic_opcua_options_provider.py +50 -0
- mtppy/pea/pea_opcua_server/opcua_options_provider/opcua_options_provider.py +103 -0
- mtppy/pea/pea_opcua_server/pea_opcua_server.py +492 -0
- mtppy/pea/pea_opcua_server/subscription_list.py +55 -0
- mtppy/pea/pea_test.py +59 -0
- mtppy/pea/rmtp_pea.py +155 -0
- mtppy/pea/rmtp_pea_test.py +27 -0
- mtppy/pea/service_factory.py +178 -0
- mtppy/pea/service_factory_test.py +156 -0
- mtppy/pea/user_logic/__init__.py +4 -0
- mtppy/pea/user_logic/user_logic_pea.py +72 -0
- mtppy/pea/user_logic/user_logic_registry.py +106 -0
- mtppy/pea/user_logic/user_logic_registry_test.py +167 -0
- mtppy/pea/user_logic/user_logic_service.py +228 -0
- mtppy/pea/user_logic/user_logic_service_test.py +289 -0
- mtppy/utils/__init__.py +4 -0
- mtppy/utils/data_assembly_dict.py +61 -0
- mtppy/utils/data_assembly_dict_test.py +107 -0
- mtppy/utils/data_item.py +446 -0
- mtppy/utils/data_item_com_test.py +82 -0
- mtppy/utils/data_item_test.py +325 -0
- mtppy/utils/logging.py +23 -0
- mtppy/utils/logging_test.py +26 -0
- mtppy/utils/mtp_file_handling/__init__.py +4 -0
- mtppy/utils/mtp_file_handling/extract_manifest_from_mtp_file.py +22 -0
- mtppy/utils/mtp_file_handling/extract_mtp_file.py +22 -0
- mtppy/utils/mtp_file_handling/is_valid_mtp.py +22 -0
- mtppy/utils/mtp_file_handling/is_valid_path_for_manifest.py +15 -0
- mtppy/utils/mtp_file_handling/is_valid_path_to_manifest.py +17 -0
- mtppy/utils/mtp_file_handling/is_valid_path_to_mtp.py +17 -0
- mtppy/utils/mtp_file_handling/load_manifest_file.py +24 -0
- mtppy/utils/mtp_file_handling/mtp_reader/__init__.py +4 -0
- mtppy/utils/mtp_file_handling/mtp_reader/mtp_reader.py +103 -0
- mtppy/utils/mtp_file_handling/mtp_reader/mtp_reader_test.py +96 -0
- mtppy/utils/mtp_file_handling/mtp_reader/mtp_xml.py +226 -0
- mtppy/utils/mtp_file_handling/mtp_reader/mtp_xml_test.py +108 -0
- mtppy/utils/mtp_file_handling/mtp_writer/__init__.py +4 -0
- mtppy/utils/mtp_file_handling/mtp_writer/mtp_writer.py +77 -0
- mtppy/utils/mtp_file_handling/mtp_writer/ressources/get_content_types.py +10 -0
- mtppy/utils/mtp_file_handling/mtp_writer/ressources/get_rels.py +11 -0
- mtppy/utils/mtp_file_handling/mtp_writer/test_mtp_writer.py +25 -0
- mtppy/utils/mtp_file_handling/write_manifest_file.py +29 -0
- mtppy/utils/mtp_file_handling/write_manifest_file_test.py +22 -0
- mtppy/utils/mtp_file_handling/write_mtp_file.py +43 -0
- mtppy/utils/mtp_file_handling/write_mtp_file_test.py +22 -0
- mtppy/utils/testing.py +31 -0
- mtppy2_0-0.1.5.dist-info/METADATA +75 -0
- mtppy2_0-0.1.5.dist-info/RECORD +214 -0
- mtppy2_0-0.1.5.dist-info/WHEEL +5 -0
- mtppy2_0-0.1.5.dist-info/entry_points.txt +3 -0
- mtppy2_0-0.1.5.dist-info/licenses/LICENSE +21 -0
- mtppy2_0-0.1.5.dist-info/top_level.txt +4 -0
- tools/__init__.py +0 -0
- tools/create_bugreport.py +150 -0
- tools/inspect_mtp.py +110 -0
- utils/__init__.py +1 -0
- utils/file_handling/__init__.py +4 -0
- utils/file_handling/cleanup_folder.py +9 -0
- utils/file_handling/get_utc_foldername_with_ms.py +15 -0
- utils/file_handling/get_work_dir.py +9 -0
- utils/file_handling/is_valid_path_for_json.py +11 -0
- utils/file_handling/is_valid_path_to_json.py +17 -0
- utils/file_handling/load_json_file.py +18 -0
- utils/file_handling/read_file_from_file_archive.py +43 -0
- utils/file_handling/read_file_string.py +10 -0
- utils/file_handling/validate_json_data.py +28 -0
- utils/file_handling/write_json_file.py +23 -0
- utils/get_uuid.py +18 -0
- utils/get_uuid_test.py +17 -0
- utils/logging.py +100 -0
- utils/logging_test.py +67 -0
- utils/opcua/check_node.py +79 -0
- utils/resolve_unit/resolve_unit.py +11 -0
- utils/resolve_unit/unit_lib.py +2101 -0
- utils/resolve_unit/unit_lib_test.py +36 -0
- utils/type_conversion.py +236 -0
- utils/type_conversion_test.py +114 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Implementation of ServiceOperationMode BaseFunction as specified in PNO MTP 2.0 Part 4, Sections 8.2.2.3
|
|
3
|
+
|
|
4
|
+
The ServiceOperationMode BaseFunction is a concept that allows the service to select its operation mode
|
|
5
|
+
based on the state of the service and the configuration parameters. It is used to determine whether
|
|
6
|
+
the service should operate in offline, operator, or automatic mode.
|
|
7
|
+
|
|
8
|
+
- Manual Operations: Operator actions are conveyed via the `Op` variables,
|
|
9
|
+
- Automatic Operation: PEA-internal actions made visible via the `Aut` variables
|
|
10
|
+
- Actual Operation: The actual state of the service is reflected in the `Act` variables.
|
|
11
|
+
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
__copyright__ = (
|
|
15
|
+
"Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
|
|
16
|
+
)
|
|
17
|
+
__license__ = "MIT"
|
|
18
|
+
|
|
19
|
+
from collections.abc import Callable
|
|
20
|
+
from typing import ClassVar
|
|
21
|
+
|
|
22
|
+
import inspect
|
|
23
|
+
|
|
24
|
+
from mtppy.utils.logging import logger
|
|
25
|
+
from mtppy.utils.data_item import DataItem, DataItemCollection
|
|
26
|
+
from mtppy.part3.definitions.mtp_access import MTPAccess
|
|
27
|
+
from mtppy.part5.definitions.w3c_xml_data_types import W3cXmlDataTypes
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ServiceOperationMode(DataItemCollection):
|
|
31
|
+
STATE_CHANNEL = "StateChannel"
|
|
32
|
+
STATE_OFF_AUT = "StateOffAut"
|
|
33
|
+
STATE_OP_AUT = "StateOpAut"
|
|
34
|
+
STATE_AUT_AUT = "StateAutAut"
|
|
35
|
+
STATE_OFF_OP = "StateOffOp"
|
|
36
|
+
STATE_OP_OP = "StateOpOp"
|
|
37
|
+
STATE_AUT_OP = "StateAutOp"
|
|
38
|
+
STATE_OP_ACT = "StateOpAct"
|
|
39
|
+
STATE_AUT_ACT = "StateAutAct"
|
|
40
|
+
STATE_OFF_ACT = "StateOffAct"
|
|
41
|
+
|
|
42
|
+
CLASS_DATA_ITEMS: ClassVar = {
|
|
43
|
+
STATE_CHANNEL: DataItem(
|
|
44
|
+
STATE_CHANNEL,
|
|
45
|
+
W3cXmlDataTypes.BOOLEAN,
|
|
46
|
+
MTPAccess.ReadAccess,
|
|
47
|
+
init_value=False,
|
|
48
|
+
),
|
|
49
|
+
STATE_OFF_AUT: DataItem(
|
|
50
|
+
STATE_OFF_AUT,
|
|
51
|
+
W3cXmlDataTypes.BOOLEAN,
|
|
52
|
+
MTPAccess.ReadAccess,
|
|
53
|
+
init_value=False,
|
|
54
|
+
),
|
|
55
|
+
STATE_OP_AUT: DataItem(
|
|
56
|
+
STATE_OP_AUT,
|
|
57
|
+
W3cXmlDataTypes.BOOLEAN,
|
|
58
|
+
MTPAccess.ReadAccess,
|
|
59
|
+
init_value=False,
|
|
60
|
+
),
|
|
61
|
+
STATE_AUT_AUT: DataItem(
|
|
62
|
+
STATE_AUT_AUT,
|
|
63
|
+
W3cXmlDataTypes.BOOLEAN,
|
|
64
|
+
MTPAccess.ReadAccess,
|
|
65
|
+
init_value=False,
|
|
66
|
+
),
|
|
67
|
+
STATE_OFF_OP: DataItem(
|
|
68
|
+
STATE_OFF_OP,
|
|
69
|
+
W3cXmlDataTypes.BOOLEAN,
|
|
70
|
+
MTPAccess.ReadWriteAccess,
|
|
71
|
+
init_value=False,
|
|
72
|
+
# self.set_state_off_op,
|
|
73
|
+
),
|
|
74
|
+
STATE_OP_OP: DataItem(
|
|
75
|
+
STATE_OP_OP,
|
|
76
|
+
W3cXmlDataTypes.BOOLEAN,
|
|
77
|
+
MTPAccess.ReadWriteAccess,
|
|
78
|
+
init_value=False,
|
|
79
|
+
# self.set_state_op_op,
|
|
80
|
+
),
|
|
81
|
+
STATE_AUT_OP: DataItem(
|
|
82
|
+
STATE_AUT_OP,
|
|
83
|
+
W3cXmlDataTypes.BOOLEAN,
|
|
84
|
+
MTPAccess.ReadWriteAccess,
|
|
85
|
+
init_value=False,
|
|
86
|
+
# self.set_state_aut_op,
|
|
87
|
+
),
|
|
88
|
+
STATE_OP_ACT: DataItem(
|
|
89
|
+
STATE_OP_ACT,
|
|
90
|
+
W3cXmlDataTypes.BOOLEAN,
|
|
91
|
+
MTPAccess.ReadAccess,
|
|
92
|
+
init_value=False,
|
|
93
|
+
),
|
|
94
|
+
STATE_AUT_ACT: DataItem(
|
|
95
|
+
STATE_AUT_ACT,
|
|
96
|
+
W3cXmlDataTypes.BOOLEAN,
|
|
97
|
+
MTPAccess.ReadAccess,
|
|
98
|
+
init_value=False,
|
|
99
|
+
),
|
|
100
|
+
STATE_OFF_ACT: DataItem(
|
|
101
|
+
STATE_OFF_ACT,
|
|
102
|
+
W3cXmlDataTypes.BOOLEAN,
|
|
103
|
+
MTPAccess.ReadAccess,
|
|
104
|
+
init_value=True,
|
|
105
|
+
),
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
switch_to_offline_mode_allowed: bool
|
|
109
|
+
|
|
110
|
+
enter_offline_callbacks: list[Callable[[], None]]
|
|
111
|
+
exit_offline_callbacks: list[Callable[[], None]]
|
|
112
|
+
enter_operator_callbacks: list[Callable[[], None]]
|
|
113
|
+
exit_operator_callbacks: list[Callable[[], None]]
|
|
114
|
+
enter_automatic_callbacks: list[Callable[[], None]]
|
|
115
|
+
exit_automatic_callbacks: list[Callable[[], None]]
|
|
116
|
+
|
|
117
|
+
def __init__(self):
|
|
118
|
+
"""
|
|
119
|
+
Represents the operation mode BaseFunction for service part.
|
|
120
|
+
"""
|
|
121
|
+
super().__init__()
|
|
122
|
+
|
|
123
|
+
self.switch_to_offline_mode_allowed = False
|
|
124
|
+
|
|
125
|
+
self.enter_offline_callbacks = []
|
|
126
|
+
self.exit_offline_callbacks = []
|
|
127
|
+
|
|
128
|
+
self.enter_operator_callbacks = []
|
|
129
|
+
self.exit_operator_callbacks = []
|
|
130
|
+
|
|
131
|
+
self.enter_automatic_callbacks = []
|
|
132
|
+
self.exit_automatic_callbacks = []
|
|
133
|
+
|
|
134
|
+
self.get_item(ServiceOperationMode.STATE_OFF_OP).ext_wrt_cb = (
|
|
135
|
+
self.set_state_off_op
|
|
136
|
+
)
|
|
137
|
+
self.get_item(ServiceOperationMode.STATE_OP_OP).ext_wrt_cb = (
|
|
138
|
+
self.set_state_op_op
|
|
139
|
+
)
|
|
140
|
+
self.get_item(ServiceOperationMode.STATE_AUT_OP).ext_wrt_cb = (
|
|
141
|
+
self.set_state_aut_op
|
|
142
|
+
)
|
|
143
|
+
# check if this brings us back in call back hell!!
|
|
144
|
+
# self.get_item(ServiceOperationMode.STATE_AUT_OP).int_wrt_cb = (
|
|
145
|
+
# self.set_state_aut_op
|
|
146
|
+
# )
|
|
147
|
+
|
|
148
|
+
self.get_item(ServiceOperationMode.STATE_OFF_AUT).int_wrt_cb = (
|
|
149
|
+
self.set_state_off_aut
|
|
150
|
+
)
|
|
151
|
+
self.get_item(ServiceOperationMode.STATE_OP_AUT).int_wrt_cb = (
|
|
152
|
+
self.set_state_op_aut
|
|
153
|
+
)
|
|
154
|
+
self.get_item(ServiceOperationMode.STATE_AUT_AUT).int_wrt_cb = (
|
|
155
|
+
self.set_state_aut_aut
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
def _get_callback_list_str(self, callbacks: list[Callable]) -> str:
|
|
159
|
+
return (
|
|
160
|
+
"["
|
|
161
|
+
+ ", ".join(
|
|
162
|
+
f"{inspect.getmodule(cb).__name__}.{cb.__name__}" for cb in callbacks
|
|
163
|
+
)
|
|
164
|
+
+ "]"
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
def allow_switch_to_offline_mode(self, allow_flag: bool):
|
|
168
|
+
self.switch_to_offline_mode_allowed = allow_flag
|
|
169
|
+
|
|
170
|
+
def add_enter_offline_callback(self, callback: Callable[[], None]):
|
|
171
|
+
self.enter_offline_callbacks.append(callback)
|
|
172
|
+
|
|
173
|
+
def add_exit_offline_callback(self, callback: Callable[[], None]):
|
|
174
|
+
self.exit_offline_callbacks.append(callback)
|
|
175
|
+
|
|
176
|
+
def add_enter_operator_callback(self, callback: Callable[[], None]):
|
|
177
|
+
self.enter_operator_callbacks.append(callback)
|
|
178
|
+
|
|
179
|
+
def add_exit_operator_callback(self, callback: Callable[[], None]):
|
|
180
|
+
self.exit_operator_callbacks.append(callback)
|
|
181
|
+
|
|
182
|
+
def add_enter_automatic_callback(self, callback: Callable[[], None]):
|
|
183
|
+
self.enter_automatic_callbacks.append(callback)
|
|
184
|
+
|
|
185
|
+
def add_exit_automatic_callback(self, callback: Callable[[], None]):
|
|
186
|
+
self.exit_automatic_callbacks.append(callback)
|
|
187
|
+
|
|
188
|
+
def _enter_off(self):
|
|
189
|
+
if len(self.enter_offline_callbacks):
|
|
190
|
+
logger.mtp("Applying enter offline mode callbacks")
|
|
191
|
+
[cb() for cb in self.enter_offline_callbacks]
|
|
192
|
+
|
|
193
|
+
def _exit_off(self):
|
|
194
|
+
if len(self.exit_offline_callbacks):
|
|
195
|
+
logger.mtp(
|
|
196
|
+
f"Applying exit offline mode callbacks {self._get_callback_list_str(self.exit_offline_callbacks)}"
|
|
197
|
+
)
|
|
198
|
+
[cb() for cb in self.exit_offline_callbacks]
|
|
199
|
+
|
|
200
|
+
def _enter_op(self):
|
|
201
|
+
if len(self.enter_operator_callbacks):
|
|
202
|
+
logger.mtp("Applying enter operator mode callbacks")
|
|
203
|
+
[cb() for cb in self.enter_operator_callbacks]
|
|
204
|
+
|
|
205
|
+
def _exit_op(self):
|
|
206
|
+
if len(self.exit_operator_callbacks):
|
|
207
|
+
logger.mtp("Applying exit operator mode callbacks")
|
|
208
|
+
[cb() for cb in self.exit_operator_callbacks]
|
|
209
|
+
|
|
210
|
+
def _enter_aut(self):
|
|
211
|
+
if len(self.enter_automatic_callbacks):
|
|
212
|
+
logger.mtp("Applying enter automatic mode callbacks")
|
|
213
|
+
[cb() for cb in self.enter_automatic_callbacks]
|
|
214
|
+
|
|
215
|
+
def _exit_aut(self):
|
|
216
|
+
if len(self.exit_automatic_callbacks):
|
|
217
|
+
logger.mtp("Applying exit automatic mode callbacks")
|
|
218
|
+
[cb() for cb in self.exit_automatic_callbacks]
|
|
219
|
+
|
|
220
|
+
def _opmode_to_off(self):
|
|
221
|
+
logger.debug(f"_opmode_to_off: args: -")
|
|
222
|
+
|
|
223
|
+
prev_mode_is_op = self[ServiceOperationMode.STATE_OP_ACT]
|
|
224
|
+
prev_mode_is_aut = self[ServiceOperationMode.STATE_AUT_ACT]
|
|
225
|
+
|
|
226
|
+
self[ServiceOperationMode.STATE_OP_ACT] = False
|
|
227
|
+
self[ServiceOperationMode.STATE_AUT_ACT] = False
|
|
228
|
+
self[ServiceOperationMode.STATE_OFF_ACT] = True
|
|
229
|
+
|
|
230
|
+
# Mode change callbacks
|
|
231
|
+
if prev_mode_is_op:
|
|
232
|
+
self._exit_op()
|
|
233
|
+
elif prev_mode_is_aut:
|
|
234
|
+
self._exit_aut()
|
|
235
|
+
self._enter_off()
|
|
236
|
+
|
|
237
|
+
logger.mtp("ServiceOperationMode is now off")
|
|
238
|
+
|
|
239
|
+
def _opmode_to_aut(self):
|
|
240
|
+
logger.debug(f"_opmode_to_aut: args: -")
|
|
241
|
+
|
|
242
|
+
prev_mode_is_off = self[ServiceOperationMode.STATE_OFF_ACT]
|
|
243
|
+
prev_mode_is_op = self[ServiceOperationMode.STATE_OP_ACT]
|
|
244
|
+
|
|
245
|
+
self[ServiceOperationMode.STATE_OP_ACT] = False
|
|
246
|
+
self[ServiceOperationMode.STATE_AUT_ACT] = True
|
|
247
|
+
self[ServiceOperationMode.STATE_OFF_ACT] = False
|
|
248
|
+
|
|
249
|
+
# Mode change callbacks
|
|
250
|
+
if prev_mode_is_off:
|
|
251
|
+
self._exit_off()
|
|
252
|
+
elif prev_mode_is_op:
|
|
253
|
+
self._exit_op()
|
|
254
|
+
self._enter_aut()
|
|
255
|
+
|
|
256
|
+
logger.mtp("ServiceOperationMode is now aut")
|
|
257
|
+
|
|
258
|
+
def _opmode_to_op(self):
|
|
259
|
+
logger.debug(f"_opmode_to_op: args: -")
|
|
260
|
+
|
|
261
|
+
prev_mode_is_off = self[ServiceOperationMode.STATE_OFF_ACT]
|
|
262
|
+
prev_mode_is_aut = self[ServiceOperationMode.STATE_AUT_ACT]
|
|
263
|
+
|
|
264
|
+
self[ServiceOperationMode.STATE_OP_ACT] = True
|
|
265
|
+
self[ServiceOperationMode.STATE_AUT_ACT] = False
|
|
266
|
+
self[ServiceOperationMode.STATE_OFF_ACT] = False
|
|
267
|
+
|
|
268
|
+
# Mode change callbacks
|
|
269
|
+
if prev_mode_is_off:
|
|
270
|
+
self._exit_off()
|
|
271
|
+
elif prev_mode_is_aut:
|
|
272
|
+
self._exit_aut()
|
|
273
|
+
self._enter_op()
|
|
274
|
+
|
|
275
|
+
logger.mtp(
|
|
276
|
+
f"ServiceOperationMode is now op, StateOpAct={self[ServiceOperationMode.STATE_OP_ACT]}"
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
def set_state_channel(self, value: bool):
|
|
280
|
+
logger.debug(f"set_state_channel: value={value}")
|
|
281
|
+
|
|
282
|
+
self[ServiceOperationMode.STATE_CHANNEL] = value
|
|
283
|
+
|
|
284
|
+
logger.info(
|
|
285
|
+
f"requested {value}, got {self[ServiceOperationMode.STATE_CHANNEL]}"
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
def set_state_aut_aut(self, value: bool):
|
|
289
|
+
logger.debug(f"value={value}")
|
|
290
|
+
|
|
291
|
+
if self[ServiceOperationMode.STATE_CHANNEL] and value:
|
|
292
|
+
if (
|
|
293
|
+
self[ServiceOperationMode.STATE_OFF_ACT]
|
|
294
|
+
or self[ServiceOperationMode.STATE_OP_ACT]
|
|
295
|
+
):
|
|
296
|
+
self._opmode_to_aut()
|
|
297
|
+
|
|
298
|
+
logger.info(
|
|
299
|
+
f"requested {value}, got {self[ServiceOperationMode.STATE_AUT_AUT]}"
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
def set_state_aut_op(self, value: bool):
|
|
303
|
+
logger.debug(f"set_state_aut_op: value={value}")
|
|
304
|
+
|
|
305
|
+
if not self[ServiceOperationMode.STATE_CHANNEL] and value:
|
|
306
|
+
if (
|
|
307
|
+
self[ServiceOperationMode.STATE_OFF_ACT]
|
|
308
|
+
or self[ServiceOperationMode.STATE_OP_ACT]
|
|
309
|
+
):
|
|
310
|
+
self._opmode_to_aut()
|
|
311
|
+
# handshake
|
|
312
|
+
self[ServiceOperationMode.STATE_AUT_OP] = False
|
|
313
|
+
|
|
314
|
+
def set_state_off_aut(self, value: bool):
|
|
315
|
+
logger.debug(f"set_state_off_aut: value={value}")
|
|
316
|
+
|
|
317
|
+
if (
|
|
318
|
+
self[ServiceOperationMode.STATE_CHANNEL]
|
|
319
|
+
and value
|
|
320
|
+
and self.switch_to_offline_mode_allowed
|
|
321
|
+
):
|
|
322
|
+
if (
|
|
323
|
+
self[ServiceOperationMode.STATE_AUT_ACT]
|
|
324
|
+
or self[ServiceOperationMode.STATE_OP_ACT]
|
|
325
|
+
):
|
|
326
|
+
self._opmode_to_off()
|
|
327
|
+
|
|
328
|
+
logger.info(
|
|
329
|
+
f"requested {value}, got {self[ServiceOperationMode.STATE_OFF_AUT]}"
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
def set_state_off_op(self, value: bool):
|
|
333
|
+
logger.debug(f"value={value}")
|
|
334
|
+
|
|
335
|
+
if (
|
|
336
|
+
value
|
|
337
|
+
and not self[ServiceOperationMode.STATE_CHANNEL]
|
|
338
|
+
and self.switch_to_offline_mode_allowed
|
|
339
|
+
):
|
|
340
|
+
if (
|
|
341
|
+
self[ServiceOperationMode.STATE_AUT_ACT]
|
|
342
|
+
or self[ServiceOperationMode.STATE_OP_ACT]
|
|
343
|
+
):
|
|
344
|
+
self._opmode_to_off()
|
|
345
|
+
self[ServiceOperationMode.STATE_OFF_OP] = False
|
|
346
|
+
|
|
347
|
+
def set_state_op_aut(self, value: bool):
|
|
348
|
+
logger.debug(f"set_state_op_aut: value={value}")
|
|
349
|
+
|
|
350
|
+
if self[ServiceOperationMode.STATE_CHANNEL] and value:
|
|
351
|
+
if (
|
|
352
|
+
self[ServiceOperationMode.STATE_OFF_ACT]
|
|
353
|
+
or self[ServiceOperationMode.STATE_AUT_ACT]
|
|
354
|
+
):
|
|
355
|
+
self._opmode_to_op()
|
|
356
|
+
|
|
357
|
+
logger.info(f"requested {value}, got {self[ServiceOperationMode.STATE_OP_AUT]}")
|
|
358
|
+
|
|
359
|
+
def set_state_op_op(self, value: bool):
|
|
360
|
+
logger.debug(f"set_state_op_op: value={value}")
|
|
361
|
+
|
|
362
|
+
if not self[ServiceOperationMode.STATE_CHANNEL] and value:
|
|
363
|
+
if (
|
|
364
|
+
self[ServiceOperationMode.STATE_OFF_ACT] # offline
|
|
365
|
+
or self[ServiceOperationMode.STATE_AUT_ACT] # automatic
|
|
366
|
+
):
|
|
367
|
+
self._opmode_to_op()
|
|
368
|
+
self[ServiceOperationMode.STATE_OP_OP] = False
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"""Test Suite for ServiceOperationMode."""
|
|
2
|
+
|
|
3
|
+
__copyright__ = (
|
|
4
|
+
"Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
|
|
5
|
+
)
|
|
6
|
+
__license__ = "MIT"
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
from mtppy.part4.automation_services.base_functions.service_operation_mode import ServiceOperationMode
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def init_service_op_mode(
|
|
13
|
+
init_mode="off", state_channel=False, allow_switch_to_offline=True
|
|
14
|
+
):
|
|
15
|
+
service_op_mode = ServiceOperationMode()
|
|
16
|
+
service_op_mode.allow_switch_to_offline_mode(allow_switch_to_offline)
|
|
17
|
+
service_op_mode.set_state_channel(state_channel)
|
|
18
|
+
|
|
19
|
+
# MTP Part 4 Figure 2
|
|
20
|
+
if init_mode == "off":
|
|
21
|
+
service_op_mode[ServiceOperationMode.STATE_OFF_ACT] = True
|
|
22
|
+
service_op_mode[ServiceOperationMode.STATE_OP_ACT] = False
|
|
23
|
+
service_op_mode[ServiceOperationMode.STATE_AUT_ACT] = False
|
|
24
|
+
elif init_mode == "op":
|
|
25
|
+
service_op_mode[ServiceOperationMode.STATE_OFF_ACT] = False
|
|
26
|
+
service_op_mode[ServiceOperationMode.STATE_OP_ACT] = True
|
|
27
|
+
service_op_mode[ServiceOperationMode.STATE_AUT_ACT] = False
|
|
28
|
+
elif init_mode == "aut":
|
|
29
|
+
service_op_mode[ServiceOperationMode.STATE_OFF_ACT] = False
|
|
30
|
+
service_op_mode[ServiceOperationMode.STATE_OP_ACT] = False
|
|
31
|
+
service_op_mode[ServiceOperationMode.STATE_AUT_ACT] = True
|
|
32
|
+
else:
|
|
33
|
+
raise ValueError(f"ServiceOperationMode {init_mode} is unknown")
|
|
34
|
+
|
|
35
|
+
return service_op_mode
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def service_operation_mode_is_in_specific_mode(
|
|
39
|
+
service_op_mode: ServiceOperationMode,
|
|
40
|
+
state_channel_assertion: bool,
|
|
41
|
+
mode_assertion: str,
|
|
42
|
+
):
|
|
43
|
+
mode = lambda m: (mode_assertion == m)
|
|
44
|
+
return all(
|
|
45
|
+
[
|
|
46
|
+
service_op_mode[ServiceOperationMode.STATE_CHANNEL]
|
|
47
|
+
== state_channel_assertion,
|
|
48
|
+
service_op_mode[ServiceOperationMode.STATE_OFF_AUT] == False,
|
|
49
|
+
service_op_mode[ServiceOperationMode.STATE_OP_AUT] == False,
|
|
50
|
+
service_op_mode[ServiceOperationMode.STATE_AUT_AUT] == False,
|
|
51
|
+
service_op_mode[ServiceOperationMode.STATE_OFF_OP] == False,
|
|
52
|
+
service_op_mode[ServiceOperationMode.STATE_OP_OP] == False,
|
|
53
|
+
service_op_mode[ServiceOperationMode.STATE_AUT_OP] == False,
|
|
54
|
+
service_op_mode[ServiceOperationMode.STATE_OP_ACT] == mode("op"),
|
|
55
|
+
service_op_mode[ServiceOperationMode.STATE_AUT_ACT] == mode("aut"),
|
|
56
|
+
service_op_mode[ServiceOperationMode.STATE_OFF_ACT] == mode("off"),
|
|
57
|
+
]
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def assert_service_operation_mode_is_in_specific_mode(
|
|
62
|
+
service_op_mode: ServiceOperationMode,
|
|
63
|
+
state_channel_assertion: bool,
|
|
64
|
+
mode_assertion: str,
|
|
65
|
+
) -> None:
|
|
66
|
+
mode = lambda m: (mode_assertion == m)
|
|
67
|
+
assert (
|
|
68
|
+
service_op_mode[ServiceOperationMode.STATE_CHANNEL] == state_channel_assertion
|
|
69
|
+
)
|
|
70
|
+
assert service_op_mode[ServiceOperationMode.STATE_OP_ACT] == mode("op")
|
|
71
|
+
assert service_op_mode[ServiceOperationMode.STATE_AUT_ACT] == mode("aut")
|
|
72
|
+
assert service_op_mode[ServiceOperationMode.STATE_OFF_ACT] == mode("off")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_initial_state():
|
|
76
|
+
service_op_mode = init_service_op_mode(init_mode="off", state_channel=False)
|
|
77
|
+
assert service_operation_mode_is_in_specific_mode(service_op_mode, False, "off")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_state_channel_change():
|
|
81
|
+
# Initial mode
|
|
82
|
+
service_op_mode = init_service_op_mode()
|
|
83
|
+
# Trigger source channel change
|
|
84
|
+
service_op_mode["StateChannel"] = True
|
|
85
|
+
assert service_operation_mode_is_in_specific_mode(service_op_mode, True, "off")
|
|
86
|
+
# Trigger source channel change back
|
|
87
|
+
service_op_mode["StateChannel"] = False
|
|
88
|
+
assert service_operation_mode_is_in_specific_mode(service_op_mode, False, "off")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@pytest.mark.parametrize(
|
|
92
|
+
"init_mode, target_mode, param_extern, param_intern",
|
|
93
|
+
[
|
|
94
|
+
(
|
|
95
|
+
"off",
|
|
96
|
+
"op",
|
|
97
|
+
ServiceOperationMode.STATE_OP_OP,
|
|
98
|
+
ServiceOperationMode.STATE_OP_AUT,
|
|
99
|
+
),
|
|
100
|
+
(
|
|
101
|
+
"op",
|
|
102
|
+
"off",
|
|
103
|
+
ServiceOperationMode.STATE_OFF_OP,
|
|
104
|
+
ServiceOperationMode.STATE_OFF_AUT,
|
|
105
|
+
),
|
|
106
|
+
(
|
|
107
|
+
"off",
|
|
108
|
+
"aut",
|
|
109
|
+
ServiceOperationMode.STATE_AUT_OP,
|
|
110
|
+
ServiceOperationMode.STATE_AUT_AUT,
|
|
111
|
+
),
|
|
112
|
+
(
|
|
113
|
+
"aut",
|
|
114
|
+
"off",
|
|
115
|
+
ServiceOperationMode.STATE_OFF_OP,
|
|
116
|
+
ServiceOperationMode.STATE_OFF_AUT,
|
|
117
|
+
),
|
|
118
|
+
(
|
|
119
|
+
"aut",
|
|
120
|
+
"op",
|
|
121
|
+
ServiceOperationMode.STATE_OP_OP,
|
|
122
|
+
ServiceOperationMode.STATE_OP_AUT,
|
|
123
|
+
),
|
|
124
|
+
(
|
|
125
|
+
"op",
|
|
126
|
+
"aut",
|
|
127
|
+
ServiceOperationMode.STATE_AUT_OP,
|
|
128
|
+
ServiceOperationMode.STATE_AUT_AUT,
|
|
129
|
+
),
|
|
130
|
+
],
|
|
131
|
+
)
|
|
132
|
+
def test_switch_state(
|
|
133
|
+
init_mode: str,
|
|
134
|
+
target_mode: str,
|
|
135
|
+
param_extern: str,
|
|
136
|
+
param_intern: str,
|
|
137
|
+
):
|
|
138
|
+
# Step 1 - request by operator (= external write), state_channel = False
|
|
139
|
+
sc = False
|
|
140
|
+
op_mode = init_service_op_mode(init_mode=init_mode, state_channel=sc)
|
|
141
|
+
assert_service_operation_mode_is_in_specific_mode(op_mode, sc, init_mode)
|
|
142
|
+
op_mode.get_item(param_extern).ext_wrt_cb(True)
|
|
143
|
+
assert_service_operation_mode_is_in_specific_mode(op_mode, sc, target_mode)
|
|
144
|
+
|
|
145
|
+
# Step 2 - request by automation (= internal write), state_channel = True
|
|
146
|
+
sc = True
|
|
147
|
+
op_mode = init_service_op_mode(init_mode=init_mode, state_channel=sc)
|
|
148
|
+
assert_service_operation_mode_is_in_specific_mode(op_mode, sc, init_mode)
|
|
149
|
+
op_mode[param_intern] = True
|
|
150
|
+
assert_service_operation_mode_is_in_specific_mode(op_mode, sc, target_mode)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def test_allowance_change_op_to_off_state_channel_false():
|
|
154
|
+
# Initial mode
|
|
155
|
+
service_op_mode = init_service_op_mode(
|
|
156
|
+
init_mode="op", state_channel=False, allow_switch_to_offline=False
|
|
157
|
+
)
|
|
158
|
+
assert service_operation_mode_is_in_specific_mode(service_op_mode, False, "op")
|
|
159
|
+
# Trigger mode transition
|
|
160
|
+
service_op_mode.get_item(ServiceOperationMode.STATE_OFF_OP).ext_wrt_cb(True)
|
|
161
|
+
assert service_operation_mode_is_in_specific_mode(service_op_mode, False, "op")
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def test_allowance_change_op_to_off_state_channel_true():
|
|
165
|
+
# Initial mode
|
|
166
|
+
service_op_mode = init_service_op_mode(
|
|
167
|
+
init_mode="op", state_channel=True, allow_switch_to_offline=False
|
|
168
|
+
)
|
|
169
|
+
assert service_operation_mode_is_in_specific_mode(service_op_mode, True, "op")
|
|
170
|
+
# Trigger mode transition
|
|
171
|
+
service_op_mode[ServiceOperationMode.STATE_OFF_AUT] = True
|
|
172
|
+
assert service_operation_mode_is_in_specific_mode(service_op_mode, True, "op")
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def test_allowance_change_aut_to_off_state_channel_false():
|
|
176
|
+
# Initial mode
|
|
177
|
+
service_op_mode = init_service_op_mode(
|
|
178
|
+
init_mode="aut", state_channel=False, allow_switch_to_offline=False
|
|
179
|
+
)
|
|
180
|
+
assert service_operation_mode_is_in_specific_mode(service_op_mode, False, "aut")
|
|
181
|
+
# Trigger mode transition
|
|
182
|
+
service_op_mode.get_item(ServiceOperationMode.STATE_OFF_OP).ext_wrt_cb(True)
|
|
183
|
+
assert service_operation_mode_is_in_specific_mode(service_op_mode, False, "aut")
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def test_allowance_change_aut_to_off_state_channel_true():
|
|
187
|
+
# Initial mode
|
|
188
|
+
service_op_mode = init_service_op_mode(
|
|
189
|
+
init_mode="aut", state_channel=True, allow_switch_to_offline=False
|
|
190
|
+
)
|
|
191
|
+
assert service_operation_mode_is_in_specific_mode(service_op_mode, True, "aut")
|
|
192
|
+
# Trigger mode transition
|
|
193
|
+
service_op_mode.set_state_off_aut(True)
|
|
194
|
+
assert service_operation_mode_is_in_specific_mode(service_op_mode, True, "aut")
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def test_exit_offline_callback_off_to_op():
|
|
198
|
+
test_value = {"value": 0}
|
|
199
|
+
|
|
200
|
+
def callback_function():
|
|
201
|
+
test_value["value"] = 10
|
|
202
|
+
|
|
203
|
+
# Initial mode
|
|
204
|
+
service_op_mode = init_service_op_mode(init_mode="off")
|
|
205
|
+
assert service_operation_mode_is_in_specific_mode(service_op_mode, False, "off")
|
|
206
|
+
# Emulate external write on StateOpOp w/o exit callback
|
|
207
|
+
service_op_mode.get_item(ServiceOperationMode.STATE_OP_OP).ext_wrt_cb(True)
|
|
208
|
+
assert service_operation_mode_is_in_specific_mode(service_op_mode, False, "op")
|
|
209
|
+
assert test_value["value"] == 0
|
|
210
|
+
|
|
211
|
+
# Initial mode
|
|
212
|
+
service_op_mode = init_service_op_mode(init_mode="off")
|
|
213
|
+
assert service_operation_mode_is_in_specific_mode(service_op_mode, False, "off")
|
|
214
|
+
service_op_mode.add_exit_offline_callback(callback_function)
|
|
215
|
+
# Trigger mode transition with exit callback
|
|
216
|
+
service_op_mode.get_item(ServiceOperationMode.STATE_OP_OP).ext_wrt_cb(True)
|
|
217
|
+
assert service_operation_mode_is_in_specific_mode(service_op_mode, False, "op")
|
|
218
|
+
assert test_value["value"] == 10
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def test_exit_offline_callback_off_to_aut():
|
|
222
|
+
test_value = {"value": 0}
|
|
223
|
+
|
|
224
|
+
def callback_function():
|
|
225
|
+
test_value["value"] = 10
|
|
226
|
+
|
|
227
|
+
# Initial mode
|
|
228
|
+
service_op_mode = init_service_op_mode(init_mode="off")
|
|
229
|
+
assert service_operation_mode_is_in_specific_mode(service_op_mode, False, "off")
|
|
230
|
+
# Trigger mode transition without callback
|
|
231
|
+
service_op_mode.get_item(ServiceOperationMode.STATE_AUT_OP).ext_wrt_cb(True)
|
|
232
|
+
assert service_operation_mode_is_in_specific_mode(service_op_mode, False, "aut")
|
|
233
|
+
assert test_value["value"] == 0
|
|
234
|
+
|
|
235
|
+
# Initial mode
|
|
236
|
+
service_op_mode = init_service_op_mode(init_mode="off")
|
|
237
|
+
assert service_operation_mode_is_in_specific_mode(service_op_mode, False, "off")
|
|
238
|
+
service_op_mode.add_exit_offline_callback(callback_function)
|
|
239
|
+
# Trigger mode transition with callback
|
|
240
|
+
service_op_mode.get_item(ServiceOperationMode.STATE_AUT_OP).ext_wrt_cb(True)
|
|
241
|
+
assert service_operation_mode_is_in_specific_mode(service_op_mode, False, "aut")
|
|
242
|
+
assert test_value["value"] == 10
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Implementation of ServiceOperatorInteraction."""
|
|
2
|
+
|
|
3
|
+
__copyright__ = (
|
|
4
|
+
"Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
|
|
5
|
+
)
|
|
6
|
+
__license__ = "MIT"
|
|
7
|
+
|
|
8
|
+
from mtppy.utils.data_item import DataItem, DataItemCollection
|
|
9
|
+
from mtppy.part3.definitions.mtp_access import MTPAccess
|
|
10
|
+
from mtppy.part5.definitions.w3c_xml_data_types import W3cXmlDataTypes
|
|
11
|
+
from typing import ClassVar
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ServiceOperatorInteraction(DataItemCollection):
|
|
15
|
+
# DataItem constant definitions
|
|
16
|
+
INTERACT_QUESTION_ID = "InteractQuestionID"
|
|
17
|
+
INTERACT_ANSWER_ID = "InteractAnswerID"
|
|
18
|
+
POS_TEXT_ID = "PosTextID"
|
|
19
|
+
INTERACT_ADD_INFO = "InteractAddInfo"
|
|
20
|
+
|
|
21
|
+
# Class-level data items dictionary
|
|
22
|
+
CLASS_DATA_ITEMS: ClassVar = {
|
|
23
|
+
INTERACT_QUESTION_ID: DataItem(
|
|
24
|
+
INTERACT_QUESTION_ID,
|
|
25
|
+
W3cXmlDataTypes.UNSIGNED_SHORT,
|
|
26
|
+
MTPAccess.ReadAccess,
|
|
27
|
+
0,
|
|
28
|
+
),
|
|
29
|
+
INTERACT_ANSWER_ID: DataItem(
|
|
30
|
+
INTERACT_ANSWER_ID,
|
|
31
|
+
W3cXmlDataTypes.UNSIGNED_SHORT,
|
|
32
|
+
MTPAccess.ReadWriteAccess,
|
|
33
|
+
0,
|
|
34
|
+
),
|
|
35
|
+
POS_TEXT_ID: DataItem(
|
|
36
|
+
POS_TEXT_ID, W3cXmlDataTypes.UNSIGNED_SHORT, MTPAccess.ReadAccess, 0
|
|
37
|
+
),
|
|
38
|
+
INTERACT_ADD_INFO: DataItem(
|
|
39
|
+
INTERACT_ADD_INFO, W3cXmlDataTypes.STRING, MTPAccess.ReadAccess, ""
|
|
40
|
+
),
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
def __init__(self):
|
|
44
|
+
"""
|
|
45
|
+
Represents the service interactions BaseFunction for service part.
|
|
46
|
+
"""
|
|
47
|
+
super().__init__()
|
|
48
|
+
# Initialize data items with dynamic values (using class-defined data items)
|
|
49
|
+
self[ServiceOperatorInteraction.INTERACT_QUESTION_ID] = 0
|
|
50
|
+
self[ServiceOperatorInteraction.INTERACT_ANSWER_ID] = 0
|
|
51
|
+
self[ServiceOperatorInteraction.POS_TEXT_ID] = 0
|
|
52
|
+
self[ServiceOperatorInteraction.INTERACT_ADD_INFO] = ""
|
|
53
|
+
|
|
54
|
+
# Set callbacks for writable items (using the correct pattern for callbacks)
|
|
55
|
+
self.get_item(ServiceOperatorInteraction.INTERACT_ANSWER_ID).ext_wrt_cb = (
|
|
56
|
+
self.set_interact_answer_id
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
def set_interact_answer_id(self, value):
|
|
60
|
+
self[ServiceOperatorInteraction.INTERACT_ANSWER_ID] = value
|
|
61
|
+
|
|
62
|
+
# A list of interactions shall be added and method such as "raise_service_interaction" added
|