pymscada 0.0.15__py3-none-any.whl → 0.1.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.

Potentially problematic release.


This version of pymscada might be problematic. Click here for more details.

pymscada/validate.py CHANGED
@@ -51,7 +51,7 @@ TAG_SCHEMA = {
51
51
  'keysrules': {
52
52
  'type': 'string',
53
53
  # tag name discovery, save for later checking
54
- 'ms_tagname': True
54
+ 'ms_tagname': 'save'
55
55
  },
56
56
  'valuesrules': {
57
57
  'type': 'dict',
@@ -67,7 +67,7 @@ TAG_SCHEMA = {
67
67
  BUS_SCHEMA = {
68
68
  'type': 'dict',
69
69
  'schema': {
70
- 'ip': {'type': 'string', 'ms_ip': True},
70
+ 'ip': {'type': 'string', 'ms_ip': 'ipv4 none'},
71
71
  'port': {'type': 'integer', 'min': 1024, 'max': 65536}
72
72
  }
73
73
  }
@@ -88,12 +88,12 @@ VALUESETFILES_LIST = {
88
88
  'allowed': ['value', 'setpoint', 'files'],
89
89
  },
90
90
  # tagname must have been found in parsing tags.yaml
91
- 'tagname': {'type': 'string', 'ms_tagname': False}
91
+ 'tagname': {'type': 'string', 'ms_tagname': 'exists'}
92
92
  }
93
93
  SELECTDICT_LIST = {
94
94
  'type': {'type': 'string', 'allowed': ['selectdict']},
95
95
  # tagname must have been found in parsing tags.yaml
96
- 'tagname': {'type': 'string', 'ms_tagname': False},
96
+ 'tagname': {'type': 'string', 'ms_tagname': 'exists'},
97
97
  'opts': {
98
98
  'type': 'dict',
99
99
  # 'schema': {
@@ -138,7 +138,7 @@ UPLOT_LIST = {
138
138
  'type': 'dict',
139
139
  'schema': {
140
140
  # tagname must have been found in parsing tags.yaml
141
- 'tagname': {'type': 'string', 'ms_tagname': False},
141
+ 'tagname': {'type': 'string', 'ms_tagname': 'exists'},
142
142
  'label': {'type': 'string', 'required': False},
143
143
  'scale': {'type': 'string', 'required': False},
144
144
  'color': {'type': 'string', 'required': False},
@@ -170,9 +170,9 @@ LIST_WWWSERVER = {
170
170
  WWWSERVER_SCHEMA = {
171
171
  'type': 'dict',
172
172
  'schema': {
173
- 'bus_ip': {'type': 'string', 'ms_ip': True},
173
+ 'bus_ip': {'type': 'string', 'ms_ip': 'none ipv4'},
174
174
  'bus_port': {'type': 'integer', 'min': 1024, 'max': 65536},
175
- 'ip': {'type': 'string', 'ms_ip': True},
175
+ 'ip': {'type': 'string', 'ms_ip': 'none ipv4'},
176
176
  'port': {'type': 'integer', 'min': 1024, 'max': 65536},
177
177
  'get_path': {'nullable': True},
178
178
  'paths': {'type': 'list', 'allowed': ['history', 'config', 'pdf']},
@@ -186,7 +186,7 @@ WWWSERVER_SCHEMA = {
186
186
  HISTORY_SCHEMA = {
187
187
  'type': 'dict',
188
188
  'schema': {
189
- 'bus_ip': {'type': 'string', 'ms_ip': True},
189
+ 'bus_ip': {'type': 'string', 'ms_ip': 'none ipv4'},
190
190
  'bus_port': {'type': 'integer', 'min': 1024, 'max': 65536},
191
191
  'path': {'type': 'string'},
192
192
  }
@@ -195,7 +195,7 @@ HISTORY_SCHEMA = {
195
195
  MODBUSSERVER_SCHEMA = {
196
196
  'type': 'dict',
197
197
  'schema': {
198
- 'bus_ip': {'type': 'string', 'ms_ip': True, 'nullable': True},
198
+ 'bus_ip': {'type': 'string', 'ms_ip': 'none ipv4', 'nullable': True},
199
199
  'bus_port': {'type': 'integer', 'min': 1024, 'max': 65536,
200
200
  'nullable': True},
201
201
  'path': {'type': 'string'},
@@ -219,7 +219,7 @@ MODBUSSERVER_SCHEMA = {
219
219
  'type': 'dict',
220
220
  'keysrules': {
221
221
  'type': 'string',
222
- 'ms_tagname': False
222
+ 'ms_tagname': 'none'
223
223
  },
224
224
  'valuesrules': {
225
225
  'type': 'dict',
@@ -235,7 +235,7 @@ MODBUSSERVER_SCHEMA = {
235
235
  MODBUSCLIENT_SCHEMA = {
236
236
  'type': 'dict',
237
237
  'schema': {
238
- 'bus_ip': {'type': 'string', 'ms_ip': True, 'nullable': True},
238
+ 'bus_ip': {'type': 'string', 'ms_ip': 'ipv4'},
239
239
  'bus_port': {'type': 'integer', 'min': 1024, 'max': 65536,
240
240
  'nullable': True},
241
241
  'path': {'type': 'string'},
@@ -249,11 +249,48 @@ MODBUSCLIENT_SCHEMA = {
249
249
  'port': {},
250
250
  'tcp_udp': {'type': 'string', 'allowed': ['tcp', 'udp']},
251
251
  'rate': {},
252
- 'read': {
252
+ 'poll': {
253
253
  'type': 'list',
254
254
  'schema': {}
255
- },
256
- 'writeok': {
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': {
257
294
  'type': 'list',
258
295
  'schema': {}
259
296
  }
@@ -264,44 +301,91 @@ MODBUSCLIENT_SCHEMA = {
264
301
  'type': 'dict',
265
302
  'keysrules': {
266
303
  'type': 'string',
267
- 'ms_tagname': False
304
+ 'ms_tagname': 'exists'
268
305
  },
269
306
  'valuesrules': {
270
307
  'type': 'dict',
271
308
  'schema': {
272
309
  'type': {},
273
- 'addr': {}
274
- },
310
+ 'read': {}
311
+ }
275
312
  }
276
- }
313
+ }
277
314
  }
278
315
  }
279
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
+
280
358
  class MsValidator(Validator):
281
- """Add additional application checks"""
359
+ """Additional application checks."""
360
+
282
361
  ms_tagnames = {}
362
+ ms_notagcheck = {}
283
363
 
284
364
  def _validate_ms_tagname(self, constraint, field, value):
285
- """ Test tagname exists, capture when true.
365
+ """
366
+ Test tagname exists, capture when true.
286
367
 
287
368
  The rule's arguments are validated against this schema:
288
- {'type': 'boolean'}
369
+ {'type': 'string'}
289
370
  """
290
371
  if '.' in field:
291
372
  self._error(field, "'.' invalid in tag definition.")
292
- if constraint:
373
+ if constraint == 'save':
293
374
  if field in self.ms_tagnames:
294
375
  self._error(field, 'attempt to redefine')
295
376
  else:
296
377
  self.ms_tagnames[field] = {'type': None}
297
- else:
378
+ elif constraint == 'exists':
298
379
  if value not in self.ms_tagnames:
299
380
  self._error(field, 'tag was not defined in tags.yaml')
300
- else:
301
- pass
381
+ elif constraint == 'none':
382
+ pass
383
+ else:
384
+ pass
302
385
 
303
386
  def _validate_ms_tagtype(self, constraint, field, value):
304
- """ Test tagname type, capture when true.
387
+ """
388
+ Test tagname type, capture when true.
305
389
 
306
390
  The rule's arguments are validated against this schema:
307
391
  {'type': 'boolean'}
@@ -318,18 +402,22 @@ class MsValidator(Validator):
318
402
  pass
319
403
 
320
404
  def _validate_ms_ip(self, constraint, field, value):
321
- """ Test session.inet_aton works for the address.
405
+ """
406
+ Test session.inet_aton works for the address.
322
407
 
323
408
  The rule's arguments are validated against this schema:
324
- {'type': 'boolean'}
409
+ {'type': 'string'}
325
410
  """
326
- try:
327
- inet_aton(value)
328
- except (OSError, TypeError):
329
- self._error(field, 'ip address fails socket.inet_aton')
411
+ if value is None and 'none' in constraint:
412
+ pass
413
+ elif 'ipv4' in constraint:
414
+ try:
415
+ inet_aton(value)
416
+ except (OSError, TypeError):
417
+ self._error(field, 'ip address fails socket.inet_aton')
330
418
 
331
419
 
332
- def validate(path: str=None):
420
+ def validate(path: str = None):
333
421
  """Validate."""
334
422
  s = {
335
423
  'tags': TAG_SCHEMA,
@@ -338,8 +426,10 @@ def validate(path: str=None):
338
426
  'history': HISTORY_SCHEMA,
339
427
  'modbusserver': MODBUSSERVER_SCHEMA,
340
428
  'modbusclient': MODBUSCLIENT_SCHEMA,
429
+ 'snmpclient': SNMPCLIENT_SCHEMA,
430
+ 'logixclient': LOGIXCLIENT_SCHEMA,
341
431
  }
342
- prefix = ''
432
+ prefix = './'
343
433
  if path is not None:
344
434
  prefix = path + '/'
345
435
  c = {
@@ -349,8 +439,10 @@ def validate(path: str=None):
349
439
  'history': dict(Config(f'{prefix}history.yaml')),
350
440
  'modbusserver': dict(Config(f'{prefix}modbusserver.yaml')),
351
441
  'modbusclient': dict(Config(f'{prefix}modbusclient.yaml')),
442
+ 'snmpclient': dict(Config(f'{prefix}snmpclient.yaml')),
443
+ 'logixclient': dict(Config(f'{prefix}logixclient.yaml')),
352
444
  }
353
445
  v = MsValidator(s)
354
446
  res = v.validate(c)
355
447
  wdy = dump(v.errors) # , default_flow_style=False)
356
- return res, wdy
448
+ return res, wdy, prefix
@@ -0,0 +1,88 @@
1
+ Metadata-Version: 2.1
2
+ Name: pymscada
3
+ Version: 0.1.0
4
+ Summary: Shared tag value SCADA with python backup and Angular UI
5
+ Author-Email: Jamie Walton <jamie@walton.net.nz>
6
+ License: GPL-3.0-or-later
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: JavaScript
9
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Environment :: Console
12
+ Classifier: Development Status :: 1 - Planning
13
+ Requires-Python: >=3.9
14
+ Requires-Dist: PyYAML>=6.0.1
15
+ Requires-Dist: aiohttp>=3.8.5
16
+ Requires-Dist: pymscada-html==0.1.0
17
+ Requires-Dist: cerberus>=1.3.5
18
+ Requires-Dist: pycomm3>=1.2.14
19
+ Requires-Dist: pysnmplib>=5.0.24
20
+ Description-Content-Type: text/markdown
21
+
22
+ # pymscada
23
+ #### [Docs](https://github.com/jamie0walton/pymscada/blob/main/docs/README.md)
24
+
25
+ #### [@Github](https://github.com/jamie0walton/pymscada/blob/main/README.md)
26
+
27
+ ## Python Mobile SCADA
28
+
29
+ ```pymscada``` read / write to Modbus and Logix PLCs. Read SNMP OIDs.
30
+ Collect history values and provide the ability to set values and trends
31
+ and issue commands.
32
+
33
+ User interface is via a web client embedded in this package. Examples included
34
+ for securing with Apache as a proxy.
35
+
36
+ Configuration with text yaml files, including the web page which are
37
+ procedurally built.
38
+
39
+ # See also
40
+
41
+ - The angular project [angmscada](https://github.com/jamie0walton/angmscada)
42
+ - Python container for the compiled angular pages [pymscada-html](https://github.com/jamie0walton/pymscada-html)
43
+
44
+ # Licence
45
+
46
+ ```pymscada``` is distributed under the GPLv3 [license](./LICENSE).
47
+
48
+ # Use
49
+ Checkout the example files.
50
+ ```bash
51
+ mscada@raspberrypi:~/test $ pymscada checkout
52
+ making 'history' folder
53
+ making pdf dir
54
+ making config dir
55
+ Creating /home/mscada/test/config/modbusclient.yaml
56
+ Creating /home/mscada/test/config/pymscada-history.service
57
+ Creating /home/mscada/test/config/wwwserver.yaml
58
+ Creating /home/mscada/test/config/pymscada-demo-modbus_plc.service
59
+ Creating /home/mscada/test/config/files.yaml
60
+ Creating /home/mscada/test/config/pymscada-modbusserver.service
61
+ Creating /home/mscada/test/config/pymscada-wwwserver.service
62
+ Creating /home/mscada/test/config/simulate.yaml
63
+ Creating /home/mscada/test/config/tags.yaml
64
+ Creating /home/mscada/test/config/history.yaml
65
+ Creating /home/mscada/test/config/pymscada-files.service
66
+ Creating /home/mscada/test/config/bus.yaml
67
+ Creating /home/mscada/test/config/modbusserver.yaml
68
+ Creating /home/mscada/test/config/modbus_plc.py
69
+ Creating /home/mscada/test/config/pymscada-modbusclient.service
70
+ Creating /home/mscada/test/config/pymscada-bus.service
71
+ Creating /home/mscada/test/config/README.md
72
+ mscada@raspberrypi:~/test $ pymscada validate
73
+ WARNING:root:pymscada 0.1.0 starting
74
+ Config files in ./ valid.
75
+ ```
76
+
77
+ Runs on a Raspberry Pi and includes preconfigured systemd files to
78
+ automate running the services. Mostly works on Windows, works better
79
+ on linux.
80
+
81
+ Modules can be run from the command line, although you need
82
+ a terminal for each running module (better with systemd).
83
+ ```bash
84
+ pymscada bus --config bus.yaml
85
+ pymscada wwwserver --config wwwserver.yaml --tags tags.yaml
86
+ pymscada history --config history.yaml --tags tags.yaml
87
+ python weather.py
88
+ ```
@@ -1,12 +1,12 @@
1
- pymscada-0.0.15.dist-info/METADATA,sha256=KTv3Do9ckYlHrqe9MFYhi_VqY0EPGCBT965n1SvLn40,9946
2
- pymscada-0.0.15.dist-info/WHEEL,sha256=N2J68yzZqJh3mI_Wg92rwhw0rtJDFpZj9bwQIMJgaVg,90
3
- pymscada-0.0.15.dist-info/entry_points.txt,sha256=AcZZ7HFj8k1ztP6ge-5bdRinYF8glW2s6lFEQG3esN4,57
4
- pymscada-0.0.15.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
1
+ pymscada-0.1.0.dist-info/METADATA,sha256=4k0xZCUfrBdcOG1vnI9reNEvrOg8UKqG6MaN5vp_Oqs,3212
2
+ pymscada-0.1.0.dist-info/WHEEL,sha256=N2J68yzZqJh3mI_Wg92rwhw0rtJDFpZj9bwQIMJgaVg,90
3
+ pymscada-0.1.0.dist-info/entry_points.txt,sha256=AcZZ7HFj8k1ztP6ge-5bdRinYF8glW2s6lFEQG3esN4,57
4
+ pymscada-0.1.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
5
5
  pymscada/__init__.py,sha256=D_4aEDWkW6xMVQane3CbehBKPxT3FCaDY_QpFwglCe8,653
6
6
  pymscada/__main__.py,sha256=WcyVlrYOoDdktJhOoyubTOycMwpayksFdxwelRU5xpQ,272
7
7
  pymscada/bus_client.py,sha256=A6EI3OafyOBKd8UVFXS4K39SpmwBoFE-kSIwldVeDKE,8695
8
8
  pymscada/bus_server.py,sha256=-Bn4rfdE4OWXE0av459sROnCwAqV0VKFWulF0m1abHE,11255
9
- pymscada/checkout.py,sha256=c8Fy--Oz4GMXaOO48VmySyI31Y2qjxL-_Tuh7V29JeY,2236
9
+ pymscada/checkout.py,sha256=ISXhwRJbZBRu0cMeVHwgHzMgk9YslhnPtjs7-0lgjRo,3351
10
10
  pymscada/config.py,sha256=vwGxieaJBYXiHNQEOYVDFaPuGmnUlCnbNm_W9bugKlc,1851
11
11
  pymscada/console.py,sha256=sw1k6eXY53S8qMR-kx8pHOyhXHHt88e4RTcXmC7AQlw,946
12
12
  pymscada/demo/README.md,sha256=iNcVbCTkq-d4agLV-979lNRaqf_hbJCn3OFzY-6qfU8,880
@@ -14,10 +14,11 @@ pymscada/demo/__init__.py,sha256=WsDDgkWnZBJbt2-cJCdc2NvRAv_T4a7WOC1Q0k_l0gI,29
14
14
  pymscada/demo/bus.yaml,sha256=zde5JDo2Yv5s7NvJ569gAEoTDvsvgBwRPxfrYhsxj3w,26
15
15
  pymscada/demo/files.yaml,sha256=Uizvo-LEhHCRePGNLdvl9m_Z40qOlT7aZDLE4zTFSMc,169
16
16
  pymscada/demo/history.yaml,sha256=mn_Xf4h_bK6vwZVQ0Iz9BzJpwWok2gEgSKbDgEM8AOQ,46
17
- pymscada/demo/logixclient.yaml,sha256=_b6TlvjLKshlp3aFNh4c229QDLJCrbLIUI1eOYk_uFM,1055
17
+ pymscada/demo/logixclient.yaml,sha256=G_NlJhBYwT1a9ceHDgO6fCNKFmBM2pVO_t9Xa1NqlRY,912
18
18
  pymscada/demo/modbus_plc.py,sha256=3zZHHbyrdxyryEHBeNIw-fpcDGcS1MaJiqEwQDr6zWI,2397
19
- pymscada/demo/modbusclient.yaml,sha256=vF96CTA-fsIbTUMnVa1yE4NBnMW5-jeVyEe4HrwfvCY,1020
20
- pymscada/demo/modbusserver.yaml,sha256=xit3bfQrf_CnJZXXiGnWAOOAfH33KIxJC4jQWqkEJLc,1036
19
+ pymscada/demo/modbusclient.yaml,sha256=geeCsUJZkkEj7jjXR_Yk6R5zA5Ta9IczrHsARz7ZgXY,1099
20
+ pymscada/demo/modbusserver.yaml,sha256=67_mED6jXgtnzlDIky9Cg4j-nXur06iz9ve3JUwSyG8,1133
21
+ pymscada/demo/ping.yaml,sha256=r_VBGTLU5r4cZi9bIGL3M4eNw70KnoBptOUoNrSbnFY,210
21
22
  pymscada/demo/pymscada-bus.service,sha256=rRTFwHaS8XWd9YAIB3cET4QvASaIO9emmxFiUAbl14g,257
22
23
  pymscada/demo/pymscada-demo-modbus_plc.service,sha256=jmgk_peoxwKVXe-LbyK2VluMS1JMmoTud4JZHi9Tgec,316
23
24
  pymscada/demo/pymscada-files.service,sha256=yGTxYnmQ_QBjIzItFatw_uIg_cG11vadLDaR0C9pPEk,322
@@ -25,24 +26,25 @@ pymscada/demo/pymscada-history.service,sha256=kEU_RsrRSmEUE-nR23n2q2yZxjALm_nCrJ
25
26
  pymscada/demo/pymscada-io-logixclient.service,sha256=gvnCJgUeqnIgnuN-Wf1XhB0FJxVYyLksmMO7JC0wT-Y,344
26
27
  pymscada/demo/pymscada-io-modbusclient.service,sha256=4tenKcrfRi0iMdv8-k2gtMQA4OPTM59zAyKpovwemxM,344
27
28
  pymscada/demo/pymscada-io-modbusserver.service,sha256=FqCMD3EJKoiq6EbYnoijRLX5UeUWbZrNzDs50eQj7iE,344
29
+ pymscada/demo/pymscada-io-ping.service,sha256=d1n32srVKGd8qo8JWeBYEEznCRZWRWaBQLOYzdqEXWg,327
28
30
  pymscada/demo/pymscada-io-snmpclient.service,sha256=wrA2kDR3bgO30lP_lNJrIsVXNQiXmWKnphoqUj3QTRI,339
29
31
  pymscada/demo/pymscada-wwwserver.service,sha256=uDnqzfvAdAnTrqOCqDm1PN7SmeMSuOdmAhorHPJdEVI,366
30
- pymscada/demo/simulate.yaml,sha256=SprrcLY8sx5c-2RggtZqhZS3SL03QM3Yp-Rh5F0trA8,324
31
- pymscada/demo/snmpclient.yaml,sha256=SCjAN8kg7cmS3-Hu6gHw2mo7KxQSYtSi5ew-fIxRs6I,2013
32
- pymscada/demo/tags.yaml,sha256=cLSS6tIQ1XyVf7ldcdW0J2Tv8VsIQwfka4dPuBsY7FI,3494
33
- pymscada/demo/wwwserver.yaml,sha256=KDbe3mmslcK1L0ZnuvtOhhuJG-Hyw9OztMz813pChYw,1920
32
+ pymscada/demo/snmpclient.yaml,sha256=z8iACrFvMftYUtqGrRjPZYZTpn7aOXI-Kp675NAM8cU,2013
33
+ pymscada/demo/tags.yaml,sha256=GH90X3QRBANUhvd2E9OuyIoiZD25OBihHHlDBw1uzlw,4231
34
+ pymscada/demo/wwwserver.yaml,sha256=jyboShIbwkwgMAcFLkpn88VMvGfoc7a44jNrI_B6OqY,12493
34
35
  pymscada/files.py,sha256=kEkiD7j5k69EK4jfMHEp-lbTnulYyrtUjkQDa5xmGPc,1784
35
36
  pymscada/history.py,sha256=yAUfjzo4O3w9-hGEAbtvz9UseMTwRVz7NqS832RtvIs,9455
36
37
  pymscada/iodrivers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
- pymscada/iodrivers/logix_client.py,sha256=czoMLo6VXaj-JrhOkmvoZlXUTzqF4Ez_I2Fe0oW15p8,2808
38
- pymscada/iodrivers/logix_map.py,sha256=Cq3pIb7HmbpPdrDLGVCJ65pdXch2O5672RCgz3Fm-Ec,5575
39
- pymscada/iodrivers/modbus_client.py,sha256=aGmy1lAlpmnJxEALZc8VfOXCZpI4Ukgslv14S9EYrpE,10140
40
- pymscada/iodrivers/modbus_map.py,sha256=oFCCkHeGQudSPKhiUnSK3gg2WL_O25wuRCQXEZfpqcA,7172
41
- pymscada/iodrivers/modbus_server.py,sha256=e_wCiF044CK_a-WgfHBfHwj4v3Qwybt3yIpBcInoPMk,7087
42
- pymscada/iodrivers/snmp_client.py,sha256=plKh0gyI_KqD-RLorBJ3OfPrmMG0tuPwOB-SUrC-qFs,2542
43
- pymscada/iodrivers/snmp_client2.py,sha256=pdn5dYyEv4q-ubA0zQ8X-3tQDYxGC7f7Xexa7QPaL40,1675
44
- pymscada/iodrivers/snmp_map.py,sha256=rCTniik40Yy6avooIW8PqWanrnUglwBLKReIM7Ab5lA,2369
45
- pymscada/main.py,sha256=ss_wlV5Pmtp4yKOhk8-KaEloBGGytKLEntdK7WNjcaQ,3975
38
+ pymscada/iodrivers/logix_client.py,sha256=wDhl0pCqs_k7Hd1MtbOuBrEx9emOwDrLJqBNussH1qU,2806
39
+ pymscada/iodrivers/logix_map.py,sha256=ljjBAMJcw199v1V5u0Yfl38U6zbZzba5mdY4I3ZvdIM,5401
40
+ pymscada/iodrivers/modbus_client.py,sha256=mo7VRLWi4W2J5Ft3M6Y5jSbK5vbd6wn4R2dncbiL4nU,9529
41
+ pymscada/iodrivers/modbus_map.py,sha256=af2J3CGSeYQ4mSy8rNsERp9z7fRgRUYk3it5Mrc_IQA,7255
42
+ pymscada/iodrivers/modbus_server.py,sha256=nnUjd6iCny_Abylk06J8dq4slNAyNPQC2Rw2ZWEElKM,6507
43
+ pymscada/iodrivers/ping_client.py,sha256=dIR4SuKeNCu2FyhUNfTbkv-El3qPFMb0zUG1GJSD-80,4166
44
+ pymscada/iodrivers/ping_map.py,sha256=EbOteqfEYKIOMqPymROJ4now2If-ekEj6jnM5hthoSA,1403
45
+ pymscada/iodrivers/snmp_client.py,sha256=e-ea5BQnp-PxTetM9d61Mm-GkQrNZkUUlNp1Km9ZasI,2601
46
+ pymscada/iodrivers/snmp_map.py,sha256=sDdIR5ZPAETpozDfBt_XQiZ-f4t99UCPlzj7BxFxQyM,2369
47
+ pymscada/main.py,sha256=LxFUuijrIuNLOGhNy_5yNIq4pvNz4npsUwHaDtvrdCQ,7191
46
48
  pymscada/misc.py,sha256=0Cj6OFhQonyhyk9x0BG5MiS-6EPk_w6zvavt8o_Hlf0,622
47
49
  pymscada/pdf/__init__.py,sha256=WsDDgkWnZBJbt2-cJCdc2NvRAv_T4a7WOC1Q0k_l0gI,29
48
50
  pymscada/pdf/one.pdf,sha256=eoJ45DrAjVZrwmwdA_EAz1fwmT44eRnt_tkc2pmMrKY,1488
@@ -50,9 +52,9 @@ pymscada/pdf/two.pdf,sha256=TAuW5yLU1_wfmTH_I5ezHwY0pxhCVuZh3ixu0kwmJwE,1516
50
52
  pymscada/periodic.py,sha256=MLlL93VLvFqBBgjO1Us1t0aLHTZ5BFdW0B__G02T1nQ,1235
51
53
  pymscada/protocol_constants.py,sha256=ndFeuzhWjKTr_ahvmGJc6Cs5pYkHM8CRgNNDe0Tqecs,1997
52
54
  pymscada/samplers.py,sha256=t0IscgsCm5YByioOZ6aOKMO_guDFS_wxnJSiOGKI4Nw,2583
53
- pymscada/simulate.py,sha256=39PfIc_jJC4ekuSEqlVb8SlxvIjwgyTkCL5fSlwmanc,2404
54
55
  pymscada/tag.py,sha256=Q2EgacTnsxnGLM_zoHYsVWdwmd1faqfbxw9byI5fCgY,9463
56
+ pymscada/tools/snmp_client2.py,sha256=pdn5dYyEv4q-ubA0zQ8X-3tQDYxGC7f7Xexa7QPaL40,1675
55
57
  pymscada/tools/walk.py,sha256=OgpprUbKLhEWMvJGfU1ckUt_PFEpwZVOD8HucCgzmOc,1625
56
- pymscada/validate.py,sha256=h2VnIT3EV3hLGtaAaBTjeDVMrVl8SpP9nz_fqyRtU4Y,10634
58
+ pymscada/validate.py,sha256=VPpAVEwfgori5OREEwWlbPoPxz5Tfqr6dw-O5pINHyI,13125
57
59
  pymscada/www_server.py,sha256=BW6-ctE5BgyFDiyUJsOpe3c06DRp8k83MCGjwPh5fco,11473
58
- pymscada-0.0.15.dist-info/RECORD,,
60
+ pymscada-0.1.0.dist-info/RECORD,,
@@ -1,21 +0,0 @@
1
- bus_ip: 127.0.0.1
2
- bus_port: 1324
3
- process:
4
- one:
5
- type: sine
6
- offset: 50
7
- amplitude: 40
8
- frequency: 0.0005
9
- sub:
10
- offset: FloatSet
11
- pub:
12
- result: FloatVal
13
- two:
14
- type: sawtooth
15
- offset: 10
16
- amplitude: 80
17
- frequency: 0.00005
18
- sub:
19
- offset: IntSet
20
- pub:
21
- result: IntVal
pymscada/simulate.py DELETED
@@ -1,66 +0,0 @@
1
- """Simulate logic."""
2
- import asyncio
3
- import logging
4
- import math
5
- from pymscada.bus_client import BusClient
6
- from pymscada.misc import find_nodes
7
- from pymscada.tag import Tag, TYPES
8
- from time import monotonic
9
-
10
-
11
- class Simulate():
12
- """Modify tag values to simulate a real process."""
13
-
14
- def __init__(self, bus_ip: str = '127.0.0.1', bus_port: int = 1324,
15
- tag_info: dict = {}, process: dict = {}) -> None:
16
- """
17
- Connect to bus on bus_ip:bus_port, modify tag values per process.
18
-
19
- Event loop must be running.
20
- """
21
- self.busclient = BusClient(bus_ip, bus_port, tag_info)
22
- self.process = process
23
- self.tags = {}
24
- for x in ['sub', 'pub']: # resist the temptation to
25
- for t in find_nodes(x, self.process):
26
- for tagname in t[x].values():
27
- self.tags[tagname] = Tag(
28
- tagname, TYPES[tag_info[tagname]['type']])
29
-
30
- def step(self):
31
- """Do simulation step."""
32
- for e in self.process.values():
33
- time = monotonic()
34
- tagname = e['pub']['result']
35
- if e['type'] == 'sine':
36
- result = e['offset'] + e['amplitude'] * \
37
- math.sin(e['frequency'] * time)
38
- elif e['type'] == 'sawtooth':
39
- period = 1 / e['frequency']
40
- result = int(e['offset'] + e['amplitude'] * (time %
41
- period) / period)
42
- else:
43
- return
44
- if self.tags[tagname].value is None or \
45
- abs(self.tags[tagname].value - result) > 0.01:
46
- self.tags[tagname].value = result
47
- logging.warning(f'simulate {tagname} {result}')
48
-
49
- async def periodic(self):
50
- """Run simulation step every 5 seconds."""
51
- while True:
52
- self.next_run += 1.0
53
- self.step()
54
- self.last_ran = monotonic()
55
- sleep_time = self.next_run - self.last_ran
56
- if sleep_time < 0:
57
- self.next_run = self.last_ran
58
- logging.warning(f'Health count skipped at {self.last_ran}')
59
- else:
60
- await asyncio.sleep(sleep_time)
61
-
62
- async def start(self):
63
- """Provide the simulation process."""
64
- await self.busclient.start()
65
- self.next_run = monotonic()
66
- await asyncio.create_task(self.periodic())