pymscada 0.2.1__py3-none-any.whl → 0.2.2__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.
Potentially problematic release.
This version of pymscada might be problematic. Click here for more details.
- pymscada/__init__.py +6 -2
- pymscada/alarms.py +6 -2
- pymscada/callout.py +127 -74
- pymscada/config.py +20 -1
- pymscada/demo/__pycache__/__init__.cpython-311.pyc +0 -0
- pymscada/demo/callout.yaml +13 -4
- pymscada/demo/openweather.yaml +1 -1
- pymscada/demo/piapi.yaml +15 -0
- pymscada/demo/pymscada-io-piapi.service +15 -0
- pymscada/demo/pymscada-io-sms.service +18 -0
- pymscada/demo/sms.yaml +11 -0
- pymscada/demo/tags.yaml +3 -0
- pymscada/demo/witsapi.yaml +2 -2
- pymscada/demo/wwwserver.yaml +15 -0
- pymscada/history.py +3 -5
- pymscada/iodrivers/piapi.py +133 -0
- pymscada/iodrivers/sms.py +212 -0
- pymscada/iodrivers/witsapi.py +26 -35
- pymscada/module_config.py +24 -18
- pymscada/opnotes.py +4 -2
- pymscada/pdf/__pycache__/__init__.cpython-311.pyc +0 -0
- pymscada/tools/get_history.py +147 -0
- pymscada/www_server.py +1 -1
- {pymscada-0.2.1.dist-info → pymscada-0.2.2.dist-info}/METADATA +1 -1
- {pymscada-0.2.1.dist-info → pymscada-0.2.2.dist-info}/RECORD +29 -23
- pymscada/validate.py +0 -451
- {pymscada-0.2.1.dist-info → pymscada-0.2.2.dist-info}/WHEEL +0 -0
- {pymscada-0.2.1.dist-info → pymscada-0.2.2.dist-info}/entry_points.txt +0 -0
- {pymscada-0.2.1.dist-info → pymscada-0.2.2.dist-info}/licenses/LICENSE +0 -0
- {pymscada-0.2.1.dist-info → pymscada-0.2.2.dist-info}/top_level.txt +0 -0
pymscada/validate.py
DELETED
|
@@ -1,451 +0,0 @@
|
|
|
1
|
-
"""Config file validation."""
|
|
2
|
-
from cerberus import Validator
|
|
3
|
-
import logging
|
|
4
|
-
from yaml import dump
|
|
5
|
-
from pymscada import Config
|
|
6
|
-
from socket import inet_aton
|
|
7
|
-
|
|
8
|
-
INT_TAG = {
|
|
9
|
-
'desc': {'type': 'string'},
|
|
10
|
-
'type': {'type': 'string', 'allowed': ['int']},
|
|
11
|
-
'min': {'type': 'integer', 'required': False},
|
|
12
|
-
'max': {'type': 'integer', 'required': False},
|
|
13
|
-
'init': {'type': 'integer', 'required': False},
|
|
14
|
-
'units': {'type': 'string', 'maxlength': 5, 'required': False},
|
|
15
|
-
'format': {'type': 'string', 'allowed': ['date', 'time', 'datetime']}
|
|
16
|
-
}
|
|
17
|
-
FLOAT_TAG = {
|
|
18
|
-
'desc': {'type': 'string'},
|
|
19
|
-
'type': {'type': 'string', 'allowed': ['float']},
|
|
20
|
-
'min': {'type': 'float', 'required': False},
|
|
21
|
-
'max': {'type': 'float', 'required': False},
|
|
22
|
-
'init': {'type': 'float', 'required': False},
|
|
23
|
-
'units': {'type': 'string', 'maxlength': 5, 'required': False},
|
|
24
|
-
'dp': {'type': 'integer', 'min': 0, 'max': 6, 'required': False},
|
|
25
|
-
}
|
|
26
|
-
STR_TAG = {
|
|
27
|
-
'desc': {'type': 'string'},
|
|
28
|
-
'type': {'type': 'string', 'allowed': ['str']},
|
|
29
|
-
'init': {'type': 'string', 'required': False},
|
|
30
|
-
}
|
|
31
|
-
LIST_TAG = {
|
|
32
|
-
'desc': {'type': 'string'},
|
|
33
|
-
'type': {'type': 'string', 'allowed': ['list']},
|
|
34
|
-
}
|
|
35
|
-
DICT_TAG = {
|
|
36
|
-
'desc': {'type': 'string'},
|
|
37
|
-
'type': {'type': 'string', 'allowed': ['dict']},
|
|
38
|
-
'init': {}
|
|
39
|
-
}
|
|
40
|
-
MULTI_TAG = {
|
|
41
|
-
'desc': {'type': 'string'},
|
|
42
|
-
'multi': {'type': 'list'},
|
|
43
|
-
'init': {'type': 'integer', 'required': False},
|
|
44
|
-
}
|
|
45
|
-
BYTES_TAG = {
|
|
46
|
-
'desc': {'type': 'string'},
|
|
47
|
-
'type': {'type': 'string', 'allowed': ['bytes']},
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
TAG_SCHEMA = {
|
|
51
|
-
'type': 'dict',
|
|
52
|
-
'keysrules': {
|
|
53
|
-
'type': 'string',
|
|
54
|
-
# tag name discovery, save for later checking
|
|
55
|
-
'ms_tagname': 'save'
|
|
56
|
-
},
|
|
57
|
-
'valuesrules': {
|
|
58
|
-
'type': 'dict',
|
|
59
|
-
'oneof_schema': [
|
|
60
|
-
INT_TAG, FLOAT_TAG, STR_TAG, LIST_TAG, DICT_TAG, MULTI_TAG,
|
|
61
|
-
BYTES_TAG
|
|
62
|
-
],
|
|
63
|
-
# tag type discovery, save for later checking
|
|
64
|
-
'ms_tagtype': True
|
|
65
|
-
},
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
BUS_SCHEMA = {
|
|
69
|
-
'type': 'dict',
|
|
70
|
-
'schema': {
|
|
71
|
-
'ip': {'type': 'string', 'ms_ip': 'ipv4 none'},
|
|
72
|
-
'port': {'type': 'integer', 'min': 1024, 'max': 65536}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
BRHR_LIST = {
|
|
77
|
-
'type': {'type': 'string', 'allowed': ['br', 'hr']},
|
|
78
|
-
}
|
|
79
|
-
H123P_LIST = {
|
|
80
|
-
'type': {
|
|
81
|
-
'type': 'string',
|
|
82
|
-
'allowed': ['h1', 'h2', 'h3', 'p'],
|
|
83
|
-
},
|
|
84
|
-
'desc': {'type': 'string'}
|
|
85
|
-
}
|
|
86
|
-
VALUESETFILES_LIST = {
|
|
87
|
-
'type': {
|
|
88
|
-
'type': 'string',
|
|
89
|
-
'allowed': ['value', 'setpoint', 'files'],
|
|
90
|
-
},
|
|
91
|
-
# tagname must have been found in parsing tags.yaml
|
|
92
|
-
'tagname': {'type': 'string', 'ms_tagname': 'exists'}
|
|
93
|
-
}
|
|
94
|
-
SELECTDICT_LIST = {
|
|
95
|
-
'type': {'type': 'string', 'allowed': ['selectdict']},
|
|
96
|
-
# tagname must have been found in parsing tags.yaml
|
|
97
|
-
'tagname': {'type': 'string', 'ms_tagname': 'exists'},
|
|
98
|
-
'opts': {
|
|
99
|
-
'type': 'dict',
|
|
100
|
-
# 'schema': {
|
|
101
|
-
# 'type': 'dict',
|
|
102
|
-
# # 'schema': {
|
|
103
|
-
# # 'type': {'type': 'string', 'required': False},
|
|
104
|
-
# # 'multi': {'type': 'list', 'required': False},
|
|
105
|
-
# # }
|
|
106
|
-
# },
|
|
107
|
-
'required': False
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
OPNOTE_LIST = {
|
|
111
|
-
'type': {'type': 'string', 'allowed': ['opnote']},
|
|
112
|
-
'site': {'type': 'list'},
|
|
113
|
-
'by': {'type': 'list'}
|
|
114
|
-
}
|
|
115
|
-
UPLOT_LIST = {
|
|
116
|
-
'type': {'type': 'string', 'allowed': ['uplot']},
|
|
117
|
-
'ms': {
|
|
118
|
-
'type': 'dict',
|
|
119
|
-
'required': False
|
|
120
|
-
},
|
|
121
|
-
'axes': {
|
|
122
|
-
'type': 'list',
|
|
123
|
-
'schema': {
|
|
124
|
-
'type': 'dict',
|
|
125
|
-
'schema': {
|
|
126
|
-
'scale': {'type': 'string'},
|
|
127
|
-
'range': {'type': 'list', 'items': [
|
|
128
|
-
{'type': 'float'}, {'type': 'float'}
|
|
129
|
-
]},
|
|
130
|
-
'dp': {'type': 'integer', 'required': False}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
},
|
|
134
|
-
'series': {
|
|
135
|
-
'type': 'list',
|
|
136
|
-
'schema': {
|
|
137
|
-
'type': 'dict',
|
|
138
|
-
'schema': {
|
|
139
|
-
# tagname must have been found in parsing tags.yaml
|
|
140
|
-
'tagname': {'type': 'string', 'ms_tagname': 'exists'},
|
|
141
|
-
'label': {'type': 'string', 'required': False},
|
|
142
|
-
'scale': {'type': 'string', 'required': False},
|
|
143
|
-
'color': {'type': 'string', 'required': False},
|
|
144
|
-
'width': {'type': 'float', 'required': False},
|
|
145
|
-
'dp': {'type': 'integer', 'required': False},
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
LIST_WWWSERVER = {
|
|
152
|
-
'type': 'dict',
|
|
153
|
-
'schema': {
|
|
154
|
-
'name': {'type': 'string'},
|
|
155
|
-
'parent': {'type': 'string', 'nullable': True},
|
|
156
|
-
'items': {
|
|
157
|
-
'type': 'list',
|
|
158
|
-
'schema': {
|
|
159
|
-
'type': 'dict',
|
|
160
|
-
'oneof_schema': [
|
|
161
|
-
BRHR_LIST, H123P_LIST, VALUESETFILES_LIST,
|
|
162
|
-
SELECTDICT_LIST, OPNOTE_LIST, UPLOT_LIST
|
|
163
|
-
]
|
|
164
|
-
}
|
|
165
|
-
},
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
WWWSERVER_SCHEMA = {
|
|
170
|
-
'type': 'dict',
|
|
171
|
-
'schema': {
|
|
172
|
-
'bus_ip': {'type': 'string', 'ms_ip': 'none ipv4'},
|
|
173
|
-
'bus_port': {'type': 'integer', 'min': 1024, 'max': 65536},
|
|
174
|
-
'ip': {'type': 'string', 'ms_ip': 'none ipv4'},
|
|
175
|
-
'port': {'type': 'integer', 'min': 1024, 'max': 65536},
|
|
176
|
-
'get_path': {'nullable': True},
|
|
177
|
-
'serve_path': {'nullable': True},
|
|
178
|
-
'paths': {'type': 'list', 'allowed': ['history', 'config', 'pdf']},
|
|
179
|
-
'pages': {
|
|
180
|
-
'type': 'list',
|
|
181
|
-
'schema': LIST_WWWSERVER
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
HISTORY_SCHEMA = {
|
|
187
|
-
'type': 'dict',
|
|
188
|
-
'schema': {
|
|
189
|
-
'bus_ip': {'type': 'string', 'ms_ip': 'none ipv4'},
|
|
190
|
-
'bus_port': {'type': 'integer', 'min': 1024, 'max': 65536},
|
|
191
|
-
'path': {'type': 'string'},
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
MODBUSSERVER_SCHEMA = {
|
|
196
|
-
'type': 'dict',
|
|
197
|
-
'schema': {
|
|
198
|
-
'bus_ip': {'type': 'string', 'ms_ip': 'none ipv4', 'nullable': True},
|
|
199
|
-
'bus_port': {'type': 'integer', 'min': 1024, 'max': 65536,
|
|
200
|
-
'nullable': True},
|
|
201
|
-
'path': {'type': 'string'},
|
|
202
|
-
'rtus': {
|
|
203
|
-
'type': 'list',
|
|
204
|
-
'schema': {
|
|
205
|
-
'type': 'dict',
|
|
206
|
-
'schema': {
|
|
207
|
-
'name': {},
|
|
208
|
-
'ip': {},
|
|
209
|
-
'port': {},
|
|
210
|
-
'tcp_udp': {'type': 'string', 'allowed': ['tcp', 'udp']},
|
|
211
|
-
'serve': {
|
|
212
|
-
'type': 'list',
|
|
213
|
-
'schema': {}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
},
|
|
218
|
-
'tags': {
|
|
219
|
-
'type': 'dict',
|
|
220
|
-
'keysrules': {
|
|
221
|
-
'type': 'string',
|
|
222
|
-
'ms_tagname': 'none'
|
|
223
|
-
},
|
|
224
|
-
'valuesrules': {
|
|
225
|
-
'type': 'dict',
|
|
226
|
-
'schema': {
|
|
227
|
-
'type': {},
|
|
228
|
-
'addr': {}
|
|
229
|
-
},
|
|
230
|
-
},
|
|
231
|
-
},
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
MODBUSCLIENT_SCHEMA = {
|
|
236
|
-
'type': 'dict',
|
|
237
|
-
'schema': {
|
|
238
|
-
'bus_ip': {'type': 'string', 'ms_ip': 'ipv4'},
|
|
239
|
-
'bus_port': {'type': 'integer', 'min': 1024, 'max': 65536,
|
|
240
|
-
'nullable': True},
|
|
241
|
-
'path': {'type': 'string'},
|
|
242
|
-
'rtus': {
|
|
243
|
-
'type': 'list',
|
|
244
|
-
'schema': {
|
|
245
|
-
'type': 'dict',
|
|
246
|
-
'schema': {
|
|
247
|
-
'name': {},
|
|
248
|
-
'ip': {},
|
|
249
|
-
'port': {},
|
|
250
|
-
'tcp_udp': {'type': 'string', 'allowed': ['tcp', 'udp']},
|
|
251
|
-
'rate': {},
|
|
252
|
-
'poll': {
|
|
253
|
-
'type': 'list',
|
|
254
|
-
'schema': {}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
},
|
|
259
|
-
'tags': {
|
|
260
|
-
'type': 'dict',
|
|
261
|
-
'keysrules': {
|
|
262
|
-
'type': 'string',
|
|
263
|
-
'ms_tagname': 'exists'
|
|
264
|
-
},
|
|
265
|
-
'valuesrules': {
|
|
266
|
-
'type': 'dict',
|
|
267
|
-
'schema': {
|
|
268
|
-
'type': {},
|
|
269
|
-
'read': {},
|
|
270
|
-
'write': {}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
SNMPCLIENT_SCHEMA = {
|
|
278
|
-
'type': 'dict',
|
|
279
|
-
'schema': {
|
|
280
|
-
'bus_ip': {'type': 'string', 'ms_ip': 'ipv4'},
|
|
281
|
-
'bus_port': {'type': 'integer', 'min': 1024, 'max': 65536,
|
|
282
|
-
'nullable': True},
|
|
283
|
-
'path': {'type': 'string'},
|
|
284
|
-
'rtus': {
|
|
285
|
-
'type': 'list',
|
|
286
|
-
'schema': {
|
|
287
|
-
'type': 'dict',
|
|
288
|
-
'schema': {
|
|
289
|
-
'name': {},
|
|
290
|
-
'ip': {},
|
|
291
|
-
'community': {},
|
|
292
|
-
'rate': {},
|
|
293
|
-
'poll': {
|
|
294
|
-
'type': 'list',
|
|
295
|
-
'schema': {}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
},
|
|
300
|
-
'tags': {
|
|
301
|
-
'type': 'dict',
|
|
302
|
-
'keysrules': {
|
|
303
|
-
'type': 'string',
|
|
304
|
-
'ms_tagname': 'exists'
|
|
305
|
-
},
|
|
306
|
-
'valuesrules': {
|
|
307
|
-
'type': 'dict',
|
|
308
|
-
'schema': {
|
|
309
|
-
'type': {},
|
|
310
|
-
'read': {}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
LOGIXCLIENT_SCHEMA = {
|
|
318
|
-
'type': 'dict',
|
|
319
|
-
'schema': {
|
|
320
|
-
'bus_ip': {'type': 'string', 'ms_ip': 'ipv4'},
|
|
321
|
-
'bus_port': {'type': 'integer', 'min': 1024, 'max': 65536,
|
|
322
|
-
'nullable': True},
|
|
323
|
-
'path': {'type': 'string'},
|
|
324
|
-
'rtus': {
|
|
325
|
-
'type': 'list',
|
|
326
|
-
'schema': {
|
|
327
|
-
'type': 'dict',
|
|
328
|
-
'schema': {
|
|
329
|
-
'name': {},
|
|
330
|
-
'ip': {},
|
|
331
|
-
'rate': {},
|
|
332
|
-
'poll': {
|
|
333
|
-
'type': 'list',
|
|
334
|
-
'schema': {}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
},
|
|
339
|
-
'tags': {
|
|
340
|
-
'type': 'dict',
|
|
341
|
-
'keysrules': {
|
|
342
|
-
'type': 'string',
|
|
343
|
-
'ms_tagname': 'exists'
|
|
344
|
-
},
|
|
345
|
-
'valuesrules': {
|
|
346
|
-
'type': 'dict',
|
|
347
|
-
'schema': {
|
|
348
|
-
'type': {},
|
|
349
|
-
'read': {},
|
|
350
|
-
'write': {}
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
class MsValidator(Validator):
|
|
359
|
-
"""Additional application checks."""
|
|
360
|
-
|
|
361
|
-
ms_tagnames = {}
|
|
362
|
-
ms_notagcheck = {}
|
|
363
|
-
|
|
364
|
-
def __init__(self, *args, **kwargs):
|
|
365
|
-
super().__init__(*args, **kwargs)
|
|
366
|
-
self.context = {
|
|
367
|
-
'current_file': None,
|
|
368
|
-
'current_section': None
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
def _validate_ms_tagname(self, constraint, field, value):
|
|
372
|
-
"""
|
|
373
|
-
Test tagname exists, capture when true.
|
|
374
|
-
|
|
375
|
-
The rule's arguments are validated against this schema:
|
|
376
|
-
{'type': 'string'}
|
|
377
|
-
"""
|
|
378
|
-
if '.' in field:
|
|
379
|
-
self._error(field, f"'.' invalid in tag definition in {self.context['current_file']}")
|
|
380
|
-
if constraint == 'save':
|
|
381
|
-
if field in self.ms_tagnames:
|
|
382
|
-
self._error(field, f"attempt to redefine in {self.context['current_file']}")
|
|
383
|
-
else:
|
|
384
|
-
self.ms_tagnames[field] = {'type': None}
|
|
385
|
-
elif constraint == 'exists':
|
|
386
|
-
if value not in self.ms_tagnames:
|
|
387
|
-
self._error(field, f"tag '{value}' was not defined in tags.yaml")
|
|
388
|
-
elif constraint == 'none':
|
|
389
|
-
pass
|
|
390
|
-
else:
|
|
391
|
-
pass
|
|
392
|
-
|
|
393
|
-
def _validate_ms_tagtype(self, constraint, field, value):
|
|
394
|
-
"""
|
|
395
|
-
Test tagname type, capture when true.
|
|
396
|
-
|
|
397
|
-
The rule's arguments are validated against this schema:
|
|
398
|
-
{'type': 'boolean'}
|
|
399
|
-
"""
|
|
400
|
-
if constraint and field in self.ms_tagnames:
|
|
401
|
-
if self.ms_tagnames[field]['type'] is None:
|
|
402
|
-
if 'multi' in value:
|
|
403
|
-
self.ms_tagnames[field]['type'] = 'int'
|
|
404
|
-
else:
|
|
405
|
-
self.ms_tagnames[field]['type'] = value['type']
|
|
406
|
-
else:
|
|
407
|
-
self._error(field, 'attempt to redefine type')
|
|
408
|
-
else:
|
|
409
|
-
pass
|
|
410
|
-
|
|
411
|
-
def _validate_ms_ip(self, constraint, field, value):
|
|
412
|
-
"""
|
|
413
|
-
Test session.inet_aton works for the address.
|
|
414
|
-
|
|
415
|
-
The rule's arguments are validated against this schema:
|
|
416
|
-
{'type': 'string'}
|
|
417
|
-
"""
|
|
418
|
-
if value is None and 'none' in constraint:
|
|
419
|
-
pass
|
|
420
|
-
elif 'ipv4' in constraint:
|
|
421
|
-
try:
|
|
422
|
-
inet_aton(value)
|
|
423
|
-
except (OSError, TypeError):
|
|
424
|
-
self._error(field, 'ip address fails socket.inet_aton')
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
def validate(path: str = None):
|
|
428
|
-
"""Validate."""
|
|
429
|
-
s = {
|
|
430
|
-
'tags': TAG_SCHEMA,
|
|
431
|
-
'bus': BUS_SCHEMA,
|
|
432
|
-
'wwwserver': WWWSERVER_SCHEMA,
|
|
433
|
-
'history': HISTORY_SCHEMA,
|
|
434
|
-
'modbusserver': MODBUSSERVER_SCHEMA,
|
|
435
|
-
'modbusclient': MODBUSCLIENT_SCHEMA,
|
|
436
|
-
'snmpclient': SNMPCLIENT_SCHEMA,
|
|
437
|
-
'logixclient': LOGIXCLIENT_SCHEMA,
|
|
438
|
-
}
|
|
439
|
-
v = MsValidator(s)
|
|
440
|
-
c = {}
|
|
441
|
-
prefix = './' if path is None else f"{path}/"
|
|
442
|
-
for name in s.keys():
|
|
443
|
-
try:
|
|
444
|
-
v.context['current_file'] = f'{name}.yaml'
|
|
445
|
-
v.context['current_section'] = name
|
|
446
|
-
c[name] = dict(Config(f'{prefix}{name}.yaml'))
|
|
447
|
-
except Exception as e:
|
|
448
|
-
v._error(name, f'Failed to load {name}.yaml: {str(e)}')
|
|
449
|
-
return False, dump(v.errors), prefix
|
|
450
|
-
res = v.validate(c)
|
|
451
|
-
return res, dump(v.errors), prefix
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|