PyAutomationIO 0.0.0__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.
- automation/__init__.py +46 -0
- automation/alarms/__init__.py +563 -0
- automation/alarms/states.py +192 -0
- automation/alarms/trigger.py +64 -0
- automation/buffer.py +132 -0
- automation/core.py +1775 -0
- automation/dbmodels/__init__.py +23 -0
- automation/dbmodels/alarms.py +524 -0
- automation/dbmodels/core.py +86 -0
- automation/dbmodels/events.py +153 -0
- automation/dbmodels/logs.py +155 -0
- automation/dbmodels/machines.py +181 -0
- automation/dbmodels/opcua.py +81 -0
- automation/dbmodels/opcua_server.py +174 -0
- automation/dbmodels/tags.py +921 -0
- automation/dbmodels/users.py +259 -0
- automation/extensions/__init__.py +15 -0
- automation/extensions/api.py +149 -0
- automation/extensions/cors.py +18 -0
- automation/filter/__init__.py +19 -0
- automation/iad/__init__.py +3 -0
- automation/iad/frozen_data.py +54 -0
- automation/iad/out_of_range.py +51 -0
- automation/iad/outliers.py +51 -0
- automation/logger/__init__.py +0 -0
- automation/logger/alarms.py +426 -0
- automation/logger/core.py +265 -0
- automation/logger/datalogger.py +646 -0
- automation/logger/events.py +194 -0
- automation/logger/logdict.py +53 -0
- automation/logger/logs.py +203 -0
- automation/logger/machines.py +248 -0
- automation/logger/opcua_server.py +130 -0
- automation/logger/users.py +96 -0
- automation/managers/__init__.py +4 -0
- automation/managers/alarms.py +455 -0
- automation/managers/db.py +328 -0
- automation/managers/opcua_client.py +186 -0
- automation/managers/state_machine.py +183 -0
- automation/models.py +174 -0
- automation/modules/__init__.py +14 -0
- automation/modules/alarms/__init__.py +0 -0
- automation/modules/alarms/resources/__init__.py +10 -0
- automation/modules/alarms/resources/alarms.py +280 -0
- automation/modules/alarms/resources/summary.py +79 -0
- automation/modules/events/__init__.py +0 -0
- automation/modules/events/resources/__init__.py +10 -0
- automation/modules/events/resources/events.py +83 -0
- automation/modules/events/resources/logs.py +109 -0
- automation/modules/tags/__init__.py +0 -0
- automation/modules/tags/resources/__init__.py +8 -0
- automation/modules/tags/resources/tags.py +201 -0
- automation/modules/users/__init__.py +2 -0
- automation/modules/users/resources/__init__.py +10 -0
- automation/modules/users/resources/models/__init__.py +2 -0
- automation/modules/users/resources/models/roles.py +5 -0
- automation/modules/users/resources/models/users.py +14 -0
- automation/modules/users/resources/roles.py +38 -0
- automation/modules/users/resources/users.py +113 -0
- automation/modules/users/roles.py +121 -0
- automation/modules/users/users.py +335 -0
- automation/opcua/__init__.py +1 -0
- automation/opcua/models.py +541 -0
- automation/opcua/subscription.py +259 -0
- automation/pages/__init__.py +0 -0
- automation/pages/alarms.py +34 -0
- automation/pages/alarms_history.py +21 -0
- automation/pages/assets/styles.css +7 -0
- automation/pages/callbacks/__init__.py +28 -0
- automation/pages/callbacks/alarms.py +218 -0
- automation/pages/callbacks/alarms_summary.py +20 -0
- automation/pages/callbacks/db.py +222 -0
- automation/pages/callbacks/filter.py +238 -0
- automation/pages/callbacks/machines.py +29 -0
- automation/pages/callbacks/machines_detailed.py +581 -0
- automation/pages/callbacks/opcua.py +266 -0
- automation/pages/callbacks/opcua_server.py +244 -0
- automation/pages/callbacks/tags.py +495 -0
- automation/pages/callbacks/trends.py +119 -0
- automation/pages/communications.py +129 -0
- automation/pages/components/__init__.py +123 -0
- automation/pages/components/alarms.py +151 -0
- automation/pages/components/alarms_summary.py +45 -0
- automation/pages/components/database.py +128 -0
- automation/pages/components/gaussian_filter.py +69 -0
- automation/pages/components/machines.py +396 -0
- automation/pages/components/opcua.py +384 -0
- automation/pages/components/opcua_server.py +53 -0
- automation/pages/components/tags.py +253 -0
- automation/pages/components/trends.py +66 -0
- automation/pages/database.py +26 -0
- automation/pages/filter.py +55 -0
- automation/pages/machines.py +20 -0
- automation/pages/machines_detailed.py +41 -0
- automation/pages/main.py +63 -0
- automation/pages/opcua_server.py +28 -0
- automation/pages/tags.py +40 -0
- automation/pages/trends.py +35 -0
- automation/singleton.py +30 -0
- automation/state_machine.py +1672 -0
- automation/tags/__init__.py +2 -0
- automation/tags/cvt.py +1198 -0
- automation/tags/filter.py +55 -0
- automation/tags/tag.py +418 -0
- automation/tests/__init__.py +10 -0
- automation/tests/test_alarms.py +110 -0
- automation/tests/test_core.py +257 -0
- automation/tests/test_unit.py +21 -0
- automation/tests/test_user.py +155 -0
- automation/utils/__init__.py +164 -0
- automation/utils/decorators.py +222 -0
- automation/utils/npw.py +294 -0
- automation/utils/observer.py +21 -0
- automation/utils/units.py +118 -0
- automation/variables/__init__.py +55 -0
- automation/variables/adimentional.py +30 -0
- automation/variables/current.py +71 -0
- automation/variables/density.py +115 -0
- automation/variables/eng_time.py +68 -0
- automation/variables/force.py +90 -0
- automation/variables/length.py +104 -0
- automation/variables/mass.py +80 -0
- automation/variables/mass_flow.py +101 -0
- automation/variables/percentage.py +30 -0
- automation/variables/power.py +113 -0
- automation/variables/pressure.py +93 -0
- automation/variables/temperature.py +168 -0
- automation/variables/volume.py +70 -0
- automation/variables/volumetric_flow.py +100 -0
- automation/workers/__init__.py +2 -0
- automation/workers/logger.py +164 -0
- automation/workers/state_machine.py +207 -0
- automation/workers/worker.py +36 -0
- pyautomationio-0.0.0.dist-info/METADATA +198 -0
- pyautomationio-0.0.0.dist-info/RECORD +138 -0
- pyautomationio-0.0.0.dist-info/WHEEL +5 -0
- pyautomationio-0.0.0.dist-info/licenses/LICENSE +21 -0
- pyautomationio-0.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""pyhades/managers/logger.py
|
|
3
|
+
|
|
4
|
+
This module implements Logger Manager.
|
|
5
|
+
"""
|
|
6
|
+
import logging, queue
|
|
7
|
+
from ..singleton import Singleton
|
|
8
|
+
from ..logger.datalogger import DataLoggerEngine
|
|
9
|
+
from ..logger.logdict import LogTable
|
|
10
|
+
from ..logger.alarms import AlarmsLoggerEngine
|
|
11
|
+
from ..logger.events import EventsLoggerEngine
|
|
12
|
+
from ..logger.users import UsersLoggerEngine
|
|
13
|
+
from ..logger.logs import LogsLoggerEngine
|
|
14
|
+
from ..logger.machines import MachinesLoggerEngine
|
|
15
|
+
from ..logger.opcua_server import OPCUAServerLoggerEngine
|
|
16
|
+
from ..tags import CVTEngine, TagObserver
|
|
17
|
+
from ..modules.users.users import User
|
|
18
|
+
from ..utils.decorators import logging_error_handler
|
|
19
|
+
from ..dbmodels import (
|
|
20
|
+
Manufacturer,
|
|
21
|
+
Segment,
|
|
22
|
+
Tags,
|
|
23
|
+
TagValue,
|
|
24
|
+
AlarmTypes,
|
|
25
|
+
AlarmStates,
|
|
26
|
+
Alarms,
|
|
27
|
+
AlarmSummary,
|
|
28
|
+
Variables,
|
|
29
|
+
Units,
|
|
30
|
+
DataTypes,
|
|
31
|
+
OPCUA,
|
|
32
|
+
Users,
|
|
33
|
+
Roles,
|
|
34
|
+
Events,
|
|
35
|
+
Logs,
|
|
36
|
+
Machines,
|
|
37
|
+
TagsMachines,
|
|
38
|
+
AccessType,
|
|
39
|
+
OPCUAServer,
|
|
40
|
+
BaseModel
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class DBManager(Singleton):
|
|
45
|
+
r"""
|
|
46
|
+
Database Manager class for database logging settings.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self, period:float=1.0, delay:float=1.0, drop_tables:bool=False):
|
|
50
|
+
|
|
51
|
+
self._period = period
|
|
52
|
+
self._delay = delay
|
|
53
|
+
self._drop_tables = drop_tables
|
|
54
|
+
self._tag_queue = queue.Queue()
|
|
55
|
+
self.engine = CVTEngine()
|
|
56
|
+
self._logging_tags = LogTable()
|
|
57
|
+
self._logger = DataLoggerEngine()
|
|
58
|
+
self.alarms_logger = AlarmsLoggerEngine()
|
|
59
|
+
self.events_logger = EventsLoggerEngine()
|
|
60
|
+
self.users_logger = UsersLoggerEngine()
|
|
61
|
+
self.logs_logger = LogsLoggerEngine()
|
|
62
|
+
self.machines_logger = MachinesLoggerEngine()
|
|
63
|
+
self.opcuaserver_logger = OPCUAServerLoggerEngine()
|
|
64
|
+
self._tables = [
|
|
65
|
+
Manufacturer,
|
|
66
|
+
Segment,
|
|
67
|
+
Variables,
|
|
68
|
+
Units,
|
|
69
|
+
DataTypes,
|
|
70
|
+
Tags,
|
|
71
|
+
TagValue,
|
|
72
|
+
AlarmTypes,
|
|
73
|
+
AlarmStates,
|
|
74
|
+
Alarms,
|
|
75
|
+
AlarmSummary,
|
|
76
|
+
OPCUA,
|
|
77
|
+
Roles,
|
|
78
|
+
Users,
|
|
79
|
+
Events,
|
|
80
|
+
Logs,
|
|
81
|
+
Machines,
|
|
82
|
+
TagsMachines,
|
|
83
|
+
AccessType,
|
|
84
|
+
OPCUAServer
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
self._extra_tables = []
|
|
88
|
+
|
|
89
|
+
def get_queue(self)->queue.Queue:
|
|
90
|
+
r"""
|
|
91
|
+
Documentation here
|
|
92
|
+
"""
|
|
93
|
+
return self._tag_queue
|
|
94
|
+
|
|
95
|
+
def set_db(self, db, is_history_logged:bool=False):
|
|
96
|
+
r"""
|
|
97
|
+
Initialize a new DB Object SQLite - Postgres - MySQL
|
|
98
|
+
|
|
99
|
+
**Parameters**
|
|
100
|
+
|
|
101
|
+
* **db** (db object): Sqlite - Postgres or MySql db object
|
|
102
|
+
|
|
103
|
+
**Returns** `None`
|
|
104
|
+
"""
|
|
105
|
+
self._logger.set_db(db)
|
|
106
|
+
self._logger.logger.set_is_history_logged(value=is_history_logged)
|
|
107
|
+
self.alarms_logger.set_db(db)
|
|
108
|
+
self.alarms_logger.logger.set_is_history_logged(value=is_history_logged)
|
|
109
|
+
self.events_logger.set_db(db)
|
|
110
|
+
self.events_logger.logger.set_is_history_logged(value=is_history_logged)
|
|
111
|
+
self.users_logger.set_db(db)
|
|
112
|
+
self.logs_logger.set_db(db)
|
|
113
|
+
self.logs_logger.logger.set_is_history_logged(value=is_history_logged)
|
|
114
|
+
self.machines_logger.set_db(db)
|
|
115
|
+
self.opcuaserver_logger.logger.set_db(db)
|
|
116
|
+
|
|
117
|
+
def get_db(self):
|
|
118
|
+
r"""
|
|
119
|
+
Returns a DB object
|
|
120
|
+
"""
|
|
121
|
+
return self._logger.get_db()
|
|
122
|
+
|
|
123
|
+
def set_dropped(self, drop_tables:bool):
|
|
124
|
+
r"""
|
|
125
|
+
Allows to you set a flag variable to drop database tables when run app.
|
|
126
|
+
|
|
127
|
+
**Parameters**
|
|
128
|
+
|
|
129
|
+
* **drop_tables** (bool) If True, drop all tables define in the app an initialized it in blank.
|
|
130
|
+
|
|
131
|
+
**Returns**
|
|
132
|
+
|
|
133
|
+
* **None**
|
|
134
|
+
"""
|
|
135
|
+
self._drop_tables = drop_tables
|
|
136
|
+
|
|
137
|
+
def get_dropped(self)->bool:
|
|
138
|
+
r"""
|
|
139
|
+
Gets flag variables to drop tables when initialize the app
|
|
140
|
+
|
|
141
|
+
**Return**
|
|
142
|
+
|
|
143
|
+
* **drop_tables** (bool) Flag variables to drop table
|
|
144
|
+
"""
|
|
145
|
+
return self._drop_tables
|
|
146
|
+
|
|
147
|
+
def register_table(self, cls:BaseModel):
|
|
148
|
+
r"""
|
|
149
|
+
Allows to you register a new database model
|
|
150
|
+
|
|
151
|
+
**Parameters**
|
|
152
|
+
|
|
153
|
+
* **cls* (BaseModel): A class that inherit from BaseModel
|
|
154
|
+
|
|
155
|
+
"""
|
|
156
|
+
self._tables.append(cls)
|
|
157
|
+
|
|
158
|
+
def get_db_table(self, tablename:str):
|
|
159
|
+
r"""
|
|
160
|
+
Documentation here
|
|
161
|
+
"""
|
|
162
|
+
for table in self._tables:
|
|
163
|
+
|
|
164
|
+
if table._meta.table_name.lower()==tablename.lower():
|
|
165
|
+
|
|
166
|
+
return table
|
|
167
|
+
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
def create_tables(self):
|
|
171
|
+
r"""
|
|
172
|
+
Creates default tables and tables registered with method *register_table*
|
|
173
|
+
"""
|
|
174
|
+
self._tables.extend(self._extra_tables)
|
|
175
|
+
self._logger.create_tables(self._tables)
|
|
176
|
+
self.alarms_logger.create_tables(self._tables)
|
|
177
|
+
|
|
178
|
+
def drop_tables(self):
|
|
179
|
+
r"""
|
|
180
|
+
Drop all tables defined
|
|
181
|
+
"""
|
|
182
|
+
tables = self._tables
|
|
183
|
+
|
|
184
|
+
self._logger.drop_tables(tables)
|
|
185
|
+
|
|
186
|
+
def clear_default_tables(self):
|
|
187
|
+
r"""
|
|
188
|
+
If you want initialize any PyHades app without default tables, you can use this method
|
|
189
|
+
"""
|
|
190
|
+
self._tables = []
|
|
191
|
+
|
|
192
|
+
def get_tags(self)->dict:
|
|
193
|
+
r"""
|
|
194
|
+
Gets all tag defined in tag's repository
|
|
195
|
+
"""
|
|
196
|
+
return self._logger.get_tags()
|
|
197
|
+
|
|
198
|
+
def get_alarms(self)->dict:
|
|
199
|
+
r"""
|
|
200
|
+
Gets all tag defined in tag's repository
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
return self.alarms_logger.get_alarms()
|
|
204
|
+
|
|
205
|
+
@logging_error_handler
|
|
206
|
+
def set_tag(
|
|
207
|
+
self,
|
|
208
|
+
tag:str,
|
|
209
|
+
unit:str,
|
|
210
|
+
data_type:str,
|
|
211
|
+
description:str,
|
|
212
|
+
display_name:str="",
|
|
213
|
+
min_value:float=None,
|
|
214
|
+
max_value:float=None,
|
|
215
|
+
tcp_source_address:str=None,
|
|
216
|
+
node_namespace:str=None):
|
|
217
|
+
r"""
|
|
218
|
+
Sets tag to Database
|
|
219
|
+
|
|
220
|
+
**Parameters**
|
|
221
|
+
|
|
222
|
+
* **tag** (str):
|
|
223
|
+
* **unit** (str):
|
|
224
|
+
* **data_type** (str):
|
|
225
|
+
* **description** (str):
|
|
226
|
+
* **min_value** (float)[Optional]:
|
|
227
|
+
* **max_value** (float)[Optional]:
|
|
228
|
+
* **tcp_source_address** (str)[Optional]:
|
|
229
|
+
* **node_namespace** (str)[Optional]:
|
|
230
|
+
"""
|
|
231
|
+
self._logger.set_tag(
|
|
232
|
+
tag=tag,
|
|
233
|
+
unit=unit,
|
|
234
|
+
data_type=data_type,
|
|
235
|
+
description=description,
|
|
236
|
+
display_name=display_name,
|
|
237
|
+
min_value=min_value,
|
|
238
|
+
max_value=max_value,
|
|
239
|
+
tcp_source_address=tcp_source_address,
|
|
240
|
+
node_namespace=node_namespace
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
def set_tags(self):
|
|
244
|
+
r"""
|
|
245
|
+
Allows to you define all tags added with *add_tag* method
|
|
246
|
+
"""
|
|
247
|
+
for period in self._logging_tags.get_groups():
|
|
248
|
+
|
|
249
|
+
tags = self._logging_tags.get_tags(period)
|
|
250
|
+
|
|
251
|
+
for tag, unit, data_type, description, display_name, min_value, max_value, tcp_source_address, node_namespace in tags:
|
|
252
|
+
|
|
253
|
+
self.set_tag(
|
|
254
|
+
tag=tag,
|
|
255
|
+
unit=unit,
|
|
256
|
+
data_type=data_type,
|
|
257
|
+
description=description,
|
|
258
|
+
display_name=display_name,
|
|
259
|
+
min_value=min_value,
|
|
260
|
+
max_value=max_value,
|
|
261
|
+
tcp_source_address=tcp_source_address,
|
|
262
|
+
node_namespace=node_namespace)
|
|
263
|
+
|
|
264
|
+
def init_database(self):
|
|
265
|
+
r"""
|
|
266
|
+
Initializes all databases.
|
|
267
|
+
"""
|
|
268
|
+
if self.get_dropped():
|
|
269
|
+
try:
|
|
270
|
+
self.drop_tables()
|
|
271
|
+
except Exception as e:
|
|
272
|
+
error = str(e)
|
|
273
|
+
logger = logging.getLogger("pyautomation")
|
|
274
|
+
logger.error("Database:{}".format(error))
|
|
275
|
+
|
|
276
|
+
self.create_tables()
|
|
277
|
+
|
|
278
|
+
def stop_database(self):
|
|
279
|
+
r"""
|
|
280
|
+
Documentation here
|
|
281
|
+
"""
|
|
282
|
+
self._logger.stop_db()
|
|
283
|
+
|
|
284
|
+
def get_opcua_clients(self):
|
|
285
|
+
r"""
|
|
286
|
+
Documentation here
|
|
287
|
+
"""
|
|
288
|
+
return OPCUA.read_all()
|
|
289
|
+
|
|
290
|
+
# USERS METHODS
|
|
291
|
+
def set_role(self, name:str, level:int, identifier:str):
|
|
292
|
+
r"""
|
|
293
|
+
Documentation here
|
|
294
|
+
"""
|
|
295
|
+
return self.users_logger.set_role(name=name, level=level, identifier=identifier)
|
|
296
|
+
|
|
297
|
+
def set_user(self, user:User):
|
|
298
|
+
r"""
|
|
299
|
+
Documentation here
|
|
300
|
+
"""
|
|
301
|
+
return self.users_logger.set_user(user=user)
|
|
302
|
+
|
|
303
|
+
def login(self, password:str, username:str="", email:str=""):
|
|
304
|
+
r"""
|
|
305
|
+
Documentation here
|
|
306
|
+
"""
|
|
307
|
+
return self.users_logger.login(password=password, username=username, email=email)
|
|
308
|
+
|
|
309
|
+
def summary(self)->dict:
|
|
310
|
+
r"""
|
|
311
|
+
Get database manager summary
|
|
312
|
+
|
|
313
|
+
**Returns**
|
|
314
|
+
|
|
315
|
+
* **summary** (dict): Database summary
|
|
316
|
+
"""
|
|
317
|
+
result = dict()
|
|
318
|
+
|
|
319
|
+
result["period"] = self.get_period()
|
|
320
|
+
result["tags"] = self.get_tags()
|
|
321
|
+
result["delay"] = self.get_delay()
|
|
322
|
+
|
|
323
|
+
return result
|
|
324
|
+
|
|
325
|
+
def attach(self, tag_name:str):
|
|
326
|
+
|
|
327
|
+
observer = TagObserver(self._tag_queue)
|
|
328
|
+
self.engine.attach(name=tag_name, observer=observer)
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
from ..opcua.models import Client
|
|
2
|
+
from ..dbmodels import OPCUA
|
|
3
|
+
from ..logger.datalogger import DataLoggerEngine
|
|
4
|
+
from ..tags import CVTEngine
|
|
5
|
+
from ..opcua.subscription import DAS
|
|
6
|
+
|
|
7
|
+
class OPCUAClientManager:
|
|
8
|
+
r"""
|
|
9
|
+
Documentation here
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
r"""
|
|
14
|
+
Documentation here
|
|
15
|
+
"""
|
|
16
|
+
self._clients = dict()
|
|
17
|
+
self.logger = DataLoggerEngine()
|
|
18
|
+
self.cvt = CVTEngine()
|
|
19
|
+
self.das = DAS()
|
|
20
|
+
|
|
21
|
+
def discovery(self, host:str='127.0.0.1', port:int=4840)->list[dict]:
|
|
22
|
+
r"""
|
|
23
|
+
Documentation here
|
|
24
|
+
"""
|
|
25
|
+
return Client.find_servers(host, port)
|
|
26
|
+
|
|
27
|
+
def add(self, client_name:str, host:str, port:int):
|
|
28
|
+
r"""
|
|
29
|
+
Documentation here
|
|
30
|
+
"""
|
|
31
|
+
endpoint_url = f"opc.tcp://{host}:{port}"
|
|
32
|
+
if client_name in self._clients:
|
|
33
|
+
|
|
34
|
+
return True, f"Client Name {client_name} duplicated"
|
|
35
|
+
|
|
36
|
+
opcua_client = Client(endpoint_url, client_name=client_name)
|
|
37
|
+
|
|
38
|
+
message, status_connection = opcua_client.connect()
|
|
39
|
+
if status_connection==200:
|
|
40
|
+
|
|
41
|
+
self._clients[client_name] = opcua_client
|
|
42
|
+
|
|
43
|
+
# DATABASE PERSISTENCY
|
|
44
|
+
if self.logger.get_db():
|
|
45
|
+
|
|
46
|
+
OPCUA.create(client_name=client_name, host=host, port=port)
|
|
47
|
+
|
|
48
|
+
# RECONNECT TO SUBSCRIPTION
|
|
49
|
+
for tag in self.cvt.get_tags():
|
|
50
|
+
|
|
51
|
+
if tag["opcua_address"]==endpoint_url:
|
|
52
|
+
|
|
53
|
+
if not tag["scan_time"]:
|
|
54
|
+
|
|
55
|
+
subscription = opcua_client.create_subscription(1000, self.das)
|
|
56
|
+
node_id = opcua_client.get_node_id_by_namespace(tag["node_namespace"])
|
|
57
|
+
self.das.subscribe(subscription=subscription, client_name=client_name, node_id=node_id)
|
|
58
|
+
|
|
59
|
+
self.das.restart_buffer(tag=self.cvt.get_tag(id=tag["id"]))
|
|
60
|
+
|
|
61
|
+
return True, message
|
|
62
|
+
|
|
63
|
+
return False, message
|
|
64
|
+
|
|
65
|
+
def remove(self, client_name:str):
|
|
66
|
+
r"""
|
|
67
|
+
Documentation here
|
|
68
|
+
"""
|
|
69
|
+
if client_name in self._clients:
|
|
70
|
+
try:
|
|
71
|
+
opcua_client = self._clients.pop(client_name)
|
|
72
|
+
opcua_client.disconnect()
|
|
73
|
+
# DATABASE PERSISTENCY
|
|
74
|
+
opcua = OPCUA.get_by_client_name(client_name=client_name)
|
|
75
|
+
if opcua:
|
|
76
|
+
if self.logger.get_db():
|
|
77
|
+
query = OPCUA.delete().where(OPCUA.client_name == client_name)
|
|
78
|
+
query.execute()
|
|
79
|
+
|
|
80
|
+
return True
|
|
81
|
+
except Exception as err:
|
|
82
|
+
|
|
83
|
+
return False
|
|
84
|
+
|
|
85
|
+
return False
|
|
86
|
+
|
|
87
|
+
def connect(self, client_name:str)->dict:
|
|
88
|
+
r"""
|
|
89
|
+
Documentation here
|
|
90
|
+
"""
|
|
91
|
+
if client_name in self._clients:
|
|
92
|
+
|
|
93
|
+
self._clients[client_name].connect()
|
|
94
|
+
|
|
95
|
+
def disconnect(self, client_name:str)->dict:
|
|
96
|
+
r"""
|
|
97
|
+
Documentation here
|
|
98
|
+
"""
|
|
99
|
+
if client_name in self._clients:
|
|
100
|
+
|
|
101
|
+
self._clients[client_name].disconnect()
|
|
102
|
+
|
|
103
|
+
def get(self, client_name:str)->Client:
|
|
104
|
+
r"""
|
|
105
|
+
Documentation here
|
|
106
|
+
"""
|
|
107
|
+
if client_name in self._clients:
|
|
108
|
+
|
|
109
|
+
return self._clients[client_name]
|
|
110
|
+
|
|
111
|
+
def get_opcua_tree(self, client_name):
|
|
112
|
+
r"""
|
|
113
|
+
Documentation here
|
|
114
|
+
"""
|
|
115
|
+
client = self.get(client_name=client_name)
|
|
116
|
+
if client.is_connected():
|
|
117
|
+
root_node = client.get_root_node()
|
|
118
|
+
_tree = client.browse_tree(root_node)
|
|
119
|
+
result = {
|
|
120
|
+
"Objects": _tree[0]["children"]
|
|
121
|
+
}
|
|
122
|
+
return result, 200
|
|
123
|
+
|
|
124
|
+
return {}, 400
|
|
125
|
+
|
|
126
|
+
def get_node_values(self, client_name:str, namespaces:list)->list:
|
|
127
|
+
|
|
128
|
+
if client_name in self._clients:
|
|
129
|
+
|
|
130
|
+
client = self._clients[client_name]
|
|
131
|
+
if client.is_conneted():
|
|
132
|
+
return client.get_nodes_values(namespaces=namespaces)
|
|
133
|
+
|
|
134
|
+
def get_client_by_address(self, opcua_address:str)->Client|None:
|
|
135
|
+
r"""
|
|
136
|
+
Obtiene el cliente OPC UA correspondiente a una dirección
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
opcua_address: Dirección del servidor OPC UA (ej: "opc.tcp://localhost:4840")
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Client: Cliente OPC UA si existe y está conectado, None en caso contrario
|
|
143
|
+
"""
|
|
144
|
+
for client_name, client in self._clients.items():
|
|
145
|
+
if opcua_address == client.serialize()["server_url"]:
|
|
146
|
+
if client.is_connected():
|
|
147
|
+
return client
|
|
148
|
+
return None
|
|
149
|
+
|
|
150
|
+
def get_node_value_by_opcua_address(self, opcua_address:str, namespace:str)->list:
|
|
151
|
+
r"""
|
|
152
|
+
Documentation here
|
|
153
|
+
"""
|
|
154
|
+
for client_name, client in self._clients.items():
|
|
155
|
+
|
|
156
|
+
if opcua_address==client.serialize()["server_url"]:
|
|
157
|
+
if client.is_connected():
|
|
158
|
+
return self.get_node_attributes(client_name=client_name, namespaces=[namespace])
|
|
159
|
+
|
|
160
|
+
def get_node_attributes(self, client_name:str, namespaces:list)->list:
|
|
161
|
+
|
|
162
|
+
result = list()
|
|
163
|
+
|
|
164
|
+
if client_name in self._clients:
|
|
165
|
+
|
|
166
|
+
client = self._clients[client_name]
|
|
167
|
+
|
|
168
|
+
for namespace in namespaces:
|
|
169
|
+
if client.is_connected():
|
|
170
|
+
result.append(client.get_node_attributes(node_namespace=namespace))
|
|
171
|
+
|
|
172
|
+
return result
|
|
173
|
+
|
|
174
|
+
def serialize(self, client_name:str=None)->dict:
|
|
175
|
+
r"""
|
|
176
|
+
Documentation here
|
|
177
|
+
"""
|
|
178
|
+
if client_name:
|
|
179
|
+
|
|
180
|
+
if client_name in self._clients:
|
|
181
|
+
|
|
182
|
+
opcua_client = self._clients[client_name]
|
|
183
|
+
|
|
184
|
+
return opcua_client.serialize()
|
|
185
|
+
|
|
186
|
+
return {client_name: client.serialize() for client_name, client in self._clients.items()}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""broker/managers/state_machine.py
|
|
3
|
+
|
|
4
|
+
This module implements Function Manager.
|
|
5
|
+
"""
|
|
6
|
+
from statemachine import StateMachine
|
|
7
|
+
from ..models import StringType
|
|
8
|
+
from ..tags import TagObserver, CVTEngine, Tag
|
|
9
|
+
import queue
|
|
10
|
+
|
|
11
|
+
class StateMachineManager:
|
|
12
|
+
r"""
|
|
13
|
+
Handles and manager the state machines defined in the application in a store defined by a list of tuples
|
|
14
|
+
|
|
15
|
+
Its structure is [(machine_1, interval_1, mode_1), (machine_2, interval_2, mode_2), ... (machine_n, interval_n, mode_n)]
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self):
|
|
19
|
+
|
|
20
|
+
self._machines = list()
|
|
21
|
+
self._tag_queue = queue.Queue()
|
|
22
|
+
|
|
23
|
+
def get_queue(self)->queue.Queue:
|
|
24
|
+
r"""Documentation here
|
|
25
|
+
|
|
26
|
+
# Parameters
|
|
27
|
+
|
|
28
|
+
-
|
|
29
|
+
|
|
30
|
+
# Returns
|
|
31
|
+
|
|
32
|
+
-
|
|
33
|
+
"""
|
|
34
|
+
return self._tag_queue
|
|
35
|
+
|
|
36
|
+
def append_machine(self, machine:StateMachine):
|
|
37
|
+
r"""
|
|
38
|
+
Appends machines to the store
|
|
39
|
+
|
|
40
|
+
**Parameters**
|
|
41
|
+
|
|
42
|
+
* **machine:** (PyHadesStateMachine) instance
|
|
43
|
+
* **interval:** (float) Execution interval in seconds
|
|
44
|
+
* **mode:** (str) Thread mode of the state machine, allowed mode ('sync', 'async')
|
|
45
|
+
|
|
46
|
+
**Returns** `None`
|
|
47
|
+
|
|
48
|
+
Usage
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
>>> manager = app.get_state_machine_manager()
|
|
52
|
+
>>> manager.append_machine(machine, interval, mode)
|
|
53
|
+
```
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
self._machines.append(machine)
|
|
57
|
+
|
|
58
|
+
def get_machines(self)->list:
|
|
59
|
+
r"""
|
|
60
|
+
Gets state machines
|
|
61
|
+
|
|
62
|
+
**Returns**
|
|
63
|
+
|
|
64
|
+
* **machines** (list of tuples)
|
|
65
|
+
|
|
66
|
+
Usage
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
>>> manager = app.get_state_machine_manager()
|
|
70
|
+
>>> machines = manager.get_machines()
|
|
71
|
+
[(machine_1, interval_1, mode_1), (machine_2, interval_2, mode_2), ... (machine_n, interval_n, mode_n)]
|
|
72
|
+
```
|
|
73
|
+
"""
|
|
74
|
+
result = self._machines
|
|
75
|
+
|
|
76
|
+
return result
|
|
77
|
+
|
|
78
|
+
def serialize_machines(self):
|
|
79
|
+
r"""
|
|
80
|
+
Documentation here
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
return [machine.serialize() for machine, _, _ in self.get_machines()]
|
|
84
|
+
|
|
85
|
+
def get_machine(self, name:StringType)->StateMachine:
|
|
86
|
+
r"""
|
|
87
|
+
Gets state machine by its name
|
|
88
|
+
|
|
89
|
+
**Parameters**
|
|
90
|
+
|
|
91
|
+
* **name:** (str) State machine name
|
|
92
|
+
|
|
93
|
+
**Returns**
|
|
94
|
+
|
|
95
|
+
* **machine:** (PyHadesStateMachine) instance
|
|
96
|
+
|
|
97
|
+
Usage
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
>>> manager = app.get_state_machine_manager()
|
|
101
|
+
>>> machine = manager.get_machine(state_machine_name)
|
|
102
|
+
```
|
|
103
|
+
"""
|
|
104
|
+
for machine, _, _ in self._machines:
|
|
105
|
+
|
|
106
|
+
if name.value == machine.name.value:
|
|
107
|
+
|
|
108
|
+
return machine
|
|
109
|
+
|
|
110
|
+
def drop(self, name:str):
|
|
111
|
+
r"""
|
|
112
|
+
Documentation here
|
|
113
|
+
"""
|
|
114
|
+
index = 0
|
|
115
|
+
for machine, _, _ in self._machines:
|
|
116
|
+
|
|
117
|
+
if name == machine.name.value:
|
|
118
|
+
|
|
119
|
+
machine_to_revome_from_worker = self._machines.pop(index)
|
|
120
|
+
break
|
|
121
|
+
|
|
122
|
+
index += 1
|
|
123
|
+
|
|
124
|
+
if machine_to_revome_from_worker:
|
|
125
|
+
|
|
126
|
+
return machine_to_revome_from_worker
|
|
127
|
+
|
|
128
|
+
def unsubscribe_tag(self, tag:Tag):
|
|
129
|
+
r"""
|
|
130
|
+
Documentation here
|
|
131
|
+
"""
|
|
132
|
+
machine_to_revome_from_worker = (None, None, None)
|
|
133
|
+
for machine, _, _ in self._machines:
|
|
134
|
+
|
|
135
|
+
if hasattr(machine, "unsubscribe_to"):
|
|
136
|
+
|
|
137
|
+
machine.unsubscribe_to(tag=tag)
|
|
138
|
+
|
|
139
|
+
if machine.classification.value.lower()=="data acquisition system":
|
|
140
|
+
|
|
141
|
+
if not machine.get_subscribed_tags():
|
|
142
|
+
|
|
143
|
+
machine_to_revome_from_worker = self.drop(name=machine.name.value)
|
|
144
|
+
break
|
|
145
|
+
|
|
146
|
+
if machine_to_revome_from_worker:
|
|
147
|
+
|
|
148
|
+
return machine_to_revome_from_worker
|
|
149
|
+
|
|
150
|
+
def summary(self)->dict:
|
|
151
|
+
r"""
|
|
152
|
+
Get a summary of the state machine defined
|
|
153
|
+
|
|
154
|
+
**Returns**
|
|
155
|
+
|
|
156
|
+
* **summary:** (dict) with keys ('length' (int) - 'state_machines' (list of state machine names))
|
|
157
|
+
"""
|
|
158
|
+
result = dict()
|
|
159
|
+
machines = [machine.name for machine, _, _ in self.get_machines()]
|
|
160
|
+
|
|
161
|
+
result["length"] = len(machines)
|
|
162
|
+
result["state_machines"] = machines
|
|
163
|
+
|
|
164
|
+
return result
|
|
165
|
+
|
|
166
|
+
def exist_machines(self)->bool:
|
|
167
|
+
r"""
|
|
168
|
+
Checks if exist state machines defined
|
|
169
|
+
|
|
170
|
+
**Returns**
|
|
171
|
+
|
|
172
|
+
* **Bool**
|
|
173
|
+
|
|
174
|
+
Usage
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
>>> manager = app.get_state_machine_manager()
|
|
178
|
+
>>> manager.exist_machines()
|
|
179
|
+
```
|
|
180
|
+
"""
|
|
181
|
+
return len(self._machines) > 0
|
|
182
|
+
|
|
183
|
+
|