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,295 @@
|
|
|
1
|
+
"""Implementation of MonBinDrv."""
|
|
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.part3.data_assembly_set.active_elements.drives.bin_drv import BinDrv
|
|
11
|
+
from mtppy.utils.logging import logger
|
|
12
|
+
import threading
|
|
13
|
+
from time import sleep
|
|
14
|
+
|
|
15
|
+
from mtppy.utils.data_item import DataItem
|
|
16
|
+
from mtppy.part3.definitions.mtp_access import MTPAccess
|
|
17
|
+
from mtppy.part5.definitions.w3c_xml_data_types import W3cXmlDataTypes
|
|
18
|
+
from mtppy.part3.data_assembly_set.base_functions.operation_mode import (
|
|
19
|
+
OperationMode as OM,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class MonBinDrvValues:
|
|
24
|
+
def __init__(
|
|
25
|
+
self, fwd_aut, fwd_op, rev_aut, rev_op, stop_aut, stop_op, reset_aut, reset_op
|
|
26
|
+
):
|
|
27
|
+
self.fwd_aut = fwd_aut
|
|
28
|
+
self.fwd_op = fwd_op
|
|
29
|
+
self.rev_aut = rev_aut
|
|
30
|
+
self.rev_op = rev_op
|
|
31
|
+
self.stop_aut = stop_aut
|
|
32
|
+
self.stop_op = stop_op
|
|
33
|
+
self.reset_aut = reset_aut
|
|
34
|
+
self.reset_op = reset_op
|
|
35
|
+
self.lock = threading.Lock()
|
|
36
|
+
self.stop_event_lock = threading.Event()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class MonBinDrv(BinDrv):
|
|
40
|
+
# DataItem constant definitions
|
|
41
|
+
MON_EN = "MonEn"
|
|
42
|
+
MON_SAFE_POS = "MonSafePos"
|
|
43
|
+
MON_STAT_ERR = "MonStatErr"
|
|
44
|
+
MON_DYN_ERR = "MonDynErr"
|
|
45
|
+
MON_STAT_TI = "MonStatTi"
|
|
46
|
+
MON_DYN_TI = "MonDynTi"
|
|
47
|
+
|
|
48
|
+
# Class-level data items dictionary
|
|
49
|
+
CLASS_DATA_ITEMS: ClassVar = {
|
|
50
|
+
MON_EN: DataItem(MON_EN, W3cXmlDataTypes.BOOLEAN, MTPAccess.ReadAccess, None),
|
|
51
|
+
MON_SAFE_POS: DataItem(
|
|
52
|
+
MON_SAFE_POS, W3cXmlDataTypes.BOOLEAN, MTPAccess.ReadAccess, None
|
|
53
|
+
),
|
|
54
|
+
MON_STAT_ERR: DataItem(
|
|
55
|
+
MON_STAT_ERR, W3cXmlDataTypes.BOOLEAN, MTPAccess.ReadAccess, None
|
|
56
|
+
),
|
|
57
|
+
MON_DYN_ERR: DataItem(
|
|
58
|
+
MON_DYN_ERR, W3cXmlDataTypes.BOOLEAN, MTPAccess.ReadAccess, None
|
|
59
|
+
),
|
|
60
|
+
MON_STAT_TI: DataItem(
|
|
61
|
+
MON_STAT_TI, W3cXmlDataTypes.FLOAT, MTPAccess.ReadAccess, None
|
|
62
|
+
),
|
|
63
|
+
MON_DYN_TI: DataItem(
|
|
64
|
+
MON_DYN_TI, W3cXmlDataTypes.FLOAT, MTPAccess.ReadAccess, None
|
|
65
|
+
),
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
monitored_values: MonBinDrvValues
|
|
69
|
+
monitor_static_thread: threading.Thread
|
|
70
|
+
monitor_dynamic_thread: threading.Thread
|
|
71
|
+
|
|
72
|
+
def __init__(
|
|
73
|
+
self,
|
|
74
|
+
tag_name: str,
|
|
75
|
+
tag_description: str = "",
|
|
76
|
+
rev_fbk_calc: bool = True,
|
|
77
|
+
fwd_fbk_calc: bool = True,
|
|
78
|
+
safe_pos: int = 0,
|
|
79
|
+
fwd_en: bool = True,
|
|
80
|
+
rev_en: bool = False,
|
|
81
|
+
perm_en: bool = False,
|
|
82
|
+
intl_en: bool = False,
|
|
83
|
+
prot_en: bool = False,
|
|
84
|
+
mon_en: bool = True,
|
|
85
|
+
mon_safe_pos: bool = True,
|
|
86
|
+
mon_stat_ti: float = 1,
|
|
87
|
+
mon_dyn_ti: float = 1,
|
|
88
|
+
) -> None:
|
|
89
|
+
super().__init__(
|
|
90
|
+
tag_name,
|
|
91
|
+
tag_description,
|
|
92
|
+
rev_fbk_calc,
|
|
93
|
+
fwd_fbk_calc,
|
|
94
|
+
safe_pos,
|
|
95
|
+
fwd_en,
|
|
96
|
+
rev_en,
|
|
97
|
+
perm_en,
|
|
98
|
+
intl_en,
|
|
99
|
+
prot_en,
|
|
100
|
+
)
|
|
101
|
+
self.mtpObjectType = (
|
|
102
|
+
"MTPDataObjectSUCLib/DataAssembly/ActiveElement/BinDrv/MonBinDrv"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Initialize data items with dynamic values (using class-defined data items)
|
|
106
|
+
self[MonBinDrv.MON_EN] = mon_en
|
|
107
|
+
self[MonBinDrv.MON_SAFE_POS] = mon_safe_pos
|
|
108
|
+
self[MonBinDrv.MON_STAT_ERR] = False
|
|
109
|
+
self[MonBinDrv.MON_DYN_ERR] = False
|
|
110
|
+
self[MonBinDrv.MON_STAT_TI] = mon_stat_ti
|
|
111
|
+
self[MonBinDrv.MON_DYN_TI] = mon_dyn_ti
|
|
112
|
+
|
|
113
|
+
self.monitored_values = MonBinDrvValues(
|
|
114
|
+
self[BinDrv.FWD_AUT],
|
|
115
|
+
self[BinDrv.FWD_OP],
|
|
116
|
+
self[BinDrv.REV_AUT],
|
|
117
|
+
self[BinDrv.REV_OP],
|
|
118
|
+
self[BinDrv.STOP_AUT],
|
|
119
|
+
self[BinDrv.STOP_OP],
|
|
120
|
+
self[BinDrv.RESET_AUT],
|
|
121
|
+
self[BinDrv.RESET_OP],
|
|
122
|
+
)
|
|
123
|
+
self.monitor_static_thread = None
|
|
124
|
+
self.monitor_dynamic_thread = None
|
|
125
|
+
|
|
126
|
+
def compare_states_control_signals(self, monitor_time):
|
|
127
|
+
|
|
128
|
+
self.monitored_values.lock.acquire()
|
|
129
|
+
fwd_fbk1 = self[BinDrv.FWD_FBK]
|
|
130
|
+
rev_fbk1 = self[BinDrv.REV_FBK]
|
|
131
|
+
fwd_op1 = self.monitored_values.fwd_op
|
|
132
|
+
fwd_aut1 = self.monitored_values.fwd_aut
|
|
133
|
+
rev_op1 = self.monitored_values.rev_op
|
|
134
|
+
rev_aut1 = self.monitored_values.rev_aut
|
|
135
|
+
stop_op1 = self.monitored_values.stop_op
|
|
136
|
+
stop_aut1 = self.monitored_values.stop_aut
|
|
137
|
+
reset_op1 = self.monitored_values.reset_op
|
|
138
|
+
reset_aut1 = self.monitored_values.reset_aut
|
|
139
|
+
self.monitored_values.lock.release()
|
|
140
|
+
|
|
141
|
+
sleep(monitor_time)
|
|
142
|
+
|
|
143
|
+
self.monitored_values.lock.acquire()
|
|
144
|
+
fwd_fbk2 = self[BinDrv.FWD_FBK]
|
|
145
|
+
rev_fbk2 = self[BinDrv.REV_FBK]
|
|
146
|
+
fwd_op2 = self.monitored_values.fwd_op
|
|
147
|
+
fwd_aut2 = self.monitored_values.fwd_aut
|
|
148
|
+
rev_op2 = self.monitored_values.rev_op
|
|
149
|
+
rev_aut2 = self.monitored_values.rev_aut
|
|
150
|
+
stop_op2 = self.monitored_values.stop_op
|
|
151
|
+
stop_aut2 = self.monitored_values.stop_aut
|
|
152
|
+
reset_op2 = self.monitored_values.reset_op
|
|
153
|
+
reset_aut2 = self.monitored_values.reset_aut
|
|
154
|
+
self.monitored_values.lock.release()
|
|
155
|
+
|
|
156
|
+
control_signals_comparison = [
|
|
157
|
+
fwd_op1 == fwd_op2,
|
|
158
|
+
fwd_aut1 == fwd_aut2,
|
|
159
|
+
rev_op1 == rev_op2,
|
|
160
|
+
rev_aut1 == rev_aut2,
|
|
161
|
+
stop_op1 == stop_op2,
|
|
162
|
+
stop_aut1 == stop_aut2,
|
|
163
|
+
reset_op1 == reset_op2,
|
|
164
|
+
reset_aut1 == reset_aut2,
|
|
165
|
+
]
|
|
166
|
+
state_comparison = [fwd_fbk1 == fwd_fbk2, rev_fbk1 == rev_fbk2]
|
|
167
|
+
return state_comparison, control_signals_comparison
|
|
168
|
+
|
|
169
|
+
def monitor_static_error(self):
|
|
170
|
+
while not (self.monitored_values.stop_event_lock.is_set()):
|
|
171
|
+
states, control_signals = self.compare_states_control_signals(
|
|
172
|
+
self[MonBinDrv.MON_STAT_TI]
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
if not (all(states)) and all(control_signals):
|
|
176
|
+
self[MonBinDrv.MON_STAT_ERR] = True
|
|
177
|
+
logger.debug("Static error set to True")
|
|
178
|
+
self._handle_monitored_error()
|
|
179
|
+
|
|
180
|
+
logger.debug("static monitoring stopped")
|
|
181
|
+
|
|
182
|
+
def monitor_dynamic_error(self):
|
|
183
|
+
while not (self.monitored_values.stop_event_lock.is_set()):
|
|
184
|
+
states, control_signals = self.compare_states_control_signals(
|
|
185
|
+
self[MonBinDrv.MON_DYN_TI]
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
if all(states) and not (all(control_signals)):
|
|
189
|
+
self[MonBinDrv.MON_DYN_ERR] = True
|
|
190
|
+
logger.debug("Dynamic error set to True")
|
|
191
|
+
self._handle_monitored_error()
|
|
192
|
+
|
|
193
|
+
logger.debug("dynamic monitoring stopped")
|
|
194
|
+
|
|
195
|
+
def _handle_monitored_error(self):
|
|
196
|
+
logger.debug("set valve to safety position")
|
|
197
|
+
self._go_safe_pos()
|
|
198
|
+
self[BinDrv.SAFE_POS_ACT] = True
|
|
199
|
+
|
|
200
|
+
def start_monitor(self):
|
|
201
|
+
if self[MonBinDrv.MON_EN]:
|
|
202
|
+
self.monitor_static_thread = threading.Thread(
|
|
203
|
+
target=self.monitor_static_error, daemon=True
|
|
204
|
+
)
|
|
205
|
+
self.monitor_static_thread.start()
|
|
206
|
+
logger.debug("static monitoring start")
|
|
207
|
+
self.monitor_dynamic_thread = threading.Thread(
|
|
208
|
+
target=self.monitor_dynamic_error, daemon=True
|
|
209
|
+
)
|
|
210
|
+
self.monitor_dynamic_thread.start()
|
|
211
|
+
logger.debug("dynamic monitoring start")
|
|
212
|
+
|
|
213
|
+
def set_fwd_op(self, value: bool):
|
|
214
|
+
logger.debug(f"FwdOp set to {value}")
|
|
215
|
+
if self.op_mode[OM.STATE_OP_ACT] and self[BinDrv.FWD_EN]:
|
|
216
|
+
if value and self._run_allowed():
|
|
217
|
+
self._run_fwd_drv()
|
|
218
|
+
if self[MonBinDrv.MON_EN]:
|
|
219
|
+
self.monitored_values.lock.acquire()
|
|
220
|
+
self.monitored_values.fwd_op = value
|
|
221
|
+
self.monitored_values.lock.release()
|
|
222
|
+
self[BinDrv.FWD_OP] = False
|
|
223
|
+
|
|
224
|
+
def set_rev_op(self, value: bool):
|
|
225
|
+
logger.debug(f"RevOp set to {value}")
|
|
226
|
+
if self.op_mode[OM.STATE_OP_ACT] and self[BinDrv.REV_EN]:
|
|
227
|
+
if value and self._run_allowed():
|
|
228
|
+
|
|
229
|
+
if self[MonBinDrv.MON_EN]:
|
|
230
|
+
self.monitored_values.lock.acquire()
|
|
231
|
+
self.monitored_values.rev_op = value
|
|
232
|
+
self.monitored_values.lock.release()
|
|
233
|
+
self._run_rev_drv()
|
|
234
|
+
self[BinDrv.REV_OP] = False
|
|
235
|
+
|
|
236
|
+
def set_stop_op(self, value: bool):
|
|
237
|
+
logger.debug(f"StopOp set to {value}")
|
|
238
|
+
if self.op_mode[OM.STATE_OP_ACT] and value:
|
|
239
|
+
if self[MonBinDrv.MON_EN]:
|
|
240
|
+
self.monitored_values.lock.acquire()
|
|
241
|
+
self.monitored_values.stop_op = value
|
|
242
|
+
self.monitored_values.lock.release()
|
|
243
|
+
self._stop_drv()
|
|
244
|
+
self[BinDrv.STOP_OP] = False
|
|
245
|
+
|
|
246
|
+
def set_reset_op(self, value: bool):
|
|
247
|
+
logger.debug(f"ResetOp set to {value}")
|
|
248
|
+
if self.op_mode[OM.STATE_OP_ACT] and value:
|
|
249
|
+
if self[MonBinDrv.MON_EN]:
|
|
250
|
+
self.monitored_values.lock.acquire()
|
|
251
|
+
self.monitored_values.reset_op = value
|
|
252
|
+
self.monitored_values.lock.release()
|
|
253
|
+
self._reset_drv()
|
|
254
|
+
self[BinDrv.RESET_OP] = False
|
|
255
|
+
|
|
256
|
+
def set_fwd_aut(self, value: bool):
|
|
257
|
+
logger.debug(f"FwdAut set to {value}")
|
|
258
|
+
if self.op_mode[OM.STATE_AUT_ACT] and self[BinDrv.FWD_EN]:
|
|
259
|
+
if value and self._run_allowed():
|
|
260
|
+
if self[MonBinDrv.MON_EN]:
|
|
261
|
+
self.monitored_values.lock.acquire()
|
|
262
|
+
self.monitored_values.fwd_aut = value
|
|
263
|
+
self.monitored_values.lock.release()
|
|
264
|
+
self._run_fwd_drv()
|
|
265
|
+
|
|
266
|
+
def set_rev_aut(self, value: bool):
|
|
267
|
+
logger.debug(f"RevAut set to {value}")
|
|
268
|
+
if self.op_mode[OM.STATE_AUT_ACT] and self[BinDrv.REV_EN]:
|
|
269
|
+
if value and self._run_allowed():
|
|
270
|
+
if self[MonBinDrv.MON_EN]:
|
|
271
|
+
self.monitored_values.lock.acquire()
|
|
272
|
+
self.monitored_values.rev_aut = value
|
|
273
|
+
self.monitored_values.lock.release()
|
|
274
|
+
self._run_rev_drv()
|
|
275
|
+
|
|
276
|
+
def set_stop_aut(self, value: bool):
|
|
277
|
+
logger.debug(f"StopAut set to {value}")
|
|
278
|
+
if self.op_mode[OM.STATE_AUT_ACT] and value:
|
|
279
|
+
if self[MonBinDrv.MON_EN]:
|
|
280
|
+
self.monitored_values.lock.acquire()
|
|
281
|
+
self.monitored_values.stop_aut = value
|
|
282
|
+
self.monitored_values.lock.release()
|
|
283
|
+
self._stop_drv()
|
|
284
|
+
|
|
285
|
+
def set_reset_aut(self, value: bool):
|
|
286
|
+
logger.debug(f"ResetAut set to {value}")
|
|
287
|
+
if self.op_mode[OM.STATE_AUT_ACT] and value:
|
|
288
|
+
if self[MonBinDrv.MON_EN]:
|
|
289
|
+
self.monitored_values.lock.acquire()
|
|
290
|
+
self.monitored_values.reset_aut = value
|
|
291
|
+
self.monitored_values.lock.release()
|
|
292
|
+
self._reset_drv()
|
|
293
|
+
|
|
294
|
+
def set_stop_monitor(self):
|
|
295
|
+
self.monitored_values.stop_event_lock.set()
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"""Test Suite for MonBinDrv."""
|
|
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.drives.mon_bin_drv import MonBinDrv
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def init_mon_bin_drv(
|
|
15
|
+
init_op_mode="off",
|
|
16
|
+
init_src_mode="int",
|
|
17
|
+
rev_fbk_calc=True,
|
|
18
|
+
fwd_fbk_calc=True,
|
|
19
|
+
safe_pos=1,
|
|
20
|
+
fwd_en=True,
|
|
21
|
+
rev_en=True,
|
|
22
|
+
perm_en=False,
|
|
23
|
+
intl_en=False,
|
|
24
|
+
prot_en=False,
|
|
25
|
+
mon_en=True,
|
|
26
|
+
mon_safe_pos=1,
|
|
27
|
+
mon_stat_ti=0.5,
|
|
28
|
+
mon_dyn_ti=0.5,
|
|
29
|
+
) -> MonBinDrv:
|
|
30
|
+
instance = MonBinDrv(
|
|
31
|
+
"tag",
|
|
32
|
+
tag_description="",
|
|
33
|
+
rev_fbk_calc=rev_fbk_calc,
|
|
34
|
+
fwd_fbk_calc=fwd_fbk_calc,
|
|
35
|
+
safe_pos=safe_pos,
|
|
36
|
+
fwd_en=fwd_en,
|
|
37
|
+
rev_en=rev_en,
|
|
38
|
+
perm_en=perm_en,
|
|
39
|
+
intl_en=intl_en,
|
|
40
|
+
prot_en=prot_en,
|
|
41
|
+
mon_en=mon_en,
|
|
42
|
+
mon_safe_pos=mon_safe_pos,
|
|
43
|
+
mon_stat_ti=mon_stat_ti,
|
|
44
|
+
mon_dyn_ti=mon_dyn_ti,
|
|
45
|
+
)
|
|
46
|
+
data_object_set_modes(instance, init_op_mode, init_src_mode)
|
|
47
|
+
return instance
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
test_scenario_no_control_signals = [
|
|
51
|
+
("off", "int", "set_fwd_op"),
|
|
52
|
+
("off", "man", "set_fwd_op"),
|
|
53
|
+
("aut", "int", "set_fwd_op"),
|
|
54
|
+
("aut", "man", "set_fwd_op"),
|
|
55
|
+
("off", "int", "set_fwd_aut"),
|
|
56
|
+
("off", "man", "set_fwd_aut"),
|
|
57
|
+
("op", "int", "set_fwd_aut"),
|
|
58
|
+
("op", "man", "set_fwd_aut"),
|
|
59
|
+
("off", "int", "set_rev_op"),
|
|
60
|
+
("off", "man", "set_rev_op"),
|
|
61
|
+
("aut", "int", "set_rev_op"),
|
|
62
|
+
("aut", "man", "set_rev_op"),
|
|
63
|
+
("off", "int", "set_rev_aut"),
|
|
64
|
+
("off", "man", "set_rev_aut"),
|
|
65
|
+
("op", "int", "set_rev_aut"),
|
|
66
|
+
("op", "man", "set_rev_aut"),
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@pytest.mark.parametrize(
|
|
71
|
+
"op_mode, src_mode, set_command", test_scenario_no_control_signals
|
|
72
|
+
)
|
|
73
|
+
def test_static_error(op_mode, src_mode, set_command):
|
|
74
|
+
for command in [True, False]:
|
|
75
|
+
mon_bin_drv = init_mon_bin_drv(init_op_mode=op_mode, init_src_mode=src_mode)
|
|
76
|
+
|
|
77
|
+
mon_bin_drv.start_monitor()
|
|
78
|
+
try:
|
|
79
|
+
eval(f"mon_bin_drv.{set_command}({command})")
|
|
80
|
+
print(f"Scenario: mode {op_mode} {src_mode}, {set_command}")
|
|
81
|
+
|
|
82
|
+
mon_bin_drv["FwdFbk"] = True # FwdFbk becomes True w/o control signals
|
|
83
|
+
time.sleep(0.6)
|
|
84
|
+
|
|
85
|
+
assert mon_bin_drv["MonStatErr"] == True
|
|
86
|
+
assert mon_bin_drv["SafePosAct"] == True
|
|
87
|
+
assert mon_bin_drv["FwdFbk"] == True # safe position is clockwise rotation
|
|
88
|
+
|
|
89
|
+
mon_bin_drv["FwdFbk"] = (
|
|
90
|
+
False # FwdFbk becomes to False w/o control signals
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
time.sleep(0.6)
|
|
94
|
+
|
|
95
|
+
assert mon_bin_drv["MonStatErr"] == True
|
|
96
|
+
assert mon_bin_drv["SafePosAct"] == True
|
|
97
|
+
assert mon_bin_drv["FwdFbk"] == True # safe position is clockwise rotation
|
|
98
|
+
finally:
|
|
99
|
+
mon_bin_drv.set_stop_monitor()
|
|
100
|
+
mon_bin_drv.monitor_static_thread.join()
|
|
101
|
+
mon_bin_drv.monitor_dynamic_thread.join()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@pytest.mark.parametrize(
|
|
105
|
+
"op_mode, src_mode, set_command", test_scenario_no_control_signals
|
|
106
|
+
)
|
|
107
|
+
def test_static_error_within_monitor_time(op_mode, src_mode, set_command):
|
|
108
|
+
"""If the required state is set on the interface before the time has
|
|
109
|
+
expired (wait tme < 0.5s in this test), there is no error"""
|
|
110
|
+
|
|
111
|
+
for command in [True, False]:
|
|
112
|
+
mon_bin_drv = init_mon_bin_drv(init_op_mode=op_mode, init_src_mode=src_mode)
|
|
113
|
+
|
|
114
|
+
mon_bin_drv.start_monitor()
|
|
115
|
+
try:
|
|
116
|
+
eval(f"mon_bin_drv.{set_command}({command})")
|
|
117
|
+
print(f"Scenario: mode {op_mode} {src_mode}, {set_command}")
|
|
118
|
+
|
|
119
|
+
# mimic an error situation: FwdFbk changes to True without any change in control signal
|
|
120
|
+
mon_bin_drv["FwdFbk"] = True
|
|
121
|
+
time.sleep(0.3) # monitor time has not expired (0.5s)
|
|
122
|
+
|
|
123
|
+
assert mon_bin_drv["MonStatErr"] == False
|
|
124
|
+
assert mon_bin_drv["MonDynErr"] == False
|
|
125
|
+
# assert mon_bin_drv["SafePosAct"] == False
|
|
126
|
+
|
|
127
|
+
finally:
|
|
128
|
+
mon_bin_drv.set_stop_monitor()
|
|
129
|
+
mon_bin_drv.monitor_static_thread.join()
|
|
130
|
+
mon_bin_drv.monitor_dynamic_thread.join()
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
test_scenario_control_signals_fwd = [
|
|
134
|
+
("op", "int", "set_fwd_op", "set_stop_op"),
|
|
135
|
+
("op", "man", "set_fwd_op", "set_stop_op"),
|
|
136
|
+
("aut", "int", "set_fwd_aut", "set_stop_aut"),
|
|
137
|
+
("aut", "man", "set_fwd_aut", "set_stop_aut"),
|
|
138
|
+
]
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@pytest.mark.parametrize(
|
|
142
|
+
"op_mode, src_mode, fwd_command, stop_command", test_scenario_control_signals_fwd
|
|
143
|
+
)
|
|
144
|
+
def test_dynamic_error_fwd_stop(op_mode, src_mode, fwd_command, stop_command):
|
|
145
|
+
mon_bin_drv = init_mon_bin_drv(init_op_mode=op_mode, init_src_mode=src_mode)
|
|
146
|
+
|
|
147
|
+
mon_bin_drv.start_monitor()
|
|
148
|
+
try:
|
|
149
|
+
eval(f"mon_bin_drv.{fwd_command}(True)")
|
|
150
|
+
print(f"Scenario: mode {op_mode} {src_mode}, {fwd_command}")
|
|
151
|
+
|
|
152
|
+
# FwdFbk does not change to True after control signal has changed to clockwise rotation
|
|
153
|
+
mon_bin_drv["FwdFbk"] = False
|
|
154
|
+
time.sleep(0.6)
|
|
155
|
+
|
|
156
|
+
assert mon_bin_drv["MonDynErr"] == True
|
|
157
|
+
assert mon_bin_drv["MonStatErr"] == False
|
|
158
|
+
assert mon_bin_drv["SafePosAct"] == True
|
|
159
|
+
assert mon_bin_drv["FwdFbk"] == True # safe position is clockwise rotation
|
|
160
|
+
|
|
161
|
+
eval(f"mon_bin_drv.{stop_command}(True)")
|
|
162
|
+
print(f"Scenario: mode {op_mode} {src_mode}, {stop_command}")
|
|
163
|
+
|
|
164
|
+
# FwdFbk does not change to False after control signal has changed to stop
|
|
165
|
+
mon_bin_drv["FwdFbk"] = True
|
|
166
|
+
time.sleep(0.6)
|
|
167
|
+
|
|
168
|
+
assert mon_bin_drv["MonDynErr"] == True
|
|
169
|
+
assert mon_bin_drv["MonStatErr"] == False
|
|
170
|
+
assert mon_bin_drv["SafePosAct"] == True
|
|
171
|
+
assert mon_bin_drv["FwdFbk"] == True # safe position is clockwise rotation
|
|
172
|
+
finally:
|
|
173
|
+
mon_bin_drv.set_stop_monitor()
|
|
174
|
+
mon_bin_drv.monitor_static_thread.join()
|
|
175
|
+
mon_bin_drv.monitor_dynamic_thread.join()
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
test_scenario_control_signals_rev = [
|
|
179
|
+
("op", "int", "set_rev_op", "set_stop_op"),
|
|
180
|
+
("op", "man", "set_rev_op", "set_stop_op"),
|
|
181
|
+
("aut", "int", "set_rev_aut", "set_stop_aut"),
|
|
182
|
+
("aut", "man", "set_rev_aut", "set_stop_aut"),
|
|
183
|
+
]
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@pytest.mark.parametrize(
|
|
187
|
+
"op_mode, src_mode, rev_command, stop_command", test_scenario_control_signals_rev
|
|
188
|
+
)
|
|
189
|
+
def test_dynamic_error_rev_stop(op_mode, src_mode, rev_command, stop_command):
|
|
190
|
+
mon_bin_drv = init_mon_bin_drv(init_op_mode=op_mode, init_src_mode=src_mode)
|
|
191
|
+
|
|
192
|
+
mon_bin_drv.start_monitor()
|
|
193
|
+
try:
|
|
194
|
+
eval(f"mon_bin_drv.{rev_command}(True)")
|
|
195
|
+
print(f"Scenario: mode {op_mode} {src_mode}, {rev_command}")
|
|
196
|
+
|
|
197
|
+
# RevFbk does not change to True after control signal has changed to anti-clockwise rotation
|
|
198
|
+
mon_bin_drv["RevFbk"] = False
|
|
199
|
+
time.sleep(0.6)
|
|
200
|
+
assert mon_bin_drv["MonDynErr"] == True
|
|
201
|
+
assert mon_bin_drv["MonStatErr"] == False
|
|
202
|
+
assert mon_bin_drv["SafePosAct"] == True
|
|
203
|
+
assert mon_bin_drv["FwdFbk"] == True # safe position is clockwise rotation
|
|
204
|
+
|
|
205
|
+
eval(f"mon_bin_drv.{stop_command}(True)")
|
|
206
|
+
print(f"Scenario: mode {op_mode} {src_mode}, {stop_command}")
|
|
207
|
+
|
|
208
|
+
# FwdFbk does not change to False after control signal has changed to stop
|
|
209
|
+
mon_bin_drv["FwdFbk"] = True
|
|
210
|
+
time.sleep(0.6)
|
|
211
|
+
|
|
212
|
+
assert mon_bin_drv["MonDynErr"] == True
|
|
213
|
+
assert mon_bin_drv["MonStatErr"] == False
|
|
214
|
+
assert mon_bin_drv["SafePosAct"] == True
|
|
215
|
+
assert mon_bin_drv["FwdFbk"] == True # safe position is clockwise rotation
|
|
216
|
+
finally:
|
|
217
|
+
mon_bin_drv.set_stop_monitor()
|
|
218
|
+
mon_bin_drv.monitor_static_thread.join()
|
|
219
|
+
mon_bin_drv.monitor_dynamic_thread.join()
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
test_scenario_control_signals_reset = [
|
|
223
|
+
("op", "int", "set_reset_op"),
|
|
224
|
+
("op", "man", "set_reset_op"),
|
|
225
|
+
("aut", "int", "set_reset_aut"),
|
|
226
|
+
("aut", "man", "set_reset_aut"),
|
|
227
|
+
]
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
@pytest.mark.parametrize(
|
|
231
|
+
"op_mode, src_mode, set_command", test_scenario_control_signals_reset
|
|
232
|
+
)
|
|
233
|
+
def test_dynamic_error_reset(op_mode, src_mode, set_command):
|
|
234
|
+
mon_bin_drv = init_mon_bin_drv(init_op_mode=op_mode, init_src_mode=src_mode)
|
|
235
|
+
|
|
236
|
+
mon_bin_drv.start_monitor()
|
|
237
|
+
try:
|
|
238
|
+
# set valve to open
|
|
239
|
+
if op_mode == "op":
|
|
240
|
+
mon_bin_drv.set_fwd_op(True)
|
|
241
|
+
elif op_mode == "aut":
|
|
242
|
+
mon_bin_drv.set_fwd_aut(True)
|
|
243
|
+
time.sleep(0.6)
|
|
244
|
+
|
|
245
|
+
# reset valve
|
|
246
|
+
eval(f"mon_bin_drv.{set_command}(True)")
|
|
247
|
+
print(f"Scenario: mode {op_mode} {src_mode}, {set_command}")
|
|
248
|
+
|
|
249
|
+
# mimic an error situation: FwdFbk does not change to False after reset changes to True
|
|
250
|
+
mon_bin_drv["FwdFbk"] = True
|
|
251
|
+
time.sleep(0.6)
|
|
252
|
+
|
|
253
|
+
assert mon_bin_drv["MonDynErr"] == True
|
|
254
|
+
assert mon_bin_drv["MonStatErr"] == False
|
|
255
|
+
assert mon_bin_drv["SafePosAct"] == True
|
|
256
|
+
assert mon_bin_drv["FwdFbk"] == True # safe position of valve is open
|
|
257
|
+
finally:
|
|
258
|
+
mon_bin_drv.set_stop_monitor()
|
|
259
|
+
mon_bin_drv.monitor_static_thread.join()
|
|
260
|
+
mon_bin_drv.monitor_dynamic_thread.join()
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
if __name__ == "__main__":
|
|
264
|
+
# This allows running the tests directly if needed
|
|
265
|
+
pytest.main([__file__, "-v"])
|