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,384 @@
1
+ import dash
2
+ import dash_mantine_components as dmc
3
+ import dash_bootstrap_components as dbc
4
+ from dash_iconify import DashIconify
5
+ from ...singleton import Singleton
6
+
7
+
8
+ class FileTree:
9
+ r"""
10
+ Documentation here
11
+ """
12
+ def __init__(self):
13
+
14
+ self.data = None
15
+ self.folder_name = None
16
+
17
+ def render(self, data) -> dmc.Accordion:
18
+ r"""
19
+ Documentation here
20
+ """
21
+ self.data = data
22
+ return dmc.Accordion(
23
+ self.build_tree(self.data),
24
+ multiple=True,
25
+ id="opcua_server_tree"
26
+ )
27
+
28
+ def flatten(self, l):
29
+ r"""
30
+ Documentation here
31
+ """
32
+ return [item for sublist in l for item in sublist]
33
+
34
+ def make_file(self, file_name, key):
35
+ r"""
36
+ Documentation here
37
+ """
38
+ return dmc.Text(
39
+ [
40
+ dash.dcc.Checklist(
41
+ options=[{'label': '', 'value': f"{self.folder_name}/{key}"}],
42
+ id={'type': 'file-checklist', 'index': f"{self.folder_name}/{key}"},
43
+ style={"display": "inline-block"}
44
+ ),
45
+ DashIconify(icon="akar-icons:file"),
46
+ " ",
47
+ file_name
48
+ ],
49
+ style={"paddingTop": '5px'}
50
+ )
51
+
52
+ def make_folder(self, folder_name):
53
+ r"""
54
+ Documentation here
55
+ """
56
+ return [DashIconify(icon="akar-icons:folder"), " ", folder_name]
57
+
58
+ def build_tree(self, nodes):
59
+ r"""
60
+ Documentation here
61
+ """
62
+ d = []
63
+ for i, node in enumerate(nodes):
64
+ if node['children']:
65
+ self.folder_name = node['title']
66
+ if node['children']:
67
+ children = self.flatten([self.build_tree(node['children'])])
68
+ d.append(
69
+ dmc.AccordionItem(
70
+ children=[
71
+ dmc.AccordionControl(self.make_folder(node['title'])),
72
+ dmc.AccordionPanel(children)
73
+ ],
74
+ value=f"item-{i}"
75
+ )
76
+ )
77
+ else:
78
+ d.append(self.make_file(node['title'], node['key']))
79
+ return d
80
+
81
+
82
+ file_tree = FileTree()
83
+
84
+
85
+ class OPCUAComponents(Singleton):
86
+
87
+ @classmethod
88
+ def add_server(cls, title:str, modal_id:str, body_id:str, ok_button_id:str, cancel_button_id:str):
89
+
90
+ return dash.html.Div(
91
+ [
92
+ dash.dcc.Location(id='communications_page', refresh=False),
93
+ dbc.Modal(
94
+ [
95
+ dbc.ModalHeader(dbc.ModalTitle(title), close_button=True),
96
+ dbc.ModalBody(
97
+ dash.html.Div(
98
+ [
99
+ dash.html.H6('Server Information'), # This is the title
100
+ dash.html.Div([
101
+ dbc.InputGroup([dbc.InputGroupText("Name"), dbc.Input(placeholder="Server 1", id="opcua_client_name_input")], size="sm", className="mb-1"),
102
+ dbc.InputGroup([dbc.InputGroupText("Host"), dbc.Input(placeholder="127.0.0.1", id="opcua_client_host_input")], size="sm", className="mb-1"),
103
+ dbc.InputGroup([dbc.InputGroupText("Port"), dbc.Input(placeholder=4840, id="opcua_client_port_input", type="number")], size="sm"),
104
+ ], style={'border': '1px solid black', 'padding': '10px'}, className="mb-2"),
105
+
106
+ dash.html.H6('Security Settings'), # This is the title
107
+ dash.html.Div([
108
+ dbc.InputGroup(
109
+ [
110
+ dbc.InputGroupText("Security Policy"),
111
+ dbc.Select(
112
+ options=[
113
+ {"label": "None", "value": None},
114
+ {"label": "Basic128Rsa15", "value": "Basic128Rsa15"},
115
+ {"label": "Basic256Sha256", "value": "Basic256Sha256"},
116
+ {"label": "Aes128Sha256RsaOaep", "value": "Aes128Sha256RsaOaep"},
117
+ {"label": "Aes256Sha256RsaPss", "value": "Aes256Sha256RsaPss"},
118
+ ]
119
+ ),
120
+ ],
121
+ size="sm",
122
+ className="mb-1"
123
+ ),
124
+ dbc.InputGroup(
125
+ [
126
+ dbc.InputGroupText("Message Security Mode"),
127
+ dbc.Select(
128
+ options=[
129
+ {"label": "None", "value": None},
130
+ {"label": "Sign", "value": "Sign"},
131
+ {"label": "Sign & Encrypt", "value": "Sign & Encrypt"},
132
+ ]
133
+ )
134
+ ],
135
+ size="sm"
136
+ )
137
+ ], style={'border': '1px solid black', 'padding': '10px'}, className="mb-2"),
138
+
139
+ dash.html.H6('Authentication Settings'), # This is the title
140
+ dash.html.Div([
141
+ dbc.InputGroup([
142
+ dbc.RadioItems(
143
+ id="radio-1",
144
+ options=[{"label": "Anonymous", "value": 1}],
145
+ ),
146
+ ], className="mb-3"),
147
+ dash.html.Hr(),
148
+ dbc.InputGroup([
149
+ dbc.Row([
150
+ dbc.Col([
151
+ dbc.RadioItems(
152
+ id="radio-2",
153
+ options=[{"label": "", "value": 2}]
154
+ ),
155
+ ],
156
+ width=1),
157
+ dbc.Col([
158
+ dbc.InputGroup([dbc.InputGroupText("Username"), dbc.Input(disabled=True)], size="sm", className="mb-1"),
159
+ dbc.InputGroup([dbc.InputGroupText("Password"), dbc.Input(disabled=True)], size="sm"),
160
+ ],
161
+ width=11)
162
+ ])
163
+ ], className="mb-3"),
164
+ dash.html.Hr(),
165
+ dbc.InputGroup([
166
+ dbc.Row([
167
+ dbc.Col([
168
+ dbc.RadioItems(
169
+ id="radio-2",
170
+ options=[{"label": "", "value": 2}]
171
+ ),
172
+ ],
173
+ width=1),
174
+ dbc.Col([
175
+ dbc.InputGroup([dbc.InputGroupText("Certificate"), dbc.Input(disabled=True)], size="sm", className="mb-1"),
176
+ dbc.InputGroup([dbc.InputGroupText("Private Key"), dbc.Input(disabled=True)], size="sm"),
177
+ ],
178
+ width=11)
179
+ ])
180
+ ], className="mb-3"),
181
+ ], style={'border': '1px solid black', 'padding': '10px'}),
182
+ ]
183
+ ),
184
+ id=body_id),
185
+ dbc.ModalFooter(
186
+ [
187
+ dbc.Button(
188
+ "OK",
189
+ id=ok_button_id,
190
+ className="float-start",
191
+ n_clicks=0,
192
+ ),
193
+ dbc.Button(
194
+ "Cancel",
195
+ id=cancel_button_id,
196
+ className="ms-auto",
197
+ n_clicks=0,
198
+ )
199
+ ]
200
+ ),
201
+ ],
202
+ id=modal_id,
203
+ centered=True,
204
+ is_open=False,
205
+ ),
206
+ ]
207
+ )
208
+
209
+ @classmethod
210
+ def remove_server(cls, title:str, modal_id:str, body_id:str, ok_button_id:str, cancel_button_id:str):
211
+
212
+ return dash.html.Div(
213
+ [
214
+ dash.dcc.Location(id='communications_page', refresh=False),
215
+ dbc.Modal(
216
+ [
217
+ dbc.ModalHeader(dbc.ModalTitle(title), close_button=True),
218
+ dbc.ModalBody(
219
+ dash.html.Div(
220
+ [
221
+ dash.html.H6('Server Information'), # This is the title
222
+ dash.html.Div([
223
+ dbc.InputGroup([dbc.InputGroupText("Client Name"), dbc.Select(
224
+ options=[
225
+ {"label": "postgresql", "value": "postgresql"},
226
+ {"label": "mysql", "value": "mysql"},
227
+ {"label": "sqlite", "value": "sqlite"}
228
+ ],
229
+ id="opcua_client_names_options"
230
+ )], size="sm", className="mb-1")
231
+ ], style={'border': '1px solid black', 'padding': '10px'}, className="mb-2"),
232
+ ]
233
+ ),
234
+ id=body_id),
235
+ dbc.ModalFooter(
236
+ [
237
+ dbc.Button(
238
+ "OK",
239
+ id=ok_button_id,
240
+ className="float-start",
241
+ n_clicks=0,
242
+ ),
243
+ dbc.Button(
244
+ "Cancel",
245
+ id=cancel_button_id,
246
+ className="ms-auto",
247
+ n_clicks=0,
248
+ )
249
+ ]
250
+ ),
251
+ ],
252
+ id=modal_id,
253
+ centered=True,
254
+ is_open=False,
255
+ ),
256
+ ]
257
+ )
258
+
259
+ @classmethod
260
+ def download_node_info(cls, title:str, modal_id:str, body_id:str, ok_button_id:str, cancel_button_id:str):
261
+
262
+ return dash.html.Div(
263
+ [
264
+ dash.dcc.Location(id='communications_page', refresh=False),
265
+ dbc.Modal(
266
+ [
267
+ dbc.ModalHeader(dbc.ModalTitle(title), close_button=True),
268
+ dbc.ModalBody(
269
+ dash.html.Div(
270
+ [
271
+ dash.html.H6('Server Information'), # This is the title
272
+ dash.html.Div([
273
+ dbc.InputGroup([dbc.InputGroupText("Client Name"), dbc.Select(
274
+ options=[],
275
+ id="download_opcua_client_names_options"
276
+ )], size="sm", className="mb-1")
277
+ ], style={'border': '1px solid black', 'padding': '10px'}, className="mb-2"),
278
+ ]
279
+ ),
280
+ id=body_id),
281
+ dbc.ModalFooter(
282
+ [
283
+ dbc.Button(
284
+ "OK",
285
+ id=ok_button_id,
286
+ className="float-start",
287
+ n_clicks=0,
288
+ ),
289
+ dbc.Button(
290
+ "Cancel",
291
+ id=cancel_button_id,
292
+ className="ms-auto",
293
+ n_clicks=0,
294
+ )
295
+ ]
296
+ ),
297
+ ],
298
+ id=modal_id,
299
+ centered=True,
300
+ is_open=False,
301
+ ),
302
+ ]
303
+ )
304
+
305
+ @classmethod
306
+ def flatten_dict(cls, d, parent_name=''):
307
+ flattened_data = []
308
+ current_name = f"{parent_name}.{d['title']}" if parent_name else d['title']
309
+ current_info = {
310
+ 'name': current_name,
311
+ 'namespace': d['key'],
312
+ 'NodeClass': d['NodeClass']
313
+ }
314
+ flattened_data.append(current_info)
315
+ for child in d.get('children', []):
316
+ flattened_data.extend(cls.flatten_dict(child, current_name))
317
+ return flattened_data
318
+ @classmethod
319
+ def get_opcua_tree(cls, app):
320
+
321
+ clients = app.automation.get_opcua_clients()
322
+ data = list()
323
+ for client_name, _ in clients.items():
324
+
325
+ opcua_tree = app.automation.get_opcua_tree(client_name=client_name)
326
+ opcua_tree = opcua_tree[0]['Objects'][0]
327
+ opcua_tree["title"] = client_name
328
+ data.append(opcua_tree)
329
+
330
+ data = file_tree.render(data)
331
+
332
+ return data
333
+
334
+ def data_access_view_table(self, data:list=[])->dash.dash_table.DataTable:
335
+ r"""
336
+ Documentation here
337
+ """
338
+ self.data = data
339
+ return dbc.Container(
340
+ dbc.Row(
341
+ dbc.Col(
342
+ dash.dash_table.DataTable(
343
+ data=self.data,
344
+ columns=[
345
+ {'name': 'server', 'id': 'server', 'editable': False},
346
+ {'name': 'namespace', 'id': 'namespace', 'editable': False},
347
+ {'name': 'data_type', 'id': 'data_type', 'editable': False},
348
+ {'name': 'display_name', 'id': 'display_name', 'editable': False},
349
+ {'name': 'value', 'id': 'value', 'editable': False},
350
+ {'name': 'source_timestamp', 'id': 'source_timestamp', 'editable': False},
351
+ {'name': 'status_code', 'id': 'status_code', 'editable': False}
352
+ ],
353
+ id="data_access_view_datatable",
354
+ # row_selectable='single',
355
+ page_action="native",
356
+ page_current= 0,
357
+ page_size= 10,
358
+ persistence=True,
359
+ editable=False,
360
+ persisted_props=['data'],
361
+ export_format='xlsx',
362
+ export_headers='display',
363
+ style_table={'overflowX': 'auto'},
364
+ ),
365
+ width=12,
366
+ )
367
+ ),
368
+ fluid=True,
369
+ className="mx-0 px-0"
370
+ )
371
+
372
+ def update_data_access_view(self, namespace, value, timestamp)->dash.dash_table.DataTable:
373
+ r"""
374
+ Documentation here
375
+ """
376
+ _data = self.data.copy()
377
+ for counter, data in enumerate(_data):
378
+
379
+ if namespace==data["namespace"]:
380
+
381
+ self.data[counter]["value"] = value
382
+ self.data[counter]["source_timestamp"] = timestamp
383
+
384
+ return self.data
@@ -0,0 +1,53 @@
1
+ import dash
2
+ import dash_bootstrap_components as dbc
3
+
4
+ class OPCUAServerComponents:
5
+
6
+ @classmethod
7
+ def opcua_server_table(cls)->dash.dash_table.DataTable:
8
+ r"""
9
+ Documentation here
10
+ """
11
+ return dbc.Container(
12
+ dbc.Row(
13
+ dbc.Col(
14
+ dash.dash_table.DataTable(
15
+ data=[],
16
+ columns=[
17
+ {'name': 'name', 'id': 'name', 'editable': False},
18
+ {'name': 'namespace', 'id': 'namespace', 'editable': False},
19
+ {'name': 'access type', 'id': 'access_type', 'presentation': 'dropdown', 'clearable': False}
20
+ ],
21
+ id="opcua_server_datatable",
22
+ filter_action="native",
23
+ sort_action="native",
24
+ sort_mode="multi",
25
+ row_deletable=True,
26
+ selected_columns=[],
27
+ dropdown = {
28
+ 'access_type': {
29
+ 'options': [
30
+ {'label': 'Read', 'value': 'Read'},
31
+ {'label': 'ReadWrite', 'value': 'ReadWrite'},
32
+ {'label': 'Write', 'value': 'Write'}
33
+ ],
34
+ 'clearable': False
35
+ }
36
+ },
37
+ page_action="native",
38
+ page_current= 0,
39
+ page_size= 10,
40
+ persistence=True,
41
+ editable=True,
42
+ persisted_props=['data'],
43
+ export_format='xlsx',
44
+ export_headers='display',
45
+ style_table={'overflowX': 'auto'},
46
+ ),
47
+ width=12,
48
+ )
49
+ ),
50
+ fluid=True,
51
+ className="mx-0 px-0",
52
+
53
+ )