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.
- 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 +1792 -0
- automation/dbmodels/__init__.py +23 -0
- automation/dbmodels/alarms.py +549 -0
- automation/dbmodels/core.py +86 -0
- automation/dbmodels/events.py +178 -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 +434 -0
- automation/logger/core.py +265 -0
- automation/logger/datalogger.py +877 -0
- automation/logger/events.py +202 -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 +81 -0
- automation/modules/events/__init__.py +0 -0
- automation/modules/events/resources/__init__.py +10 -0
- automation/modules/events/resources/events.py +85 -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 +254 -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 +1674 -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-1.1.1.dist-info/METADATA +199 -0
- pyautomationio-1.1.1.dist-info/RECORD +138 -0
- pyautomationio-1.1.1.dist-info/WHEEL +5 -0
- pyautomationio-1.1.1.dist-info/licenses/LICENSE +21 -0
- pyautomationio-1.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,921 @@
|
|
|
1
|
+
from peewee import CharField, BooleanField, FloatField, ForeignKeyField, IntegerField, TimestampField, BooleanField
|
|
2
|
+
from .core import BaseModel
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Manufacturer(BaseModel):
|
|
7
|
+
|
|
8
|
+
name = CharField(unique=True)
|
|
9
|
+
|
|
10
|
+
@classmethod
|
|
11
|
+
def create(cls, name:str)-> dict:
|
|
12
|
+
r"""
|
|
13
|
+
|
|
14
|
+
"""
|
|
15
|
+
if not cls.name_exist(name):
|
|
16
|
+
|
|
17
|
+
query = cls(name=name)
|
|
18
|
+
query.save()
|
|
19
|
+
|
|
20
|
+
return query
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def read_by_name(cls, name:str)->bool:
|
|
24
|
+
r"""
|
|
25
|
+
Get instance by its a name
|
|
26
|
+
|
|
27
|
+
**Parameters**
|
|
28
|
+
|
|
29
|
+
* **name:** (str) Variable name
|
|
30
|
+
|
|
31
|
+
**Returns**
|
|
32
|
+
|
|
33
|
+
* **bool:** If True, name exist into database
|
|
34
|
+
"""
|
|
35
|
+
query = cls.get_or_none(name=name)
|
|
36
|
+
|
|
37
|
+
if query is not None:
|
|
38
|
+
|
|
39
|
+
return query
|
|
40
|
+
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def name_exist(cls, name:str)->bool:
|
|
45
|
+
r"""
|
|
46
|
+
Verify is a name exist into database
|
|
47
|
+
|
|
48
|
+
**Parameters**
|
|
49
|
+
|
|
50
|
+
* **name:** (str) Variable name
|
|
51
|
+
|
|
52
|
+
**Returns**
|
|
53
|
+
|
|
54
|
+
* **bool:** If True, name exist into database
|
|
55
|
+
"""
|
|
56
|
+
query = cls.get_or_none(name=name)
|
|
57
|
+
|
|
58
|
+
if query is not None:
|
|
59
|
+
|
|
60
|
+
return True
|
|
61
|
+
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
def serialize(self)-> dict:
|
|
65
|
+
r"""
|
|
66
|
+
Serialize database record to a jsonable object
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
"id": self.id,
|
|
71
|
+
"name": self.name
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class Segment(BaseModel):
|
|
76
|
+
|
|
77
|
+
name = CharField()
|
|
78
|
+
manufacturer = ForeignKeyField(Manufacturer, backref='segments')
|
|
79
|
+
|
|
80
|
+
@classmethod
|
|
81
|
+
def create(cls, name:str, manufacturer:str)-> dict:
|
|
82
|
+
r"""Documentation here
|
|
83
|
+
"""
|
|
84
|
+
if Manufacturer.name_exist(name=manufacturer):
|
|
85
|
+
|
|
86
|
+
manufacturer_obj = Manufacturer.read_by_name(name=manufacturer)
|
|
87
|
+
|
|
88
|
+
else:
|
|
89
|
+
|
|
90
|
+
manufacturer_obj = Manufacturer.create(name=manufacturer)
|
|
91
|
+
|
|
92
|
+
segment_obj = Segment.select().where(Segment.name == name, Segment.manufacturer == manufacturer_obj).exists()
|
|
93
|
+
|
|
94
|
+
if not segment_obj:
|
|
95
|
+
query = cls(name=name, manufacturer=manufacturer_obj)
|
|
96
|
+
query.save()
|
|
97
|
+
return query
|
|
98
|
+
|
|
99
|
+
return Segment.read_by_name(name=name)
|
|
100
|
+
|
|
101
|
+
@classmethod
|
|
102
|
+
def read_by_name(cls, name:str)->bool:
|
|
103
|
+
r"""
|
|
104
|
+
|
|
105
|
+
"""
|
|
106
|
+
query = cls.get_or_none(name=name)
|
|
107
|
+
|
|
108
|
+
if query is not None:
|
|
109
|
+
|
|
110
|
+
return query
|
|
111
|
+
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
@classmethod
|
|
115
|
+
def name_exist(cls, name:str)->bool:
|
|
116
|
+
r"""
|
|
117
|
+
|
|
118
|
+
"""
|
|
119
|
+
query = cls.get_or_none(name=name)
|
|
120
|
+
|
|
121
|
+
if query is not None:
|
|
122
|
+
|
|
123
|
+
return True
|
|
124
|
+
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
def serialize(self)-> dict:
|
|
128
|
+
r"""
|
|
129
|
+
Serialize database record to a jsonable object
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
"id": self.id,
|
|
134
|
+
"name": self.name,
|
|
135
|
+
"manufacturer": self.manufacturer.serialize()
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class Variables(BaseModel):
|
|
140
|
+
|
|
141
|
+
name = CharField(unique=True)
|
|
142
|
+
|
|
143
|
+
@classmethod
|
|
144
|
+
def create(cls, name:str)-> dict:
|
|
145
|
+
r"""
|
|
146
|
+
You can use Model.create() to create a new model instance. This method accepts keyword arguments, where the keys correspond
|
|
147
|
+
to the names of the model's fields. A new instance is returned and a row is added to the table.
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
>>> Variables.create(name='Pressure')
|
|
151
|
+
{
|
|
152
|
+
'message': (str)
|
|
153
|
+
'data': (dict) {
|
|
154
|
+
'name': 'pressure'
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
This will INSERT a new row into the database. The primary key will automatically be retrieved and stored on the model instance.
|
|
160
|
+
|
|
161
|
+
**Parameters**
|
|
162
|
+
|
|
163
|
+
* **name:** (str), Industrial protocol name
|
|
164
|
+
|
|
165
|
+
**Returns**
|
|
166
|
+
|
|
167
|
+
* **result:** (dict) --> {'message': (str), 'data': (dict) row serialized}
|
|
168
|
+
|
|
169
|
+
"""
|
|
170
|
+
result = dict()
|
|
171
|
+
data = dict()
|
|
172
|
+
|
|
173
|
+
if not cls.name_exist(name):
|
|
174
|
+
|
|
175
|
+
query = cls(name=name)
|
|
176
|
+
query.save()
|
|
177
|
+
|
|
178
|
+
message = f"{name} variable created successfully"
|
|
179
|
+
data.update(query.serialize())
|
|
180
|
+
|
|
181
|
+
result.update(
|
|
182
|
+
{
|
|
183
|
+
'message': message,
|
|
184
|
+
'data': data
|
|
185
|
+
}
|
|
186
|
+
)
|
|
187
|
+
return result
|
|
188
|
+
|
|
189
|
+
message = f"{name} variable is already into database"
|
|
190
|
+
result.update(
|
|
191
|
+
{
|
|
192
|
+
'message': message,
|
|
193
|
+
'data': data
|
|
194
|
+
}
|
|
195
|
+
)
|
|
196
|
+
return result
|
|
197
|
+
|
|
198
|
+
@classmethod
|
|
199
|
+
def read_by_name(cls, name:str)->bool:
|
|
200
|
+
r"""
|
|
201
|
+
Get instance by its a name
|
|
202
|
+
|
|
203
|
+
**Parameters**
|
|
204
|
+
|
|
205
|
+
* **name:** (str) Variable name
|
|
206
|
+
|
|
207
|
+
**Returns**
|
|
208
|
+
|
|
209
|
+
* **bool:** If True, name exist into database
|
|
210
|
+
"""
|
|
211
|
+
query = cls.get_or_none(name=name)
|
|
212
|
+
|
|
213
|
+
if query is not None:
|
|
214
|
+
|
|
215
|
+
return query
|
|
216
|
+
|
|
217
|
+
return None
|
|
218
|
+
|
|
219
|
+
@classmethod
|
|
220
|
+
def name_exist(cls, name:str)->bool:
|
|
221
|
+
r"""
|
|
222
|
+
Verify is a name exist into database
|
|
223
|
+
|
|
224
|
+
**Parameters**
|
|
225
|
+
|
|
226
|
+
* **name:** (str) Variable name
|
|
227
|
+
|
|
228
|
+
**Returns**
|
|
229
|
+
|
|
230
|
+
* **bool:** If True, name exist into database
|
|
231
|
+
"""
|
|
232
|
+
query = cls.get_or_none(name=name)
|
|
233
|
+
|
|
234
|
+
if query is not None:
|
|
235
|
+
|
|
236
|
+
return True
|
|
237
|
+
|
|
238
|
+
return False
|
|
239
|
+
|
|
240
|
+
def serialize(self)-> dict:
|
|
241
|
+
r"""
|
|
242
|
+
Serialize database record to a jsonable object
|
|
243
|
+
"""
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
"id": self.id,
|
|
247
|
+
"name": self.name
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
class Units(BaseModel):
|
|
252
|
+
|
|
253
|
+
name = CharField(unique=True)
|
|
254
|
+
unit = CharField(unique=True)
|
|
255
|
+
variable_id = ForeignKeyField(Variables, backref='units', on_delete='CASCADE')
|
|
256
|
+
|
|
257
|
+
@classmethod
|
|
258
|
+
def create(cls, name:str, unit:str, variable:str)-> dict:
|
|
259
|
+
r"""
|
|
260
|
+
You can use Model.create() to create a new model instance. This method accepts keyword arguments, where the keys correspond
|
|
261
|
+
to the names of the model's fields. A new instance is returned and a row is added to the table.
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
>>> Variables.create(name='Pa', variable='Pressure')
|
|
265
|
+
{
|
|
266
|
+
'message': (str)
|
|
267
|
+
'data': (dict) {
|
|
268
|
+
'id': 1,
|
|
269
|
+
'name': 'Pa',
|
|
270
|
+
'variable': 'pressure'
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
This will INSERT a new row into the database. The primary key will automatically be retrieved and stored on the model instance.
|
|
276
|
+
|
|
277
|
+
**Parameters**
|
|
278
|
+
|
|
279
|
+
* **name:** (str), Industrial protocol name
|
|
280
|
+
|
|
281
|
+
**Returns**
|
|
282
|
+
|
|
283
|
+
* **result:** (dict) --> {'message': (str), 'data': (dict) row serialized}
|
|
284
|
+
|
|
285
|
+
"""
|
|
286
|
+
result = dict()
|
|
287
|
+
data = dict()
|
|
288
|
+
name = name
|
|
289
|
+
|
|
290
|
+
if not cls.name_exist(name):
|
|
291
|
+
|
|
292
|
+
query_variable = Variables.read_by_name(variable)
|
|
293
|
+
|
|
294
|
+
if query_variable is not None:
|
|
295
|
+
|
|
296
|
+
variable_id = query_variable
|
|
297
|
+
|
|
298
|
+
query = cls(name=name, unit=unit, variable_id=variable_id)
|
|
299
|
+
query.save()
|
|
300
|
+
|
|
301
|
+
message = f"{name} unit created successfully"
|
|
302
|
+
data.update(query.serialize())
|
|
303
|
+
|
|
304
|
+
result.update(
|
|
305
|
+
{
|
|
306
|
+
'message': message,
|
|
307
|
+
'data': data
|
|
308
|
+
}
|
|
309
|
+
)
|
|
310
|
+
return result
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
message = f"{variable} variable not exist into database"
|
|
314
|
+
|
|
315
|
+
result.update(
|
|
316
|
+
{
|
|
317
|
+
'message': message,
|
|
318
|
+
'data': data
|
|
319
|
+
}
|
|
320
|
+
)
|
|
321
|
+
return result
|
|
322
|
+
|
|
323
|
+
message = f"{name} unit is already into database"
|
|
324
|
+
result.update(
|
|
325
|
+
{
|
|
326
|
+
'message': message,
|
|
327
|
+
'data': data
|
|
328
|
+
}
|
|
329
|
+
)
|
|
330
|
+
return result
|
|
331
|
+
|
|
332
|
+
@classmethod
|
|
333
|
+
def read_by_name(cls, name:str)->bool:
|
|
334
|
+
r"""
|
|
335
|
+
Get instance by its a name
|
|
336
|
+
|
|
337
|
+
**Parameters**
|
|
338
|
+
|
|
339
|
+
* **name:** (str) Variable name
|
|
340
|
+
|
|
341
|
+
**Returns**
|
|
342
|
+
|
|
343
|
+
* **bool:** If True, name exist into database
|
|
344
|
+
"""
|
|
345
|
+
query = cls.get_or_none(name=name)
|
|
346
|
+
|
|
347
|
+
if query is not None:
|
|
348
|
+
|
|
349
|
+
return query.serialize()
|
|
350
|
+
|
|
351
|
+
return None
|
|
352
|
+
|
|
353
|
+
@classmethod
|
|
354
|
+
def read_by_unit(cls, unit:str)->bool:
|
|
355
|
+
r"""
|
|
356
|
+
Get instance by its a name
|
|
357
|
+
|
|
358
|
+
**Parameters**
|
|
359
|
+
|
|
360
|
+
* **name:** (str) Variable name
|
|
361
|
+
|
|
362
|
+
**Returns**
|
|
363
|
+
|
|
364
|
+
* **bool:** If True, name exist into database
|
|
365
|
+
"""
|
|
366
|
+
query = cls.get_or_none(unit=unit)
|
|
367
|
+
|
|
368
|
+
if query is not None:
|
|
369
|
+
|
|
370
|
+
return query
|
|
371
|
+
|
|
372
|
+
return None
|
|
373
|
+
|
|
374
|
+
@classmethod
|
|
375
|
+
def read_by_unit(cls, unit:str)->bool:
|
|
376
|
+
r"""
|
|
377
|
+
Get instance by its a name
|
|
378
|
+
|
|
379
|
+
**Parameters**
|
|
380
|
+
|
|
381
|
+
* **name:** (str) Variable name
|
|
382
|
+
|
|
383
|
+
**Returns**
|
|
384
|
+
|
|
385
|
+
* **bool:** If True, name exist into database
|
|
386
|
+
"""
|
|
387
|
+
query = cls.get_or_none(unit=unit)
|
|
388
|
+
|
|
389
|
+
if query is not None:
|
|
390
|
+
|
|
391
|
+
return query
|
|
392
|
+
|
|
393
|
+
return None
|
|
394
|
+
|
|
395
|
+
@classmethod
|
|
396
|
+
def name_exist(cls, name:str)->bool:
|
|
397
|
+
r"""
|
|
398
|
+
Verify is a name exist into database
|
|
399
|
+
|
|
400
|
+
**Parameters**
|
|
401
|
+
|
|
402
|
+
* **name:** (str) Variable name
|
|
403
|
+
|
|
404
|
+
**Returns**
|
|
405
|
+
|
|
406
|
+
* **bool:** If True, name exist into database
|
|
407
|
+
"""
|
|
408
|
+
query = cls.get_or_none(name=name)
|
|
409
|
+
|
|
410
|
+
if query is not None:
|
|
411
|
+
|
|
412
|
+
return True
|
|
413
|
+
|
|
414
|
+
return False
|
|
415
|
+
|
|
416
|
+
def serialize(self)-> dict:
|
|
417
|
+
r"""
|
|
418
|
+
Serialize database record to a jsonable object
|
|
419
|
+
"""
|
|
420
|
+
|
|
421
|
+
return {
|
|
422
|
+
"id": self.id,
|
|
423
|
+
"name": self.name,
|
|
424
|
+
"variable": self.variable_id.name,
|
|
425
|
+
"unit": self.unit
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
class DataTypes(BaseModel):
|
|
430
|
+
|
|
431
|
+
name = CharField(unique=True)
|
|
432
|
+
|
|
433
|
+
@classmethod
|
|
434
|
+
def create(cls, name:str)-> dict:
|
|
435
|
+
r"""
|
|
436
|
+
You can use Model.create() to create a new model instance. This method accepts keyword arguments, where the keys correspond
|
|
437
|
+
to the names of the model's fields. A new instance is returned and a row is added to the table.
|
|
438
|
+
|
|
439
|
+
```python
|
|
440
|
+
>>> Variables.create(name='Pressure')
|
|
441
|
+
{
|
|
442
|
+
'message': (str)
|
|
443
|
+
'data': (dict) {
|
|
444
|
+
'name': 'pressure'
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
This will INSERT a new row into the database. The primary key will automatically be retrieved and stored on the model instance.
|
|
450
|
+
|
|
451
|
+
**Parameters**
|
|
452
|
+
|
|
453
|
+
* **name:** (str), Industrial protocol name
|
|
454
|
+
|
|
455
|
+
**Returns**
|
|
456
|
+
|
|
457
|
+
* **result:** (dict) --> {'message': (str), 'data': (dict) row serialized}
|
|
458
|
+
|
|
459
|
+
"""
|
|
460
|
+
result = dict()
|
|
461
|
+
data = dict()
|
|
462
|
+
|
|
463
|
+
if not cls.name_exist(name):
|
|
464
|
+
|
|
465
|
+
query = cls(name=name)
|
|
466
|
+
query.save()
|
|
467
|
+
|
|
468
|
+
message = f"{name} DataType created successfully"
|
|
469
|
+
data.update(query.serialize())
|
|
470
|
+
|
|
471
|
+
result.update(
|
|
472
|
+
{
|
|
473
|
+
'message': message,
|
|
474
|
+
'data': data
|
|
475
|
+
}
|
|
476
|
+
)
|
|
477
|
+
return result
|
|
478
|
+
|
|
479
|
+
message = f"{name} DataType is already into database"
|
|
480
|
+
result.update(
|
|
481
|
+
{
|
|
482
|
+
'message': message,
|
|
483
|
+
'data': data
|
|
484
|
+
}
|
|
485
|
+
)
|
|
486
|
+
return result
|
|
487
|
+
|
|
488
|
+
@classmethod
|
|
489
|
+
def read_by_name(cls, name:str)->bool:
|
|
490
|
+
r"""
|
|
491
|
+
Get instance by its a name
|
|
492
|
+
|
|
493
|
+
**Parameters**
|
|
494
|
+
|
|
495
|
+
* **name:** (str) Variable name
|
|
496
|
+
|
|
497
|
+
**Returns**
|
|
498
|
+
|
|
499
|
+
* **bool:** If True, name exist into database
|
|
500
|
+
"""
|
|
501
|
+
query = cls.get_or_none(name=name)
|
|
502
|
+
|
|
503
|
+
if query is not None:
|
|
504
|
+
|
|
505
|
+
return query
|
|
506
|
+
|
|
507
|
+
return None
|
|
508
|
+
|
|
509
|
+
@classmethod
|
|
510
|
+
def name_exist(cls, name:str)->bool:
|
|
511
|
+
r"""
|
|
512
|
+
Verify is a name exist into database
|
|
513
|
+
|
|
514
|
+
**Parameters**
|
|
515
|
+
|
|
516
|
+
* **name:** (str) Variable name
|
|
517
|
+
|
|
518
|
+
**Returns**
|
|
519
|
+
|
|
520
|
+
* **bool:** If True, name exist into database
|
|
521
|
+
"""
|
|
522
|
+
query = cls.get_or_none(name=name)
|
|
523
|
+
|
|
524
|
+
if query is not None:
|
|
525
|
+
|
|
526
|
+
return True
|
|
527
|
+
|
|
528
|
+
return False
|
|
529
|
+
|
|
530
|
+
def serialize(self)-> dict:
|
|
531
|
+
r"""
|
|
532
|
+
Serialize database record to a jsonable object
|
|
533
|
+
"""
|
|
534
|
+
|
|
535
|
+
return {
|
|
536
|
+
"id": self.id,
|
|
537
|
+
"name": self.name
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
class Tags(BaseModel):
|
|
542
|
+
|
|
543
|
+
identifier = CharField(unique=True)
|
|
544
|
+
name = CharField(unique=True)
|
|
545
|
+
unit = ForeignKeyField(Units, backref='tags')
|
|
546
|
+
data_type = ForeignKeyField(DataTypes, backref='tags')
|
|
547
|
+
segment = ForeignKeyField(Segment, backref='tags', null=True)
|
|
548
|
+
description = CharField(null=True, max_length=256)
|
|
549
|
+
display_name = CharField(unique=True)
|
|
550
|
+
display_unit = ForeignKeyField(Units)
|
|
551
|
+
opcua_address = CharField(null=True)
|
|
552
|
+
node_namespace = CharField(null=True)
|
|
553
|
+
scan_time = IntegerField(null=True)
|
|
554
|
+
dead_band = FloatField(null=True)
|
|
555
|
+
active = BooleanField(default=True)
|
|
556
|
+
process_filter = BooleanField(default=False)
|
|
557
|
+
gaussian_filter = BooleanField(default=False)
|
|
558
|
+
gaussian_filter_threshold = FloatField(default=1.0)
|
|
559
|
+
gaussian_filter_r_value = FloatField(default=0.0)
|
|
560
|
+
out_of_range_detection = BooleanField(default=False)
|
|
561
|
+
outlier_detection = BooleanField(default=False)
|
|
562
|
+
frozen_data_detection = BooleanField(default=False)
|
|
563
|
+
|
|
564
|
+
@classmethod
|
|
565
|
+
def create(
|
|
566
|
+
cls,
|
|
567
|
+
id:str,
|
|
568
|
+
name:str,
|
|
569
|
+
unit:str,
|
|
570
|
+
data_type:str,
|
|
571
|
+
description:str,
|
|
572
|
+
display_name:str,
|
|
573
|
+
display_unit:str,
|
|
574
|
+
opcua_address:str="",
|
|
575
|
+
node_namespace:str="",
|
|
576
|
+
segment:str="",
|
|
577
|
+
manufacturer:str="",
|
|
578
|
+
scan_time:int=0,
|
|
579
|
+
dead_band:float=0.0,
|
|
580
|
+
active:bool=True,
|
|
581
|
+
process_filter:bool=False,
|
|
582
|
+
gaussian_filter:bool=False,
|
|
583
|
+
gaussian_filter_threshold:float=1.0,
|
|
584
|
+
gaussian_filter_r_value:float=0.0,
|
|
585
|
+
out_of_range_detection:bool=False,
|
|
586
|
+
outlier_detection:bool=False,
|
|
587
|
+
frozen_data_detection:bool=False
|
|
588
|
+
):
|
|
589
|
+
r"""
|
|
590
|
+
Documentation here
|
|
591
|
+
"""
|
|
592
|
+
result = dict()
|
|
593
|
+
message = f"{name} already exist into database"
|
|
594
|
+
data = dict()
|
|
595
|
+
_unit = Units.read_by_unit(unit=unit)
|
|
596
|
+
_display_unit = Units.read_by_unit(unit=display_unit)
|
|
597
|
+
_data_type = DataTypes.read_by_name(name=data_type)
|
|
598
|
+
|
|
599
|
+
if not cls.name_exist(name):
|
|
600
|
+
|
|
601
|
+
if not cls.display_name_exist(name):
|
|
602
|
+
|
|
603
|
+
if _unit is not None and _display_unit is not None:
|
|
604
|
+
|
|
605
|
+
if _data_type is not None:
|
|
606
|
+
|
|
607
|
+
if segment and manufacturer:
|
|
608
|
+
|
|
609
|
+
segment_obj = Segment.create(name=segment, manufacturer=manufacturer)
|
|
610
|
+
|
|
611
|
+
if not segment_obj:
|
|
612
|
+
|
|
613
|
+
result.update(
|
|
614
|
+
{
|
|
615
|
+
'message': f"Duplicated {manufacturer}->{segment}",
|
|
616
|
+
'data': data
|
|
617
|
+
}
|
|
618
|
+
)
|
|
619
|
+
|
|
620
|
+
return result
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
query = cls(
|
|
624
|
+
identifier=id,
|
|
625
|
+
name=name,
|
|
626
|
+
unit=_unit,
|
|
627
|
+
data_type=_data_type,
|
|
628
|
+
description=description,
|
|
629
|
+
display_name=display_name,
|
|
630
|
+
display_unit=_display_unit,
|
|
631
|
+
opcua_address=opcua_address,
|
|
632
|
+
node_namespace=node_namespace,
|
|
633
|
+
scan_time=scan_time,
|
|
634
|
+
dead_band=dead_band,
|
|
635
|
+
active=active,
|
|
636
|
+
process_filter=process_filter,
|
|
637
|
+
gaussian_filter=gaussian_filter,
|
|
638
|
+
gaussian_filter_threshold=gaussian_filter_threshold,
|
|
639
|
+
gaussian_filter_r_value=gaussian_filter_r_value,
|
|
640
|
+
out_of_range_detection=out_of_range_detection,
|
|
641
|
+
outlier_detection=outlier_detection,
|
|
642
|
+
frozen_data_detection=frozen_data_detection,
|
|
643
|
+
segment=segment_obj
|
|
644
|
+
)
|
|
645
|
+
else:
|
|
646
|
+
query = cls(
|
|
647
|
+
identifier=id,
|
|
648
|
+
name=name,
|
|
649
|
+
unit=_unit,
|
|
650
|
+
data_type=_data_type,
|
|
651
|
+
description=description,
|
|
652
|
+
display_name=display_name,
|
|
653
|
+
display_unit=_display_unit,
|
|
654
|
+
opcua_address=opcua_address,
|
|
655
|
+
node_namespace=node_namespace,
|
|
656
|
+
scan_time=scan_time,
|
|
657
|
+
dead_band=dead_band,
|
|
658
|
+
active=active,
|
|
659
|
+
process_filter=process_filter,
|
|
660
|
+
gaussian_filter=gaussian_filter,
|
|
661
|
+
gaussian_filter_threshold=gaussian_filter_threshold,
|
|
662
|
+
gaussian_filter_r_value=gaussian_filter_r_value,
|
|
663
|
+
out_of_range_detection=out_of_range_detection,
|
|
664
|
+
outlier_detection=outlier_detection,
|
|
665
|
+
frozen_data_detection=frozen_data_detection
|
|
666
|
+
)
|
|
667
|
+
query.save()
|
|
668
|
+
message = f"{name} tag created successfully"
|
|
669
|
+
|
|
670
|
+
data.update(query.serialize())
|
|
671
|
+
|
|
672
|
+
result.update(
|
|
673
|
+
{
|
|
674
|
+
'message': message,
|
|
675
|
+
'data': data
|
|
676
|
+
}
|
|
677
|
+
)
|
|
678
|
+
|
|
679
|
+
return result
|
|
680
|
+
|
|
681
|
+
message = f"{data_type} data type not exist into database"
|
|
682
|
+
result.update(
|
|
683
|
+
{
|
|
684
|
+
'message': message,
|
|
685
|
+
'data': data
|
|
686
|
+
}
|
|
687
|
+
)
|
|
688
|
+
return result
|
|
689
|
+
|
|
690
|
+
message = f"{unit} unit not exist into database"
|
|
691
|
+
result.update(
|
|
692
|
+
{
|
|
693
|
+
'message': message,
|
|
694
|
+
'data': data
|
|
695
|
+
}
|
|
696
|
+
)
|
|
697
|
+
return result
|
|
698
|
+
|
|
699
|
+
else:
|
|
700
|
+
|
|
701
|
+
if _unit is not None and _display_unit is not None:
|
|
702
|
+
|
|
703
|
+
if _data_type is not None:
|
|
704
|
+
tag, _ = cls.get_or_create(name=name)
|
|
705
|
+
payload = {
|
|
706
|
+
"unit":_unit,
|
|
707
|
+
"data_type":_data_type,
|
|
708
|
+
"description":description,
|
|
709
|
+
"display_name":display_name,
|
|
710
|
+
"display_unit":_display_unit,
|
|
711
|
+
"opcua_address":opcua_address,
|
|
712
|
+
"node_namespace":node_namespace,
|
|
713
|
+
"scan_time":scan_time,
|
|
714
|
+
"dead_band":dead_band,
|
|
715
|
+
"active": True
|
|
716
|
+
}
|
|
717
|
+
cls.put(id=tag.id, **payload)
|
|
718
|
+
|
|
719
|
+
message = f"{data_type} data type not exist into database"
|
|
720
|
+
result.update(
|
|
721
|
+
{
|
|
722
|
+
'message': message,
|
|
723
|
+
'data': data
|
|
724
|
+
}
|
|
725
|
+
)
|
|
726
|
+
return result
|
|
727
|
+
|
|
728
|
+
message = f"{unit} unit not exist into database"
|
|
729
|
+
result.update(
|
|
730
|
+
{
|
|
731
|
+
'message': message,
|
|
732
|
+
'data': data
|
|
733
|
+
}
|
|
734
|
+
)
|
|
735
|
+
return result
|
|
736
|
+
|
|
737
|
+
result.update(
|
|
738
|
+
{
|
|
739
|
+
'message': message,
|
|
740
|
+
'data': data
|
|
741
|
+
}
|
|
742
|
+
)
|
|
743
|
+
return result
|
|
744
|
+
|
|
745
|
+
@classmethod
|
|
746
|
+
def put(cls, id:int, **fields)-> dict:
|
|
747
|
+
r""""
|
|
748
|
+
Update a single record
|
|
749
|
+
|
|
750
|
+
Once a model instance has a primary key, you UPDATE a field by its id.
|
|
751
|
+
The model's primary key will not change:
|
|
752
|
+
"""
|
|
753
|
+
|
|
754
|
+
if cls.id_exists(id):
|
|
755
|
+
|
|
756
|
+
if "unit" in fields:
|
|
757
|
+
|
|
758
|
+
unit = fields["unit"]
|
|
759
|
+
if isinstance(unit, str):
|
|
760
|
+
query = Units.read_by_unit(unit=unit)
|
|
761
|
+
if query:
|
|
762
|
+
|
|
763
|
+
fields["unit"] = query
|
|
764
|
+
|
|
765
|
+
if "display_unit" in fields:
|
|
766
|
+
|
|
767
|
+
display_unit = fields["display_unit"]
|
|
768
|
+
if isinstance(display_unit, str):
|
|
769
|
+
query = Units.read_by_unit(unit=display_unit)
|
|
770
|
+
if query:
|
|
771
|
+
|
|
772
|
+
fields["display_unit"] = query
|
|
773
|
+
|
|
774
|
+
if "data_type" in fields:
|
|
775
|
+
|
|
776
|
+
data_type = fields["data_type"]
|
|
777
|
+
if isinstance(data_type, str):
|
|
778
|
+
query = DataTypes.read_by_name(name=data_type)
|
|
779
|
+
if query:
|
|
780
|
+
|
|
781
|
+
fields["data_type"] = query
|
|
782
|
+
|
|
783
|
+
if "segment" in fields:
|
|
784
|
+
|
|
785
|
+
if "manufacturer" in fields:
|
|
786
|
+
|
|
787
|
+
segment = fields["segment"]
|
|
788
|
+
manufacturer = fields.pop("manufacturer")
|
|
789
|
+
if isinstance(segment, str) and isinstance(manufacturer, str):
|
|
790
|
+
manufacturer_obj = Manufacturer.get_or_none(name=manufacturer)
|
|
791
|
+
if manufacturer_obj:
|
|
792
|
+
query = Segment.get_or_none((Segment.name == segment) & (Segment.manufacturer == manufacturer_obj))
|
|
793
|
+
|
|
794
|
+
if query:
|
|
795
|
+
|
|
796
|
+
fields["segment"] = query
|
|
797
|
+
|
|
798
|
+
query = cls.update(**fields).where(cls.id == id)
|
|
799
|
+
query.execute()
|
|
800
|
+
return query
|
|
801
|
+
|
|
802
|
+
@classmethod
|
|
803
|
+
def read_by_name(cls, name):
|
|
804
|
+
r"""
|
|
805
|
+
Documentation here
|
|
806
|
+
"""
|
|
807
|
+
return cls.get_or_none(name=name)
|
|
808
|
+
|
|
809
|
+
@classmethod
|
|
810
|
+
def read_by_names(cls, names):
|
|
811
|
+
r"""
|
|
812
|
+
Documentation here
|
|
813
|
+
"""
|
|
814
|
+
query = cls.select().where(cls.name in names)
|
|
815
|
+
return query
|
|
816
|
+
|
|
817
|
+
@classmethod
|
|
818
|
+
def name_exist(cls, name):
|
|
819
|
+
r"""
|
|
820
|
+
Documentation here
|
|
821
|
+
"""
|
|
822
|
+
tag = cls.get_or_none(name=name)
|
|
823
|
+
if tag is not None:
|
|
824
|
+
|
|
825
|
+
return True
|
|
826
|
+
|
|
827
|
+
return False
|
|
828
|
+
|
|
829
|
+
@classmethod
|
|
830
|
+
def display_name_exist(cls, name):
|
|
831
|
+
r"""
|
|
832
|
+
Documentation here
|
|
833
|
+
"""
|
|
834
|
+
tag = cls.get_or_none(name=name)
|
|
835
|
+
if tag is not None:
|
|
836
|
+
|
|
837
|
+
return True
|
|
838
|
+
|
|
839
|
+
return False
|
|
840
|
+
|
|
841
|
+
def get_machines(self):
|
|
842
|
+
|
|
843
|
+
return self.machines
|
|
844
|
+
|
|
845
|
+
def serialize(self):
|
|
846
|
+
r"""
|
|
847
|
+
Documentation here
|
|
848
|
+
"""
|
|
849
|
+
segment = ""
|
|
850
|
+
manufacturer = ""
|
|
851
|
+
if self.segment:
|
|
852
|
+
|
|
853
|
+
segment = self.segment.serialize()
|
|
854
|
+
manufacturer = segment["manufacturer"]["name"]
|
|
855
|
+
segment = segment["name"]
|
|
856
|
+
|
|
857
|
+
gaussian_filter_r_value = 0
|
|
858
|
+
if hasattr(self, "gaussian_filter_r_value"):
|
|
859
|
+
|
|
860
|
+
gaussian_filter_r_value = self.gaussian_filter_r_value
|
|
861
|
+
|
|
862
|
+
gaussian_filter_threshold = 0
|
|
863
|
+
if hasattr(self, "gaussian_filter_threshold"):
|
|
864
|
+
|
|
865
|
+
gaussian_filter_threshold = self.gaussian_filter_threshold
|
|
866
|
+
|
|
867
|
+
return {
|
|
868
|
+
'id': self.identifier,
|
|
869
|
+
'name': self.name,
|
|
870
|
+
'unit': self.unit.unit,
|
|
871
|
+
'data_type': self.data_type.name,
|
|
872
|
+
'description': self.description,
|
|
873
|
+
'display_name': self.display_name,
|
|
874
|
+
'display_unit': self.display_unit.unit,
|
|
875
|
+
'opcua_address': self.opcua_address,
|
|
876
|
+
'node_namespace': self.node_namespace,
|
|
877
|
+
'scan_time': self.scan_time,
|
|
878
|
+
'dead_band': self.dead_band,
|
|
879
|
+
'variable': self.unit.variable_id.name,
|
|
880
|
+
'active': self.active,
|
|
881
|
+
'process_filter': self.process_filter,
|
|
882
|
+
'gaussian_filter': self.gaussian_filter,
|
|
883
|
+
'gaussian_filter_threshold': gaussian_filter_threshold,
|
|
884
|
+
'gaussian_filter_r_value': gaussian_filter_r_value,
|
|
885
|
+
'out_of_range_detection': self.out_of_range_detection,
|
|
886
|
+
'frozen_data_detection': self.frozen_data_detection,
|
|
887
|
+
'outlier_detection': self.outlier_detection,
|
|
888
|
+
'segment': segment,
|
|
889
|
+
"manufacturer": manufacturer
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
|
|
893
|
+
class TagValue(BaseModel):
|
|
894
|
+
|
|
895
|
+
tag = ForeignKeyField(Tags, backref='values')
|
|
896
|
+
unit = ForeignKeyField(Units, backref='values')
|
|
897
|
+
value = FloatField()
|
|
898
|
+
timestamp = TimestampField(utc=True)
|
|
899
|
+
|
|
900
|
+
class Meta:
|
|
901
|
+
indexes = (
|
|
902
|
+
(('timestamp',), False),
|
|
903
|
+
)
|
|
904
|
+
|
|
905
|
+
@classmethod
|
|
906
|
+
def create(
|
|
907
|
+
cls,
|
|
908
|
+
tag:Tags,
|
|
909
|
+
value:float,
|
|
910
|
+
timestamp:datetime,
|
|
911
|
+
unit=Units):
|
|
912
|
+
r"""
|
|
913
|
+
Documentation here
|
|
914
|
+
"""
|
|
915
|
+
query = cls(
|
|
916
|
+
tag=tag,
|
|
917
|
+
value=value,
|
|
918
|
+
timestamp=timestamp,
|
|
919
|
+
unit=unit
|
|
920
|
+
)
|
|
921
|
+
query.save()
|