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,402 @@
|
|
|
1
|
+
"""Implementation of MonAnaVlv."""
|
|
2
|
+
|
|
3
|
+
__copyright__ = (
|
|
4
|
+
"Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
|
|
5
|
+
)
|
|
6
|
+
__license__ = "MIT"
|
|
7
|
+
from typing import ClassVar
|
|
8
|
+
|
|
9
|
+
from mtppy.utils.logging import logger
|
|
10
|
+
import threading
|
|
11
|
+
from time import sleep
|
|
12
|
+
|
|
13
|
+
from mtppy.utils.data_item import DataItem
|
|
14
|
+
from mtppy.part3.definitions.mtp_access import MTPAccess
|
|
15
|
+
from mtppy.part5.definitions.w3c_xml_data_types import W3cXmlDataTypes
|
|
16
|
+
from mtppy.part3.data_assembly_set.active_elements.valves.ana_vlv import AnaVlv
|
|
17
|
+
from mtppy.part3.data_assembly_set.base_functions.operation_mode import (
|
|
18
|
+
OperationMode as OM,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class MonAnaVlvValues:
|
|
23
|
+
def __init__(
|
|
24
|
+
self, open_aut, open_op, close_aut, close_op, reset_aut, reset_op, pos
|
|
25
|
+
):
|
|
26
|
+
self.open_aut = open_aut
|
|
27
|
+
self.open_op = open_op
|
|
28
|
+
self.close_aut = close_aut
|
|
29
|
+
self.close_op = close_op
|
|
30
|
+
self.reset_aut = reset_aut
|
|
31
|
+
self.reset_op = reset_op
|
|
32
|
+
self.pos = pos
|
|
33
|
+
self.lock = threading.Lock()
|
|
34
|
+
self.stop_event_lock = threading.Event()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class MonAnaVlv(AnaVlv):
|
|
38
|
+
# DataItem constant definitions
|
|
39
|
+
MON_EN = "MonEn"
|
|
40
|
+
MON_SAFE_POS = "MonSafePos"
|
|
41
|
+
MON_STAT_ERR = "MonStatErr"
|
|
42
|
+
MON_DYN_ERR = "MonDynErr"
|
|
43
|
+
MON_STAT_TI = "MonStatTi"
|
|
44
|
+
MON_DYN_TI = "MonDynTi"
|
|
45
|
+
POS_REACHED_FBK = "PosReachedFbk"
|
|
46
|
+
POS_TOLERANCE = "PosTolerance"
|
|
47
|
+
MON_POS_TI = "MonPosTi"
|
|
48
|
+
MON_POS_ERR = "MonPosErr"
|
|
49
|
+
|
|
50
|
+
# Class-level data items dictionary
|
|
51
|
+
CLASS_DATA_ITEMS: ClassVar = {
|
|
52
|
+
MON_EN: DataItem(MON_EN, W3cXmlDataTypes.BOOLEAN, MTPAccess.ReadAccess, None),
|
|
53
|
+
MON_SAFE_POS: DataItem(
|
|
54
|
+
MON_SAFE_POS, W3cXmlDataTypes.BOOLEAN, MTPAccess.ReadAccess, None
|
|
55
|
+
),
|
|
56
|
+
MON_STAT_ERR: DataItem(
|
|
57
|
+
MON_STAT_ERR, W3cXmlDataTypes.BOOLEAN, MTPAccess.ReadAccess, None
|
|
58
|
+
),
|
|
59
|
+
MON_DYN_ERR: DataItem(
|
|
60
|
+
MON_DYN_ERR, W3cXmlDataTypes.BOOLEAN, MTPAccess.ReadAccess, None
|
|
61
|
+
),
|
|
62
|
+
MON_STAT_TI: DataItem(
|
|
63
|
+
MON_STAT_TI, W3cXmlDataTypes.FLOAT, MTPAccess.ReadAccess, None
|
|
64
|
+
),
|
|
65
|
+
MON_DYN_TI: DataItem(
|
|
66
|
+
MON_DYN_TI, W3cXmlDataTypes.FLOAT, MTPAccess.ReadAccess, None
|
|
67
|
+
),
|
|
68
|
+
POS_REACHED_FBK: DataItem(
|
|
69
|
+
POS_REACHED_FBK, W3cXmlDataTypes.BOOLEAN, MTPAccess.ReadAccess, None
|
|
70
|
+
),
|
|
71
|
+
POS_TOLERANCE: DataItem(
|
|
72
|
+
POS_TOLERANCE, W3cXmlDataTypes.FLOAT, MTPAccess.ReadAccess, None
|
|
73
|
+
),
|
|
74
|
+
MON_POS_TI: DataItem(
|
|
75
|
+
MON_POS_TI, W3cXmlDataTypes.FLOAT, MTPAccess.ReadAccess, None
|
|
76
|
+
),
|
|
77
|
+
MON_POS_ERR: DataItem(
|
|
78
|
+
MON_POS_ERR, W3cXmlDataTypes.BOOLEAN, MTPAccess.ReadAccess, None
|
|
79
|
+
),
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
monitored_values: MonAnaVlvValues
|
|
83
|
+
monitor_static_thread: threading.Thread
|
|
84
|
+
monitor_dynamic_thread: threading.Thread
|
|
85
|
+
monitor_pos_thread: threading.Thread
|
|
86
|
+
|
|
87
|
+
def __init__(
|
|
88
|
+
self,
|
|
89
|
+
tag_name,
|
|
90
|
+
tag_description="",
|
|
91
|
+
pos_min=0,
|
|
92
|
+
pos_max=100,
|
|
93
|
+
pos_scl_min=0,
|
|
94
|
+
pos_scl_max=100,
|
|
95
|
+
pos_unit=1342,
|
|
96
|
+
open_fbk_calc=True,
|
|
97
|
+
close_fbk_calc=True,
|
|
98
|
+
pos_fbk_calc=True,
|
|
99
|
+
safe_pos=0,
|
|
100
|
+
safe_pos_en=False,
|
|
101
|
+
perm_en=False,
|
|
102
|
+
intl_en=False,
|
|
103
|
+
prot_en=False,
|
|
104
|
+
mon_en=True,
|
|
105
|
+
mon_safe_pos=True,
|
|
106
|
+
mon_stat_ti=1,
|
|
107
|
+
mon_dyn_ti=1,
|
|
108
|
+
pos_tolerance=1,
|
|
109
|
+
mon_pos_ti=1,
|
|
110
|
+
) -> None:
|
|
111
|
+
super().__init__(
|
|
112
|
+
tag_name,
|
|
113
|
+
tag_description,
|
|
114
|
+
pos_min,
|
|
115
|
+
pos_max,
|
|
116
|
+
pos_scl_min,
|
|
117
|
+
pos_scl_max,
|
|
118
|
+
pos_unit,
|
|
119
|
+
open_fbk_calc,
|
|
120
|
+
close_fbk_calc,
|
|
121
|
+
pos_fbk_calc,
|
|
122
|
+
safe_pos,
|
|
123
|
+
safe_pos_en,
|
|
124
|
+
perm_en,
|
|
125
|
+
intl_en,
|
|
126
|
+
prot_en,
|
|
127
|
+
)
|
|
128
|
+
self.mtpObjectType = (
|
|
129
|
+
"MTPDataObjectSUCLib/DataAssembly/ActiveElement/AnaVlv/MonAnaVlv"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Initialize data items with dynamic values (using class-defined data items)
|
|
133
|
+
self[MonAnaVlv.MON_EN] = mon_en
|
|
134
|
+
self[MonAnaVlv.MON_SAFE_POS] = mon_safe_pos
|
|
135
|
+
self[MonAnaVlv.MON_STAT_ERR] = False
|
|
136
|
+
self[MonAnaVlv.MON_DYN_ERR] = False
|
|
137
|
+
self[MonAnaVlv.MON_STAT_TI] = mon_stat_ti
|
|
138
|
+
self[MonAnaVlv.MON_DYN_TI] = mon_dyn_ti
|
|
139
|
+
self[MonAnaVlv.POS_REACHED_FBK] = False
|
|
140
|
+
self[MonAnaVlv.POS_TOLERANCE] = pos_tolerance
|
|
141
|
+
self[MonAnaVlv.MON_POS_TI] = mon_pos_ti
|
|
142
|
+
self[MonAnaVlv.MON_POS_ERR] = False
|
|
143
|
+
|
|
144
|
+
self.monitored_values = MonAnaVlvValues(
|
|
145
|
+
self[MonAnaVlv.OPEN_AUT],
|
|
146
|
+
self[MonAnaVlv.OPEN_OP],
|
|
147
|
+
self[MonAnaVlv.CLOSE_AUT],
|
|
148
|
+
self[MonAnaVlv.CLOSE_OP],
|
|
149
|
+
self[MonAnaVlv.RESET_AUT],
|
|
150
|
+
self[MonAnaVlv.RESET_OP],
|
|
151
|
+
self[MonAnaVlv.POS_],
|
|
152
|
+
)
|
|
153
|
+
self.monitor_static_thread = None
|
|
154
|
+
self.monitor_dynamic_thread = None
|
|
155
|
+
self.monitor_pos_thread = None
|
|
156
|
+
|
|
157
|
+
def _go_save_pos(self):
|
|
158
|
+
if self[AnaVlv.SAFE_POS] == 1:
|
|
159
|
+
self._run_open_vlv()
|
|
160
|
+
safe_pos = self[AnaVlv.POS_MAX]
|
|
161
|
+
else:
|
|
162
|
+
self._run_close_vlv()
|
|
163
|
+
safe_pos = self[AnaVlv.POS_MIN]
|
|
164
|
+
|
|
165
|
+
self[AnaVlv.POS_] = safe_pos
|
|
166
|
+
logger.debug(f"Pos set to safe position {safe_pos}")
|
|
167
|
+
if self[MonAnaVlv.MON_EN]:
|
|
168
|
+
self.monitored_values.pos = safe_pos
|
|
169
|
+
self.monitor_pos_thread = threading.Thread(
|
|
170
|
+
target=self.monitor_position_reached, daemon=True
|
|
171
|
+
)
|
|
172
|
+
self.monitor_pos_thread.start()
|
|
173
|
+
|
|
174
|
+
if self[AnaVlv.POS_FBK_CALC]:
|
|
175
|
+
if self[MonAnaVlv.MON_EN]:
|
|
176
|
+
self.monitored_values.lock.acquire()
|
|
177
|
+
self[AnaVlv.POS_FBK] = safe_pos
|
|
178
|
+
self.monitored_values.lock.release()
|
|
179
|
+
else:
|
|
180
|
+
self[AnaVlv.POS_FBK] = safe_pos
|
|
181
|
+
|
|
182
|
+
def compare_states_control_signals(
|
|
183
|
+
self, monitor_time
|
|
184
|
+
) -> tuple[list[bool], list[bool]]:
|
|
185
|
+
"""
|
|
186
|
+
compare states of valve and control signals
|
|
187
|
+
|
|
188
|
+
Argument:
|
|
189
|
+
monitor_time: MonStatTi or MonDynTi times
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
results of states and control signals comparisons (a list including True or False)
|
|
193
|
+
"""
|
|
194
|
+
self.monitored_values.lock.acquire()
|
|
195
|
+
open_fbk1 = self[AnaVlv.OPEN_FBK]
|
|
196
|
+
close_fbk1 = self[AnaVlv.CLOSE_FBK]
|
|
197
|
+
open_op1 = self.monitored_values.open_op
|
|
198
|
+
open_aut1 = self.monitored_values.open_aut
|
|
199
|
+
close_op1 = self.monitored_values.close_op
|
|
200
|
+
close_aut1 = self.monitored_values.close_aut
|
|
201
|
+
reset_op1 = self.monitored_values.reset_op
|
|
202
|
+
reset_aut1 = self.monitored_values.reset_aut
|
|
203
|
+
self.monitored_values.lock.release()
|
|
204
|
+
|
|
205
|
+
sleep(monitor_time)
|
|
206
|
+
|
|
207
|
+
self.monitored_values.lock.acquire()
|
|
208
|
+
open_fbk2 = self[AnaVlv.OPEN_FBK]
|
|
209
|
+
close_fbk2 = self[AnaVlv.CLOSE_FBK]
|
|
210
|
+
open_op2 = self.monitored_values.open_op
|
|
211
|
+
open_aut2 = self.monitored_values.open_aut
|
|
212
|
+
close_op2 = self.monitored_values.close_op
|
|
213
|
+
close_aut2 = self.monitored_values.close_aut
|
|
214
|
+
reset_op2 = self.monitored_values.reset_op
|
|
215
|
+
reset_aut2 = self.monitored_values.reset_aut
|
|
216
|
+
self.monitored_values.lock.release()
|
|
217
|
+
|
|
218
|
+
control_signals_comparison = [
|
|
219
|
+
open_op1 == open_op2,
|
|
220
|
+
open_aut1 == open_aut2,
|
|
221
|
+
close_op1 == close_op2,
|
|
222
|
+
close_aut1 == close_aut2,
|
|
223
|
+
reset_op1 == reset_op2,
|
|
224
|
+
reset_aut1 == reset_aut2,
|
|
225
|
+
]
|
|
226
|
+
state_comparison = [open_fbk1 == open_fbk2, close_fbk1 == close_fbk2]
|
|
227
|
+
|
|
228
|
+
return state_comparison, control_signals_comparison
|
|
229
|
+
|
|
230
|
+
def monitor_static_error(self):
|
|
231
|
+
"""
|
|
232
|
+
monitor static error
|
|
233
|
+
"""
|
|
234
|
+
while True:
|
|
235
|
+
if self.monitored_values.stop_event_lock.is_set():
|
|
236
|
+
logger.debug("static monitoring stopped")
|
|
237
|
+
break
|
|
238
|
+
states, control_signals = self.compare_states_control_signals(
|
|
239
|
+
self[MonAnaVlv.MON_STAT_TI]
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
if not all(states): # if the states of valve changed
|
|
243
|
+
if all(control_signals): # but the control signals did not change
|
|
244
|
+
self[MonAnaVlv.MON_STAT_ERR] = True
|
|
245
|
+
logger.debug("Static error set to True")
|
|
246
|
+
self._handle_monitored_error()
|
|
247
|
+
|
|
248
|
+
def monitor_dynamic_error(self):
|
|
249
|
+
"""
|
|
250
|
+
monitor dynamic error
|
|
251
|
+
"""
|
|
252
|
+
while True:
|
|
253
|
+
if self.monitored_values.stop_event_lock.is_set():
|
|
254
|
+
logger.debug("dynamic monitoring stopped")
|
|
255
|
+
break
|
|
256
|
+
states, control_signals = self.compare_states_control_signals(
|
|
257
|
+
self[MonAnaVlv.MON_DYN_TI]
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
if all(states): # if the states of valve did not change
|
|
261
|
+
if not all(control_signals): # but a control command is executed
|
|
262
|
+
self[MonAnaVlv.MON_DYN_ERR] = True
|
|
263
|
+
logger.debug("Dynamic error set to True")
|
|
264
|
+
self._handle_monitored_error()
|
|
265
|
+
|
|
266
|
+
def monitor_position_reached(self):
|
|
267
|
+
"""
|
|
268
|
+
monitor position error
|
|
269
|
+
"""
|
|
270
|
+
pos = self.monitored_values.pos
|
|
271
|
+
sleep(self[MonAnaVlv.MON_POS_TI])
|
|
272
|
+
pos_reached_fbk = (
|
|
273
|
+
abs(self[AnaVlv.POS_FBK] - pos) <= self[MonAnaVlv.POS_TOLERANCE]
|
|
274
|
+
)
|
|
275
|
+
self[MonAnaVlv.POS_REACHED_FBK] = pos_reached_fbk
|
|
276
|
+
if not pos_reached_fbk:
|
|
277
|
+
self[MonAnaVlv.MON_POS_ERR] = True
|
|
278
|
+
logger.debug("position error set to True")
|
|
279
|
+
self._handle_monitored_error()
|
|
280
|
+
|
|
281
|
+
def _handle_monitored_error(self):
|
|
282
|
+
if self[MonAnaVlv.MON_SAFE_POS]:
|
|
283
|
+
logger.debug("set valve to safety position")
|
|
284
|
+
if self[AnaVlv.SAFE_POS_EN]:
|
|
285
|
+
self._go_save_pos()
|
|
286
|
+
else:
|
|
287
|
+
logger.debug("Device has no safe position")
|
|
288
|
+
self[AnaVlv.SAFE_POS_ACT] = True
|
|
289
|
+
|
|
290
|
+
def start_monitor(self):
|
|
291
|
+
if self[MonAnaVlv.MON_EN]:
|
|
292
|
+
self.monitor_static_thread = threading.Thread(
|
|
293
|
+
target=self.monitor_static_error, daemon=True
|
|
294
|
+
)
|
|
295
|
+
self.monitor_static_thread.start()
|
|
296
|
+
logger.debug("static monitoring start")
|
|
297
|
+
self.monitor_dynamic_thread = threading.Thread(
|
|
298
|
+
target=self.monitor_dynamic_error, daemon=True
|
|
299
|
+
)
|
|
300
|
+
self.monitor_dynamic_thread.start()
|
|
301
|
+
logger.debug("dynamic monitoring start")
|
|
302
|
+
|
|
303
|
+
def set_open_aut(self, value: bool):
|
|
304
|
+
logger.debug(f"OpenAut set to {value}")
|
|
305
|
+
if self.op_mode[OM.STATE_AUT_ACT]:
|
|
306
|
+
if value and self._run_allowed():
|
|
307
|
+
if self[MonAnaVlv.MON_EN]:
|
|
308
|
+
self.monitored_values.lock.acquire()
|
|
309
|
+
self.monitored_values.open_aut = value
|
|
310
|
+
self.monitored_values.lock.release()
|
|
311
|
+
self._run_open_vlv()
|
|
312
|
+
|
|
313
|
+
def set_close_aut(self, value: bool):
|
|
314
|
+
if self.op_mode[OM.STATE_AUT_ACT]:
|
|
315
|
+
if value and self._run_allowed():
|
|
316
|
+
if self[MonAnaVlv.MON_EN]:
|
|
317
|
+
self.monitored_values.lock.acquire()
|
|
318
|
+
self.monitored_values.close_aut = value
|
|
319
|
+
self.monitored_values.lock.release()
|
|
320
|
+
logger.debug(f"CloseAut set to {value}")
|
|
321
|
+
self._run_close_vlv()
|
|
322
|
+
|
|
323
|
+
def set_reset_aut(self, value: bool):
|
|
324
|
+
logger.debug(f"ResetAut set to {value}")
|
|
325
|
+
if self.op_mode[OM.STATE_AUT_ACT] and value:
|
|
326
|
+
if self[MonAnaVlv.MON_EN]:
|
|
327
|
+
self.monitored_values.lock.acquire()
|
|
328
|
+
self.monitored_values.reset_aut = value
|
|
329
|
+
self.monitored_values.lock.release()
|
|
330
|
+
self._reset_vlv()
|
|
331
|
+
|
|
332
|
+
def set_open_op(self, value: bool):
|
|
333
|
+
logger.debug(f"OpenOp set to {value}")
|
|
334
|
+
if self.op_mode[OM.STATE_OP_ACT]:
|
|
335
|
+
if value and self._run_allowed():
|
|
336
|
+
if self[MonAnaVlv.MON_EN]:
|
|
337
|
+
self.monitored_values.lock.acquire()
|
|
338
|
+
self.monitored_values.open_op = value
|
|
339
|
+
self.monitored_values.lock.release()
|
|
340
|
+
self._run_open_vlv()
|
|
341
|
+
self[AnaVlv.OPEN_OP] = False
|
|
342
|
+
|
|
343
|
+
def set_close_op(self, value: bool):
|
|
344
|
+
logger.debug(f"CloseOp set to {value}")
|
|
345
|
+
if self.op_mode[OM.STATE_OP_ACT]:
|
|
346
|
+
if value and self._run_allowed():
|
|
347
|
+
if self[MonAnaVlv.MON_EN]:
|
|
348
|
+
self.monitored_values.lock.acquire()
|
|
349
|
+
self.monitored_values.close_op = value
|
|
350
|
+
self.monitored_values.lock.release()
|
|
351
|
+
self._run_close_vlv()
|
|
352
|
+
self[AnaVlv.CLOSE_OP] = False
|
|
353
|
+
|
|
354
|
+
def set_reset_op(self, value: bool):
|
|
355
|
+
logger.debug(f"ResetOp set to {value}")
|
|
356
|
+
if self.op_mode[OM.STATE_OP_ACT] and value:
|
|
357
|
+
if self[MonAnaVlv.MON_EN]:
|
|
358
|
+
self.monitored_values.lock.acquire()
|
|
359
|
+
self.monitored_values.reset_op = value
|
|
360
|
+
self.monitored_values.lock.release()
|
|
361
|
+
self._reset_vlv()
|
|
362
|
+
self[AnaVlv.RESET_OP] = False
|
|
363
|
+
|
|
364
|
+
def _set_pos(self, value: float):
|
|
365
|
+
if self.valid_value(value):
|
|
366
|
+
# if SafePosAct is inactive -> manual or internal position specification
|
|
367
|
+
if self[AnaVlv.OPEN_ACT] and not self[AnaVlv.SAFE_POS_ACT]:
|
|
368
|
+
value = value
|
|
369
|
+
elif self[AnaVlv.CLOSE_ACT] and not self[AnaVlv.SAFE_POS_ACT]:
|
|
370
|
+
value = self[AnaVlv.POS_MIN]
|
|
371
|
+
|
|
372
|
+
# if SafePosAct is active, safety setting for the position is accepted
|
|
373
|
+
elif self[AnaVlv.SAFE_POS_ACT] or (
|
|
374
|
+
self[AnaVlv.PERM_EN] and self[AnaVlv.PERMIT_] == 0
|
|
375
|
+
):
|
|
376
|
+
logger.debug("manual or internal position specification inactive")
|
|
377
|
+
return
|
|
378
|
+
|
|
379
|
+
self[AnaVlv.POS_] = value
|
|
380
|
+
logger.debug(f"Pos set to {value}")
|
|
381
|
+
|
|
382
|
+
# start monitor_pos_thread to after pos has been changed
|
|
383
|
+
# if the position does not change, there is no need to check the desired position
|
|
384
|
+
if self[MonAnaVlv.MON_EN]:
|
|
385
|
+
self.monitored_values.pos = value
|
|
386
|
+
self.monitor_pos_thread = threading.Thread(
|
|
387
|
+
target=self.monitor_position_reached
|
|
388
|
+
)
|
|
389
|
+
self.monitor_pos_thread.start()
|
|
390
|
+
|
|
391
|
+
if self[AnaVlv.POS_FBK_CALC]:
|
|
392
|
+
if self[MonAnaVlv.MON_EN]:
|
|
393
|
+
self.monitored_values.lock.acquire()
|
|
394
|
+
self[AnaVlv.POS_FBK] = value
|
|
395
|
+
self.monitored_values.lock.release()
|
|
396
|
+
else:
|
|
397
|
+
self[AnaVlv.POS_FBK] = value
|
|
398
|
+
else:
|
|
399
|
+
logger.debug(f"Pos cannot be set to {value} (out of range)")
|
|
400
|
+
|
|
401
|
+
def set_stop_monitor(self):
|
|
402
|
+
self.monitored_values.stop_event_lock.set()
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"""Test Suite for MonAnaVlv."""
|
|
2
|
+
|
|
3
|
+
__copyright__ = (
|
|
4
|
+
"Copyright (c) 2026 Dresden University of Technology, Process-to-Order Group"
|
|
5
|
+
)
|
|
6
|
+
__license__ = "MIT"
|
|
7
|
+
import pytest
|
|
8
|
+
import time
|
|
9
|
+
|
|
10
|
+
from mtppy.utils.testing import data_object_set_modes
|
|
11
|
+
from mtppy.part3.data_assembly_set.active_elements.valves.mon_ana_vlv import MonAnaVlv
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def init_mon_ana_vlv(
|
|
15
|
+
init_op_mode="off",
|
|
16
|
+
init_src_mode="int",
|
|
17
|
+
open_fbk_calc=True,
|
|
18
|
+
close_fbk_calc=True,
|
|
19
|
+
pos_fbk_calc=True,
|
|
20
|
+
safe_pos=1,
|
|
21
|
+
safe_pos_en=True,
|
|
22
|
+
perm_en=False,
|
|
23
|
+
intl_en=False,
|
|
24
|
+
prot_en=False,
|
|
25
|
+
mon_en=True,
|
|
26
|
+
mon_safe_pos=True,
|
|
27
|
+
mon_stat_ti=1,
|
|
28
|
+
mon_dyn_ti=1,
|
|
29
|
+
pos_tolerance=1,
|
|
30
|
+
mon_pos_ti=1,
|
|
31
|
+
):
|
|
32
|
+
mon_ana_vlv = MonAnaVlv(
|
|
33
|
+
"tag",
|
|
34
|
+
tag_description="",
|
|
35
|
+
pos_min=2,
|
|
36
|
+
pos_max=10,
|
|
37
|
+
pos_scl_min=0,
|
|
38
|
+
pos_scl_max=10,
|
|
39
|
+
pos_unit=0,
|
|
40
|
+
open_fbk_calc=open_fbk_calc,
|
|
41
|
+
close_fbk_calc=close_fbk_calc,
|
|
42
|
+
pos_fbk_calc=pos_fbk_calc,
|
|
43
|
+
safe_pos=safe_pos,
|
|
44
|
+
safe_pos_en=safe_pos_en,
|
|
45
|
+
perm_en=perm_en,
|
|
46
|
+
intl_en=intl_en,
|
|
47
|
+
prot_en=prot_en,
|
|
48
|
+
mon_en=mon_en,
|
|
49
|
+
mon_safe_pos=mon_safe_pos,
|
|
50
|
+
mon_stat_ti=mon_stat_ti,
|
|
51
|
+
mon_dyn_ti=mon_dyn_ti,
|
|
52
|
+
pos_tolerance=pos_tolerance,
|
|
53
|
+
mon_pos_ti=mon_pos_ti,
|
|
54
|
+
)
|
|
55
|
+
data_object_set_modes(mon_ana_vlv, init_op_mode, init_src_mode)
|
|
56
|
+
return mon_ana_vlv
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
test_scenario_no_control_signals = [
|
|
60
|
+
("off", "int", "set_open_op"),
|
|
61
|
+
("off", "man", "set_open_op"),
|
|
62
|
+
("aut", "int", "set_open_op"),
|
|
63
|
+
("aut", "man", "set_open_op"),
|
|
64
|
+
("off", "int", "set_open_aut"),
|
|
65
|
+
("off", "man", "set_open_aut"),
|
|
66
|
+
("op", "int", "set_open_aut"),
|
|
67
|
+
("op", "man", "set_open_aut"),
|
|
68
|
+
("off", "int", "set_close_op"),
|
|
69
|
+
("off", "man", "set_close_op"),
|
|
70
|
+
("aut", "int", "set_close_op"),
|
|
71
|
+
("aut", "man", "set_close_op"),
|
|
72
|
+
("off", "int", "set_close_aut"),
|
|
73
|
+
("off", "man", "set_close_aut"),
|
|
74
|
+
("op", "int", "set_close_aut"),
|
|
75
|
+
("op", "man", "set_close_aut"),
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@pytest.mark.parametrize(
|
|
80
|
+
"op_mode, src_mode, set_command", test_scenario_no_control_signals
|
|
81
|
+
)
|
|
82
|
+
def test_static_error(op_mode, src_mode, set_command):
|
|
83
|
+
for command in [True, False]:
|
|
84
|
+
mon_ana_vlv = init_mon_ana_vlv(init_op_mode=op_mode, init_src_mode=src_mode)
|
|
85
|
+
try:
|
|
86
|
+
mon_ana_vlv.start_monitor()
|
|
87
|
+
eval(f"mon_ana_vlv.{set_command}({command})")
|
|
88
|
+
print(f"Scenario: mode {op_mode} {src_mode}, {set_command}")
|
|
89
|
+
|
|
90
|
+
# mimic an error situation: OpenFbk changes to True without any change in control signal
|
|
91
|
+
mon_ana_vlv["OpenFbk"] = True
|
|
92
|
+
time.sleep(1.1) # monitor time has expired (> 1s)
|
|
93
|
+
|
|
94
|
+
assert mon_ana_vlv["MonStatErr"] == True
|
|
95
|
+
assert mon_ana_vlv["MonDynErr"] == False
|
|
96
|
+
assert mon_ana_vlv["SafePosAct"] == True
|
|
97
|
+
assert mon_ana_vlv["OpenFbk"] == True # safe position of valve is open
|
|
98
|
+
|
|
99
|
+
time.sleep(0.5)
|
|
100
|
+
|
|
101
|
+
# after valve is set to safe position (open), the OpenFbk becomes to False --> another static error
|
|
102
|
+
mon_ana_vlv["OpenFbk"] = False
|
|
103
|
+
time.sleep(1.1) # monitor time has expired (> 1s)
|
|
104
|
+
|
|
105
|
+
assert mon_ana_vlv["MonStatErr"] == True
|
|
106
|
+
assert mon_ana_vlv["MonDynErr"] == False
|
|
107
|
+
assert mon_ana_vlv["SafePosAct"] == True
|
|
108
|
+
assert mon_ana_vlv["OpenFbk"] == True # safe position of valve is open
|
|
109
|
+
finally:
|
|
110
|
+
mon_ana_vlv.set_stop_monitor()
|
|
111
|
+
mon_ana_vlv.monitor_static_thread.join()
|
|
112
|
+
mon_ana_vlv.monitor_dynamic_thread.join()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@pytest.mark.parametrize(
|
|
116
|
+
"op_mode, src_mode, set_command", test_scenario_no_control_signals
|
|
117
|
+
)
|
|
118
|
+
def test_static_error_within_monitor_time(op_mode, src_mode, set_command):
|
|
119
|
+
"""If the required state is set on the interface before the time has expired (wait tme < 1s in this test),
|
|
120
|
+
there is no error"""
|
|
121
|
+
|
|
122
|
+
for command in [True, False]:
|
|
123
|
+
mon_ana_vlv = init_mon_ana_vlv(init_op_mode=op_mode, init_src_mode=src_mode)
|
|
124
|
+
try:
|
|
125
|
+
mon_ana_vlv.start_monitor()
|
|
126
|
+
eval(f"mon_ana_vlv.{set_command}({command})")
|
|
127
|
+
print(f"Scenario: mode {op_mode} {src_mode}, {set_command}")
|
|
128
|
+
|
|
129
|
+
# mimic an error situation: OpenFbk changes to True without any change in control signal
|
|
130
|
+
mon_ana_vlv["OpenFbk"] = True
|
|
131
|
+
time.sleep(0.6) # monitor time has not expired (1s)
|
|
132
|
+
|
|
133
|
+
assert mon_ana_vlv["MonStatErr"] == False
|
|
134
|
+
assert mon_ana_vlv["MonDynErr"] == False
|
|
135
|
+
assert mon_ana_vlv["SafePosAct"] == False
|
|
136
|
+
finally:
|
|
137
|
+
mon_ana_vlv.set_stop_monitor()
|
|
138
|
+
mon_ana_vlv.monitor_static_thread.join()
|
|
139
|
+
mon_ana_vlv.monitor_dynamic_thread.join()
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
test_scenario_control_signals = [
|
|
143
|
+
("op", "int", "set_open_op", "set_close_op"),
|
|
144
|
+
("op", "man", "set_open_op", "set_close_op"),
|
|
145
|
+
("aut", "int", "set_open_aut", "set_close_aut"),
|
|
146
|
+
("aut", "man", "set_open_aut", "set_close_aut"),
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@pytest.mark.parametrize(
|
|
151
|
+
"op_mode, src_mode, open_command, close_command", test_scenario_control_signals
|
|
152
|
+
)
|
|
153
|
+
def test_dynamic_error_open_close(op_mode, src_mode, open_command, close_command):
|
|
154
|
+
mon_ana_vlv = init_mon_ana_vlv(init_op_mode=op_mode, init_src_mode=src_mode)
|
|
155
|
+
try:
|
|
156
|
+
mon_ana_vlv.start_monitor()
|
|
157
|
+
eval(f"mon_ana_vlv.{open_command}(True)")
|
|
158
|
+
print(f"Scenario: mode {op_mode} {src_mode}, {open_command}")
|
|
159
|
+
|
|
160
|
+
# mimic an error situation: OpenFbk does not change to True after control signal has changed to open
|
|
161
|
+
mon_ana_vlv["OpenFbk"] = False
|
|
162
|
+
time.sleep(1.1) # monitor time has expired (> 1s)
|
|
163
|
+
|
|
164
|
+
assert mon_ana_vlv["MonDynErr"] == True
|
|
165
|
+
assert mon_ana_vlv["MonStatErr"] == False
|
|
166
|
+
assert mon_ana_vlv["SafePosAct"] == True
|
|
167
|
+
assert mon_ana_vlv["OpenFbk"] == True # safe position of valve is open
|
|
168
|
+
|
|
169
|
+
eval(f"mon_ana_vlv.{close_command}(True)")
|
|
170
|
+
print(f"Scenario: mode {op_mode} {src_mode}, {close_command}")
|
|
171
|
+
|
|
172
|
+
# mimic an error situation: OpenFbk does not change to False after control signal has changed to close
|
|
173
|
+
mon_ana_vlv["OpenFbk"] = True
|
|
174
|
+
time.sleep(1.1) # monitor time has expired (> 1s)
|
|
175
|
+
|
|
176
|
+
assert mon_ana_vlv["MonDynErr"] == True
|
|
177
|
+
assert mon_ana_vlv["MonStatErr"] == False
|
|
178
|
+
assert mon_ana_vlv["SafePosAct"] == True
|
|
179
|
+
assert mon_ana_vlv["OpenFbk"] == True # safe position of valve is open
|
|
180
|
+
finally:
|
|
181
|
+
mon_ana_vlv.set_stop_monitor()
|
|
182
|
+
mon_ana_vlv.monitor_static_thread.join()
|
|
183
|
+
mon_ana_vlv.monitor_dynamic_thread.join()
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
test_scenario_pos = [
|
|
187
|
+
("op", "int", "set_pos_int"),
|
|
188
|
+
("aut", "int", "set_pos_int"),
|
|
189
|
+
("op", "man", "set_pos_man"),
|
|
190
|
+
("aut", "man", "set_pos_man"),
|
|
191
|
+
]
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@pytest.mark.parametrize("op_mode, src_mode, set_command", test_scenario_pos)
|
|
195
|
+
def test_pos_open(op_mode, src_mode, set_command):
|
|
196
|
+
for pos_value in [-2, 4, 7, 10, 100]: # pos min is 2, pos max is 10
|
|
197
|
+
mon_ana_vlv = init_mon_ana_vlv(
|
|
198
|
+
init_op_mode=op_mode, init_src_mode=src_mode, pos_fbk_calc=True
|
|
199
|
+
)
|
|
200
|
+
if op_mode == "op":
|
|
201
|
+
mon_ana_vlv.set_open_op(True)
|
|
202
|
+
elif op_mode == "aut":
|
|
203
|
+
mon_ana_vlv.set_open_aut(True)
|
|
204
|
+
eval(f"mon_ana_vlv.{set_command}({pos_value})")
|
|
205
|
+
print(
|
|
206
|
+
f"Scenario: modes=({op_mode},{src_mode}), cmd={set_command}, pos={pos_value}"
|
|
207
|
+
)
|
|
208
|
+
# position tolerance is 1, pos_fbk is 2.5 --> pos error
|
|
209
|
+
mon_ana_vlv["PosFbk"] = 7.5
|
|
210
|
+
time.sleep(1.5) # time for monitoring pos error is 1s
|
|
211
|
+
if 2 <= pos_value < 6 or 8 < pos_value <= 10: # out of the pos tolerance range
|
|
212
|
+
assert mon_ana_vlv["PosReachedFbk"] == False
|
|
213
|
+
assert mon_ana_vlv["MonPosErr"] == True
|
|
214
|
+
assert mon_ana_vlv["SafePosAct"] == True
|
|
215
|
+
assert mon_ana_vlv["OpenAct"] == True
|
|
216
|
+
|
|
217
|
+
elif 6 <= pos_value <= 8: # within the pos tolerance range
|
|
218
|
+
assert mon_ana_vlv["PosReachedFbk"] == True
|
|
219
|
+
assert mon_ana_vlv["MonPosErr"] == False
|
|
220
|
+
assert mon_ana_vlv["SafePosAct"] == False
|
|
221
|
+
|
|
222
|
+
else:
|
|
223
|
+
assert mon_ana_vlv["PosReachedFbk"] == False
|
|
224
|
+
assert mon_ana_vlv["MonPosErr"] == False
|
|
225
|
+
assert mon_ana_vlv["SafePosAct"] == False
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
test_scenario_control_signals_reset = [
|
|
229
|
+
("op", "int", "set_reset_op"),
|
|
230
|
+
("op", "man", "set_reset_op"),
|
|
231
|
+
("aut", "int", "set_reset_aut"),
|
|
232
|
+
("aut", "man", "set_reset_aut"),
|
|
233
|
+
]
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
@pytest.mark.parametrize(
|
|
237
|
+
"op_mode, src_mode, set_command", test_scenario_control_signals_reset
|
|
238
|
+
)
|
|
239
|
+
def test_dynamic_error_reset(op_mode, src_mode, set_command):
|
|
240
|
+
mon_ana_vlv = init_mon_ana_vlv(init_op_mode=op_mode, init_src_mode=src_mode)
|
|
241
|
+
try:
|
|
242
|
+
mon_ana_vlv.start_monitor()
|
|
243
|
+
# set valve to open
|
|
244
|
+
if op_mode == "op":
|
|
245
|
+
mon_ana_vlv.set_open_op(True)
|
|
246
|
+
elif op_mode == "aut":
|
|
247
|
+
mon_ana_vlv.set_open_aut(True)
|
|
248
|
+
time.sleep(1.1)
|
|
249
|
+
|
|
250
|
+
# reset valve
|
|
251
|
+
eval(f"mon_ana_vlv.{set_command}(True)")
|
|
252
|
+
print(f"Scenario: mode {op_mode} {src_mode}, {set_command}")
|
|
253
|
+
|
|
254
|
+
# mimic an error situation: OpenFbk does not change to False after reset changes to True
|
|
255
|
+
mon_ana_vlv["OpenFbk"] = True
|
|
256
|
+
time.sleep(1.1)
|
|
257
|
+
|
|
258
|
+
assert mon_ana_vlv["MonDynErr"] == True
|
|
259
|
+
assert mon_ana_vlv["MonStatErr"] == False
|
|
260
|
+
assert mon_ana_vlv["SafePosAct"] == True
|
|
261
|
+
assert mon_ana_vlv["OpenFbk"] == True # safe position of valve is open
|
|
262
|
+
finally:
|
|
263
|
+
mon_ana_vlv.set_stop_monitor()
|
|
264
|
+
mon_ana_vlv.monitor_static_thread.join()
|
|
265
|
+
mon_ana_vlv.monitor_dynamic_thread.join()
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
if __name__ == "__main__":
|
|
269
|
+
# This allows running the tests directly if needed
|
|
270
|
+
pytest.main([__file__, "-v"])
|