PyAutomationIO 1.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. automation/__init__.py +46 -0
  2. automation/alarms/__init__.py +563 -0
  3. automation/alarms/states.py +192 -0
  4. automation/alarms/trigger.py +64 -0
  5. automation/buffer.py +132 -0
  6. automation/core.py +1792 -0
  7. automation/dbmodels/__init__.py +23 -0
  8. automation/dbmodels/alarms.py +549 -0
  9. automation/dbmodels/core.py +86 -0
  10. automation/dbmodels/events.py +178 -0
  11. automation/dbmodels/logs.py +155 -0
  12. automation/dbmodels/machines.py +181 -0
  13. automation/dbmodels/opcua.py +81 -0
  14. automation/dbmodels/opcua_server.py +174 -0
  15. automation/dbmodels/tags.py +921 -0
  16. automation/dbmodels/users.py +259 -0
  17. automation/extensions/__init__.py +15 -0
  18. automation/extensions/api.py +149 -0
  19. automation/extensions/cors.py +18 -0
  20. automation/filter/__init__.py +19 -0
  21. automation/iad/__init__.py +3 -0
  22. automation/iad/frozen_data.py +54 -0
  23. automation/iad/out_of_range.py +51 -0
  24. automation/iad/outliers.py +51 -0
  25. automation/logger/__init__.py +0 -0
  26. automation/logger/alarms.py +434 -0
  27. automation/logger/core.py +265 -0
  28. automation/logger/datalogger.py +877 -0
  29. automation/logger/events.py +202 -0
  30. automation/logger/logdict.py +53 -0
  31. automation/logger/logs.py +203 -0
  32. automation/logger/machines.py +248 -0
  33. automation/logger/opcua_server.py +130 -0
  34. automation/logger/users.py +96 -0
  35. automation/managers/__init__.py +4 -0
  36. automation/managers/alarms.py +455 -0
  37. automation/managers/db.py +328 -0
  38. automation/managers/opcua_client.py +186 -0
  39. automation/managers/state_machine.py +183 -0
  40. automation/models.py +174 -0
  41. automation/modules/__init__.py +14 -0
  42. automation/modules/alarms/__init__.py +0 -0
  43. automation/modules/alarms/resources/__init__.py +10 -0
  44. automation/modules/alarms/resources/alarms.py +280 -0
  45. automation/modules/alarms/resources/summary.py +81 -0
  46. automation/modules/events/__init__.py +0 -0
  47. automation/modules/events/resources/__init__.py +10 -0
  48. automation/modules/events/resources/events.py +85 -0
  49. automation/modules/events/resources/logs.py +109 -0
  50. automation/modules/tags/__init__.py +0 -0
  51. automation/modules/tags/resources/__init__.py +8 -0
  52. automation/modules/tags/resources/tags.py +254 -0
  53. automation/modules/users/__init__.py +2 -0
  54. automation/modules/users/resources/__init__.py +10 -0
  55. automation/modules/users/resources/models/__init__.py +2 -0
  56. automation/modules/users/resources/models/roles.py +5 -0
  57. automation/modules/users/resources/models/users.py +14 -0
  58. automation/modules/users/resources/roles.py +38 -0
  59. automation/modules/users/resources/users.py +113 -0
  60. automation/modules/users/roles.py +121 -0
  61. automation/modules/users/users.py +335 -0
  62. automation/opcua/__init__.py +1 -0
  63. automation/opcua/models.py +541 -0
  64. automation/opcua/subscription.py +259 -0
  65. automation/pages/__init__.py +0 -0
  66. automation/pages/alarms.py +34 -0
  67. automation/pages/alarms_history.py +21 -0
  68. automation/pages/assets/styles.css +7 -0
  69. automation/pages/callbacks/__init__.py +28 -0
  70. automation/pages/callbacks/alarms.py +218 -0
  71. automation/pages/callbacks/alarms_summary.py +20 -0
  72. automation/pages/callbacks/db.py +222 -0
  73. automation/pages/callbacks/filter.py +238 -0
  74. automation/pages/callbacks/machines.py +29 -0
  75. automation/pages/callbacks/machines_detailed.py +581 -0
  76. automation/pages/callbacks/opcua.py +266 -0
  77. automation/pages/callbacks/opcua_server.py +244 -0
  78. automation/pages/callbacks/tags.py +495 -0
  79. automation/pages/callbacks/trends.py +119 -0
  80. automation/pages/communications.py +129 -0
  81. automation/pages/components/__init__.py +123 -0
  82. automation/pages/components/alarms.py +151 -0
  83. automation/pages/components/alarms_summary.py +45 -0
  84. automation/pages/components/database.py +128 -0
  85. automation/pages/components/gaussian_filter.py +69 -0
  86. automation/pages/components/machines.py +396 -0
  87. automation/pages/components/opcua.py +384 -0
  88. automation/pages/components/opcua_server.py +53 -0
  89. automation/pages/components/tags.py +253 -0
  90. automation/pages/components/trends.py +66 -0
  91. automation/pages/database.py +26 -0
  92. automation/pages/filter.py +55 -0
  93. automation/pages/machines.py +20 -0
  94. automation/pages/machines_detailed.py +41 -0
  95. automation/pages/main.py +63 -0
  96. automation/pages/opcua_server.py +28 -0
  97. automation/pages/tags.py +40 -0
  98. automation/pages/trends.py +35 -0
  99. automation/singleton.py +30 -0
  100. automation/state_machine.py +1674 -0
  101. automation/tags/__init__.py +2 -0
  102. automation/tags/cvt.py +1198 -0
  103. automation/tags/filter.py +55 -0
  104. automation/tags/tag.py +418 -0
  105. automation/tests/__init__.py +10 -0
  106. automation/tests/test_alarms.py +110 -0
  107. automation/tests/test_core.py +257 -0
  108. automation/tests/test_unit.py +21 -0
  109. automation/tests/test_user.py +155 -0
  110. automation/utils/__init__.py +164 -0
  111. automation/utils/decorators.py +222 -0
  112. automation/utils/npw.py +294 -0
  113. automation/utils/observer.py +21 -0
  114. automation/utils/units.py +118 -0
  115. automation/variables/__init__.py +55 -0
  116. automation/variables/adimentional.py +30 -0
  117. automation/variables/current.py +71 -0
  118. automation/variables/density.py +115 -0
  119. automation/variables/eng_time.py +68 -0
  120. automation/variables/force.py +90 -0
  121. automation/variables/length.py +104 -0
  122. automation/variables/mass.py +80 -0
  123. automation/variables/mass_flow.py +101 -0
  124. automation/variables/percentage.py +30 -0
  125. automation/variables/power.py +113 -0
  126. automation/variables/pressure.py +93 -0
  127. automation/variables/temperature.py +168 -0
  128. automation/variables/volume.py +70 -0
  129. automation/variables/volumetric_flow.py +100 -0
  130. automation/workers/__init__.py +2 -0
  131. automation/workers/logger.py +164 -0
  132. automation/workers/state_machine.py +207 -0
  133. automation/workers/worker.py +36 -0
  134. pyautomationio-1.1.1.dist-info/METADATA +199 -0
  135. pyautomationio-1.1.1.dist-info/RECORD +138 -0
  136. pyautomationio-1.1.1.dist-info/WHEEL +5 -0
  137. pyautomationio-1.1.1.dist-info/licenses/LICENSE +21 -0
  138. pyautomationio-1.1.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,266 @@
1
+ import dash, csv, io
2
+ from ...utils import get_nodes_info, get_data_to_update_into_opcua_table
3
+ from ...pages.components.opcua import OPCUAComponents
4
+ from ...opcua.subscription import SubHandler
5
+
6
+
7
+ subscription_handler = SubHandler()
8
+ opcua_components = OPCUAComponents()
9
+
10
+
11
+ def init_callback(app:dash.Dash):
12
+
13
+ @app.callback(
14
+ dash.Output("data_access_view_table", "children", allow_duplicate=True),
15
+ dash.Input('timestamp-interval', 'n_intervals'),
16
+ dash.State({'type': 'file-checklist', 'index': dash.dependencies.ALL}, 'value'),
17
+ prevent_initial_call=False
18
+ )
19
+ def update_data_access_table( n_intervals, selected_files):
20
+
21
+ to_get_node_values = get_nodes_info(selected_files=selected_files)
22
+ data = get_data_to_update_into_opcua_table(app=app, to_get_node_values=to_get_node_values)
23
+
24
+ return opcua_components.data_access_view_table(data=data)
25
+
26
+ @app.callback(
27
+ dash.Output("data_access_view_table", "children"),
28
+ dash.Input({'type': 'file-checklist', 'index': dash.dependencies.ALL}, 'value')
29
+ )
30
+ def display_selected_file(selected_files):
31
+
32
+ subscription_handler.unsubscribe_all()
33
+ to_get_node_values = get_nodes_info(selected_files=selected_files)
34
+
35
+ data = list()
36
+ subscriptions = dict()
37
+ for client_name, namespaces in to_get_node_values.items():
38
+
39
+ client = app.automation.get_opcua_client(client_name=client_name)
40
+ if client:
41
+ subscriptions[client_name] = client.create_subscription(1000, subscription_handler)
42
+ infos = app.automation.get_node_attributes(client_name=client_name, namespaces=namespaces)
43
+ if infos:
44
+ for info in infos:
45
+ _info = info[0]
46
+ namespace = _info["Namespace"]
47
+ data.append(
48
+ {
49
+ "server": client_name,
50
+ "namespace": namespace,
51
+ "data_type": _info["DataType"],
52
+ "display_name": _info["DisplayName"],
53
+ "value": _info["Value"],
54
+ "source_timestamp": _info["DataValue"].SourceTimestamp,
55
+ "status_code": _info["DataValue"].StatusCode.name
56
+ }
57
+ )
58
+
59
+ node_id = client.get_node_id_by_namespace(namespace)
60
+ subscription = subscriptions[client_name]
61
+ subscription_handler.subscribe(subscription=subscription, client_name=client_name, node_id=node_id)
62
+
63
+ return opcua_components.data_access_view_table(data=data)
64
+
65
+ @app.callback(
66
+ dash.Output("add_server_modal", "is_open"),
67
+ dash.Input("add_server_button", "n_clicks"),
68
+ [dash.State("add_server_modal", "is_open")],
69
+ )
70
+ def add_server_button(n, is_open):
71
+ r"""
72
+ Documentation here
73
+ """
74
+ if n:
75
+
76
+ return not is_open
77
+
78
+ return is_open
79
+
80
+ @app.callback(
81
+ dash.Output("remove_server_modal", "is_open"),
82
+ dash.Input("remove_server_button", "n_clicks"),
83
+ [dash.State("remove_server_modal", "is_open")],
84
+ )
85
+ def remove_server_button(n, is_open):
86
+ r"""
87
+ Documentation here
88
+ """
89
+ if n:
90
+ clients = list()
91
+ for client_name in app.automation.get_opcua_clients().keys():
92
+
93
+ clients.append(
94
+ {"label": client_name, "value": client_name}
95
+ )
96
+
97
+ dash.set_props("opcua_client_names_options", {"options": clients})
98
+ return not is_open
99
+
100
+ return is_open
101
+
102
+ @app.callback(
103
+ dash.Output("add_server_modal", "is_open", allow_duplicate=True),
104
+ dash.Output("server_tree", "children"),
105
+ dash.Input("add_server_ok_button_modal", "n_clicks"),
106
+ dash.State("opcua_client_name_input", "value"),
107
+ dash.State("opcua_client_host_input", "value"),
108
+ dash.State("opcua_client_port_input", "value")
109
+ )
110
+ def ok_add_server_button(n, client_name:str, host:str="127.0.0.1", port:int=4840):
111
+ r"""
112
+ Documentation here
113
+ """
114
+ resp, _ = app.automation.add_opcua_client(client_name=client_name, host=host, port=port)
115
+ if resp:
116
+
117
+ data = OPCUAComponents.get_opcua_tree(app)
118
+ subscription_handler.unsubscribe_all()
119
+
120
+ return False, data
121
+
122
+ message = f"Connection refused on opc.tcp://{host}:{port}"
123
+ dash.set_props("modal-error-opcua-connection-body", {"children": message})
124
+ dash.set_props("modal-error-opcua-connection", {'is_open': True})
125
+
126
+ return False, {}
127
+
128
+ @app.callback(
129
+ dash.Output("add_server_modal", "is_open", allow_duplicate=True),
130
+ dash.Input("add_server_cancel_button_modal", "n_clicks"),
131
+ )
132
+ def cancel_add_server_button(n):
133
+ r"""
134
+ Documentation here
135
+ """
136
+ return False
137
+
138
+ @app.callback(
139
+ dash.Output("remove_server_modal", "is_open", allow_duplicate=True),
140
+ dash.Input("remove_server_ok_button_modal", "n_clicks"),
141
+ dash.State("opcua_client_names_options", "value")
142
+ )
143
+ def ok_remove_server_button(n, client_name:str):
144
+ r"""
145
+ Documentation here
146
+ """
147
+ if client_name:
148
+
149
+ app.automation.remove_opcua_client(client_name=client_name)
150
+
151
+ data = OPCUAComponents.get_opcua_tree(app)
152
+ dash.set_props("server_tree", {"children": data})
153
+ return False
154
+
155
+ @app.callback(
156
+ dash.Output("remove_server_modal", "is_open", allow_duplicate=True),
157
+ dash.Input("remove_server_cancel_button_modal", "n_clicks"),
158
+ )
159
+ def cancel_remove_server_button(n):
160
+ r"""
161
+ Documentation here
162
+ """
163
+ return False
164
+
165
+ @app.callback(
166
+ dash.Output("server_tree", "children", allow_duplicate=True),
167
+ dash.Input('communications_page', 'pathname'),
168
+ prevent_initial_call=True
169
+ )
170
+ def display_page(pathname):
171
+ r"""
172
+ Documentation here
173
+ """
174
+ if pathname=="/":
175
+
176
+ data = OPCUAComponents.get_opcua_tree(app)
177
+ subscription_handler.unsubscribe_all()
178
+
179
+ return data
180
+
181
+ @app.callback(
182
+ dash.Output("modal-success-opcua-connection", "is_open"),
183
+ dash.Input("close-success-opcua-connection", "n_clicks"),
184
+ [dash.State("modal-success-opcua-connection", "is_open")],
185
+ )
186
+ def close_success_button(n, is_open):
187
+ r"""
188
+ Documentation here
189
+ """
190
+ if n:
191
+
192
+ return not is_open
193
+
194
+ return is_open
195
+
196
+ @app.callback(
197
+ dash.Output("modal-error-opcua-connection", "is_open"),
198
+ dash.Input("close-error-opcua-connection", "n_clicks"),
199
+ [dash.State("modal-error-opcua-connection", "is_open")],
200
+ )
201
+ def close_error_opcua_connection_button(n, is_open):
202
+ r"""
203
+ Documentation here
204
+ """
205
+ if n:
206
+
207
+ return not is_open
208
+
209
+ return is_open
210
+
211
+ @app.callback(
212
+ dash.Output("download_node_info_modal", "is_open"),
213
+ dash.Input("download_node_info_button", "n_clicks"),
214
+ [dash.State("download_node_info_modal", "is_open")],
215
+ )
216
+ def download_node_info_button(n, is_open):
217
+ r"""
218
+ Documentation here
219
+ """
220
+ if n:
221
+ clients = list()
222
+ for client_name in app.automation.get_opcua_clients().keys():
223
+
224
+ clients.append(
225
+ {"label": client_name, "value": client_name}
226
+ )
227
+
228
+ dash.set_props("download_opcua_client_names_options", {"options": clients})
229
+ return not is_open
230
+
231
+ return is_open
232
+
233
+ @app.callback(
234
+ dash.Output("download_node_info_modal", "is_open", allow_duplicate=True),
235
+ dash.Output("download", "data"),
236
+ dash.Input("download_node_info_ok_button_modal", "n_clicks"),
237
+ dash.State("download_opcua_client_names_options", "value")
238
+ )
239
+ def ok_download_node_info_button(n, client_name:str):
240
+ r"""
241
+ Documentation here
242
+ """
243
+ if client_name:
244
+
245
+ opcua_tree = app.automation.get_opcua_tree(client_name=client_name)
246
+ opcua_tree = opcua_tree[0]['Objects']
247
+ flat = list()
248
+ for tree in opcua_tree:
249
+
250
+ flat.extend(OPCUAComponents.flatten_dict(tree))
251
+ output = io.StringIO()
252
+ writer = csv.DictWriter(output, fieldnames=["name", "namespace", "NodeClass"])
253
+ writer.writeheader()
254
+ writer.writerows(flat)
255
+
256
+ return False, dict(content=output.getvalue(), filename=f"opcua_server_node_{client_name}.csv")
257
+
258
+ @app.callback(
259
+ dash.Output("download_node_info_modal", "is_open", allow_duplicate=True),
260
+ dash.Input("download_node_info_cancel_button_modal", "n_clicks"),
261
+ )
262
+ def cancel_remove_server_button(n):
263
+ r"""
264
+ Documentation here
265
+ """
266
+ return False
@@ -0,0 +1,244 @@
1
+ import dash
2
+ from ...pages.components.opcua import OPCUAComponents
3
+ from ...opcua.subscription import SubHandler
4
+ from ...models import StringType
5
+ from ...state_machine import Node, ua
6
+ from ...utils import find_differences_between_lists_opcua_server
7
+
8
+
9
+ subscription_handler = SubHandler()
10
+ opcua_components = OPCUAComponents()
11
+
12
+
13
+ def init_callback(app:dash.Dash):
14
+
15
+ def create_opcua_server_table(opcua_server_machine):
16
+
17
+ attrs = list()
18
+ for attr in dir(opcua_server_machine):
19
+ if hasattr(opcua_server_machine, attr):
20
+ node = getattr(opcua_server_machine, attr)
21
+ if isinstance(node, Node):
22
+
23
+ node_class = node.get_node_class()
24
+ if node_class == ua.NodeClass.Variable:
25
+
26
+ display_name = node.get_attribute(ua.AttributeIds.DisplayName).Value.Value.Text
27
+ # Obtén el nodo padre
28
+ parent_node = node.get_parent()
29
+
30
+ # Obtén el nombre de la carpeta padre (nodo padre)
31
+ parent_name = parent_node.get_browse_name().Name
32
+ access_level = node.get_access_level()
33
+ # Verificar los niveles de acceso
34
+ write_only = ua.AccessLevel.CurrentWrite in access_level and ua.AccessLevel.CurrentRead not in access_level
35
+ read_write = ua.AccessLevel.CurrentRead in access_level and ua.AccessLevel.CurrentWrite in access_level
36
+ access_type = "Read"
37
+ if write_only:
38
+
39
+ access_type = "Write"
40
+
41
+ elif read_write:
42
+
43
+ access_type = "ReadWrite"
44
+
45
+ attrs.append({
46
+ "name": f"{parent_name}.{display_name}",
47
+ "namespace": node.nodeid.to_string(),
48
+ "access_type": access_type
49
+ })
50
+
51
+ properties = node.get_properties()
52
+ for prop in properties:
53
+ prop_name = prop.get_display_name().Text
54
+ # prop_value = prop.get_value()
55
+
56
+ access_level = prop.get_access_level()
57
+ # Verificar los niveles de acceso
58
+ write_only = ua.AccessLevel.CurrentWrite in access_level and ua.AccessLevel.CurrentRead not in access_level
59
+ read_write = ua.AccessLevel.CurrentRead in access_level and ua.AccessLevel.CurrentWrite in access_level
60
+ access_type = "Read"
61
+ if write_only:
62
+
63
+ access_type = "Write"
64
+
65
+ elif read_write:
66
+
67
+ access_type = "ReadWrite"
68
+
69
+ attrs.append({
70
+ "name": f"{parent_name}.{display_name}.{prop_name}",
71
+ "namespace": prop.nodeid.to_string(),
72
+ "access_type": access_type
73
+ })
74
+ return attrs
75
+
76
+ @app.callback(
77
+ dash.Output("opcua_server_datatable", "data", allow_duplicate=True),
78
+ dash.Input('opcua_server', 'pathname'),
79
+ prevent_initial_call=True
80
+ )
81
+ def display_page(pathname):
82
+ r"""
83
+ Documentation here
84
+ """
85
+ attrs = list()
86
+
87
+ if pathname=="/opcua-server":
88
+ opcua_server_machine = app.automation.get_machine(name=StringType("OPCUAServer"))
89
+ attrs = create_opcua_server_table(opcua_server_machine=opcua_server_machine)
90
+
91
+ return attrs
92
+
93
+ @app.callback(
94
+ dash.Input('opcua_server_datatable', 'data_timestamp'),
95
+ dash.State('opcua_server_datatable', 'data_previous'),
96
+ dash.State('opcua_server_datatable', 'data'),
97
+ )
98
+ def update_read_only(timestamp, previous, current):
99
+ message = None
100
+ attr_not_clearable = ("name", "namespace")
101
+ if timestamp:
102
+
103
+ if previous and current: # UPDATE TAG DEFINITION
104
+
105
+ to_updates = find_differences_between_lists_opcua_server(previous, current)
106
+ node_to_update = to_updates[0]
107
+ node_name = node_to_update.pop("name")
108
+ node_to_update.pop("namespace")
109
+ for attr in attr_not_clearable:
110
+ if attr in node_to_update:
111
+ if not node_to_update[attr]:
112
+ message = f"You can not empty {attr} attribute"
113
+
114
+ if message:
115
+ dash.set_props("modal-update-opcua-server-body", {"children": message})
116
+ dash.set_props("modal-update-opcua-server-centered", {'is_open': True})
117
+ return
118
+ message = f"Do you want to update node {node_name} Access Type to {node_to_update['access_type']}?"
119
+ # OPEN MODAL TO CONFIRM CHANGES
120
+ dash.set_props("modal-update-opcua-server-body", {"children": message})
121
+ dash.set_props("modal-update-opcua-server-centered", {'is_open': True})
122
+
123
+ @app.callback(
124
+ [
125
+ dash.Output("modal-update-opcua-server-centered", "is_open"),
126
+ dash.Output('opcua_server_datatable', 'data'),
127
+ dash.Output('opcua_server_datatable', 'data_timestamp'),
128
+ dash.Output("update-opcua-server-yes", "n_clicks"),
129
+ dash.Output("update-opcua-server-no", "n_clicks")
130
+ ],
131
+ [dash.Input("update-opcua-server-yes", "n_clicks"), dash.Input("update-opcua-server-no", "n_clicks")],
132
+ [
133
+ dash.State('opcua_server_datatable', 'data_timestamp'),
134
+ dash.State("modal-update-opcua-server-centered", "is_open"),
135
+ dash.State('opcua_server_datatable', 'data_previous'),
136
+ dash.State('opcua_server_datatable', 'data')
137
+ ]
138
+ )
139
+ def toggle_modal_update_read_only(yes_n, no_n, timestamp, is_open, previous, current):
140
+ r"""
141
+ Documentation here
142
+ """
143
+ from ...opcua.subscription import SubHandlerServer
144
+
145
+ handler = SubHandlerServer()
146
+ opcua_server_machine = app.automation.get_machine(name=StringType("OPCUAServer"))
147
+ attrs = create_opcua_server_table(opcua_server_machine=opcua_server_machine)
148
+
149
+ if yes_n:
150
+
151
+ if timestamp:
152
+
153
+ if previous and current: # UPDATE TAG DEFINITION
154
+ to_updates = find_differences_between_lists_opcua_server(previous, current)
155
+ node_to_update = to_updates[0]
156
+ namespace = node_to_update.pop("namespace")
157
+ access_type = node_to_update.pop("access_type")
158
+ # Code for update read_only attribute
159
+ opcua_server_machine = app.automation.get_machine(name=StringType("OPCUAServer"))
160
+ opcua_server_attrs = dir(opcua_server_machine)
161
+ node = False
162
+
163
+ for i, item in enumerate(opcua_server_attrs):
164
+
165
+ if hasattr(opcua_server_machine, item):
166
+ node = getattr(opcua_server_machine, item)
167
+ if isinstance(node, Node):
168
+
169
+ node_class = node.get_node_class()
170
+
171
+ if node_class == ua.NodeClass.Variable:
172
+
173
+ if node.nodeid.to_string()==namespace:
174
+
175
+ break
176
+
177
+ else:
178
+ props = node.get_properties()
179
+ flag = False
180
+ for node in props:
181
+
182
+ if node.nodeid.to_string()==namespace:
183
+
184
+ flag = True
185
+ break
186
+
187
+ if flag:
188
+
189
+ break
190
+
191
+ if node:
192
+
193
+ opcua_server_obj = app.automation.get_opcua_server_record_by_namespace(namespace=namespace)
194
+ if opcua_server_obj:
195
+ app.automation.update_opcua_server_access_type(namespace=namespace, access_type=access_type)
196
+ else:
197
+ app.automation.create_opcua_server_record(name=node_to_update["name"], namespace=namespace, access_type=access_type)
198
+ access_type = access_type.lower()
199
+ # Limpiar todos los bits de acceso primero
200
+ node.unset_attr_bit(ua.AttributeIds.AccessLevel, ua.AccessLevel.CurrentRead)
201
+ node.unset_attr_bit(ua.AttributeIds.AccessLevel, ua.AccessLevel.CurrentWrite)
202
+ node.unset_attr_bit(ua.AttributeIds.UserAccessLevel, ua.AccessLevel.CurrentRead)
203
+ node.unset_attr_bit(ua.AttributeIds.UserAccessLevel, ua.AccessLevel.CurrentWrite)
204
+
205
+ subscriptions = handler.subscriptions
206
+ # Unsubscribe
207
+
208
+ if namespace in subscriptions:
209
+
210
+ _sub = subscriptions.pop(namespace)
211
+ _sub.delete()
212
+
213
+ if access_type == "write":
214
+ # Solo escritura: deshabilitamos la lectura y habilitamos la escritura
215
+ node.set_attr_bit(ua.AttributeIds.AccessLevel, ua.AccessLevel.CurrentWrite)
216
+ node.set_attr_bit(ua.AttributeIds.UserAccessLevel, ua.AccessLevel.CurrentWrite)
217
+ sub = opcua_server_machine.server.create_subscription(100, handler)
218
+ sub.subscribe_data_change(node)
219
+ handler.subscriptions[namespace] = sub
220
+ elif access_type == "read":
221
+ # Solo lectura: habilitamos la lectura y deshabilitamos la escritura
222
+ node.set_attr_bit(ua.AttributeIds.AccessLevel, ua.AccessLevel.CurrentRead)
223
+ node.set_attr_bit(ua.AttributeIds.UserAccessLevel, ua.AccessLevel.CurrentRead)
224
+ elif access_type == "readwrite":
225
+ # Lectura y escritura: habilitamos ambos
226
+ node.set_attr_bit(ua.AttributeIds.AccessLevel, ua.AccessLevel.CurrentRead)
227
+ node.set_attr_bit(ua.AttributeIds.AccessLevel, ua.AccessLevel.CurrentWrite)
228
+ node.set_attr_bit(ua.AttributeIds.UserAccessLevel, ua.AccessLevel.CurrentRead)
229
+ node.set_attr_bit(ua.AttributeIds.UserAccessLevel, ua.AccessLevel.CurrentWrite)
230
+ sub = opcua_server_machine.server.create_subscription(100, handler)
231
+ sub.subscribe_data_change(node)
232
+ handler.subscriptions[namespace] = sub
233
+
234
+ attrs = create_opcua_server_table(opcua_server_machine=opcua_server_machine)
235
+
236
+ return not is_open, attrs, None, 0, 0
237
+
238
+ elif no_n:
239
+
240
+ return not is_open, attrs, None, 0, 0
241
+
242
+ else:
243
+
244
+ return is_open, attrs, None, 0, 0