pymscada 0.1.2__tar.gz → 0.1.4__tar.gz

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.

Files changed (84) hide show
  1. pymscada-0.1.4/PKG-INFO +59 -0
  2. pymscada-0.1.4/README.md +38 -0
  3. {pymscada-0.1.2 → pymscada-0.1.4}/pyproject.toml +1 -1
  4. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/checkout.py +10 -7
  5. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/console.py +1 -1
  6. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/files.yaml +1 -1
  7. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/history.yaml +1 -1
  8. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/opnotes.yaml +1 -1
  9. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/pymscada-bus.service +2 -2
  10. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/pymscada-demo-modbus_plc.service +2 -2
  11. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/pymscada-files.service +2 -2
  12. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/pymscada-history.service +2 -2
  13. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/pymscada-io-logixclient.service +2 -2
  14. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/pymscada-io-modbusclient.service +2 -2
  15. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/pymscada-io-modbusserver.service +2 -2
  16. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/pymscada-io-ping.service +2 -2
  17. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/pymscada-io-snmpclient.service +2 -2
  18. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/pymscada-opnotes.service +2 -2
  19. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/pymscada-wwwserver.service +2 -2
  20. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/wwwserver.yaml +9 -3
  21. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/iodrivers/snmp_client.py +4 -1
  22. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/opnotes.py +39 -24
  23. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/www_server.py +19 -5
  24. {pymscada-0.1.2 → pymscada-0.1.4}/tests/test_opnotes.py +17 -17
  25. pymscada-0.1.2/PKG-INFO +0 -88
  26. pymscada-0.1.2/README.md +0 -67
  27. {pymscada-0.1.2 → pymscada-0.1.4}/LICENSE +0 -0
  28. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/__init__.py +0 -0
  29. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/__main__.py +0 -0
  30. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/bus_client.py +0 -0
  31. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/bus_server.py +0 -0
  32. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/config.py +0 -0
  33. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/README.md +0 -0
  34. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/__init__.py +0 -0
  35. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/bus.yaml +0 -0
  36. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/logixclient.yaml +0 -0
  37. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/modbus_plc.py +0 -0
  38. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/modbusclient.yaml +0 -0
  39. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/modbusserver.yaml +0 -0
  40. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/ping.yaml +0 -0
  41. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/snmpclient.yaml +0 -0
  42. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/demo/tags.yaml +0 -0
  43. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/files.py +0 -0
  44. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/history.py +0 -0
  45. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/iodrivers/__init__.py +0 -0
  46. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/iodrivers/logix_client.py +0 -0
  47. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/iodrivers/logix_map.py +0 -0
  48. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/iodrivers/modbus_client.py +0 -0
  49. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/iodrivers/modbus_map.py +0 -0
  50. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/iodrivers/modbus_server.py +0 -0
  51. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/iodrivers/ping_client.py +0 -0
  52. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/iodrivers/ping_map.py +0 -0
  53. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/iodrivers/snmp_map.py +0 -0
  54. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/main.py +0 -0
  55. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/misc.py +0 -0
  56. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/pdf/__init__.py +0 -0
  57. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/pdf/one.pdf +0 -0
  58. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/pdf/two.pdf +0 -0
  59. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/periodic.py +0 -0
  60. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/protocol_constants.py +0 -0
  61. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/samplers.py +0 -0
  62. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/tag.py +0 -0
  63. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/tools/snmp_client2.py +0 -0
  64. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/tools/walk.py +0 -0
  65. {pymscada-0.1.2 → pymscada-0.1.4}/src/pymscada/validate.py +0 -0
  66. {pymscada-0.1.2 → pymscada-0.1.4}/tests/__init__.py +0 -0
  67. {pymscada-0.1.2 → pymscada-0.1.4}/tests/bus_echo.py +0 -0
  68. {pymscada-0.1.2 → pymscada-0.1.4}/tests/iodrivers/test_logix.py +0 -0
  69. {pymscada-0.1.2 → pymscada-0.1.4}/tests/iodrivers/test_modbus.py +0 -0
  70. {pymscada-0.1.2 → pymscada-0.1.4}/tests/test_assets/busserver.yaml +0 -0
  71. {pymscada-0.1.2 → pymscada-0.1.4}/tests/test_assets/db.sqlite +0 -0
  72. {pymscada-0.1.2 → pymscada-0.1.4}/tests/test_assets/hist_tag_0_0.dat +0 -0
  73. {pymscada-0.1.2 → pymscada-0.1.4}/tests/test_assets/hist_tag_0_10_2.dat +0 -0
  74. {pymscada-0.1.2 → pymscada-0.1.4}/tests/test_assets/hist_tag_0_15.dat +0 -0
  75. {pymscada-0.1.2 → pymscada-0.1.4}/tests/test_assets/hist_tag_0_26.dat +0 -0
  76. {pymscada-0.1.2 → pymscada-0.1.4}/tests/test_assets/hist_tag_0_50.dat +0 -0
  77. {pymscada-0.1.2 → pymscada-0.1.4}/tests/test_bus_server.py +0 -0
  78. {pymscada-0.1.2 → pymscada-0.1.4}/tests/test_config.py +0 -0
  79. {pymscada-0.1.2 → pymscada-0.1.4}/tests/test_history.py +0 -0
  80. {pymscada-0.1.2 → pymscada-0.1.4}/tests/test_misc.py +0 -0
  81. {pymscada-0.1.2 → pymscada-0.1.4}/tests/test_periodic.py +0 -0
  82. {pymscada-0.1.2 → pymscada-0.1.4}/tests/test_samplers.py +0 -0
  83. {pymscada-0.1.2 → pymscada-0.1.4}/tests/test_tag.py +0 -0
  84. {pymscada-0.1.2 → pymscada-0.1.4}/tests/test_validate.py +0 -0
@@ -0,0 +1,59 @@
1
+ Metadata-Version: 2.1
2
+ Name: pymscada
3
+ Version: 0.1.4
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
+ <span style='color:aqua'>WIP: Presently updating documentation and deployment.</span>
28
+
29
+ ## Python Mobile SCADA
30
+
31
+ ```pymscada``` read / write to Modbus and Logix PLCs. Read SNMP OIDs.
32
+ Collect history values and provide the ability to set values and trends
33
+ and issue commands.
34
+
35
+ User interface is via a web client embedded in this package. Examples included
36
+ for securing with Apache as a proxy.
37
+
38
+ Configuration with text yaml files, including the web page which are
39
+ procedurally built.
40
+
41
+ # See also
42
+
43
+ - The angular project [angmscada](https://github.com/jamie0walton/angmscada)
44
+ - Python container for the compiled angular pages [pymscada-html](https://github.com/jamie0walton/pymscada-html)
45
+
46
+ # Licence
47
+
48
+ ```pymscada``` is distributed under the GPLv3 [license](./LICENSE).
49
+
50
+ # Running
51
+
52
+ While many parts of ```pymscada``` will run in windows, this is not intentional.
53
+
54
+ Running a useful subset requires quite a lot of steps, you have to choose the services
55
+ you want and providing meaningful configuation. ```pymscada checkout``` will create
56
+ templates of all of these for you that allows
57
+ [Debian Quickstart](./docs/debian_quickstart.md) to get you to a working web page,
58
+ however to connect to a PLC, trend data, read data and write setpoints, requires
59
+ knowledge of typical SCADA and PLC functionality.
@@ -0,0 +1,38 @@
1
+ # pymscada
2
+ #### [Docs](https://github.com/jamie0walton/pymscada/blob/main/docs/README.md)
3
+
4
+ #### [@Github](https://github.com/jamie0walton/pymscada/blob/main/README.md)
5
+
6
+ <span style='color:aqua'>WIP: Presently updating documentation and deployment.</span>
7
+
8
+ ## Python Mobile SCADA
9
+
10
+ ```pymscada``` read / write to Modbus and Logix PLCs. Read SNMP OIDs.
11
+ Collect history values and provide the ability to set values and trends
12
+ and issue commands.
13
+
14
+ User interface is via a web client embedded in this package. Examples included
15
+ for securing with Apache as a proxy.
16
+
17
+ Configuration with text yaml files, including the web page which are
18
+ procedurally built.
19
+
20
+ # See also
21
+
22
+ - The angular project [angmscada](https://github.com/jamie0walton/angmscada)
23
+ - Python container for the compiled angular pages [pymscada-html](https://github.com/jamie0walton/pymscada-html)
24
+
25
+ # Licence
26
+
27
+ ```pymscada``` is distributed under the GPLv3 [license](./LICENSE).
28
+
29
+ # Running
30
+
31
+ While many parts of ```pymscada``` will run in windows, this is not intentional.
32
+
33
+ Running a useful subset requires quite a lot of steps, you have to choose the services
34
+ you want and providing meaningful configuation. ```pymscada checkout``` will create
35
+ templates of all of these for you that allows
36
+ [Debian Quickstart](./docs/debian_quickstart.md) to get you to a working web page,
37
+ however to connect to a PLC, trend data, read data and write setpoints, requires
38
+ knowledge of typical SCADA and PLC functionality.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pymscada"
3
- version = "0.1.2"
3
+ version = "0.1.4"
4
4
  description = "Shared tag value SCADA with python backup and Angular UI"
5
5
  authors = [
6
6
  { name = "Jamie Walton", email = "jamie@walton.net.nz" },
@@ -1,5 +1,6 @@
1
1
  """Create base config folder and check out demo files."""
2
2
  import difflib
3
+ import getpass
3
4
  from pathlib import Path
4
5
  import sys
5
6
  from pymscada.config import get_demo_files, get_pdf_files
@@ -8,16 +9,19 @@ from pymscada.config import get_demo_files, get_pdf_files
8
9
  PATH = {
9
10
  '__PYTHON__': Path(f'{sys.exec_prefix}/bin/python').absolute(),
10
11
  '__PYMSCADA__': Path(sys.argv[0]).absolute(),
11
- '__DIR__': Path('.').absolute()
12
+ '__DIR__': Path('.').absolute(),
13
+ '__HOME__': Path.home().absolute(),
14
+ '__USER__': getpass.getuser()
12
15
  }
13
16
  if sys.platform == "win32":
14
17
  PATH = {
15
18
  '__PYTHON__': Path(f'{sys.exec_prefix}/python.exe').absolute(),
16
19
  '__PYMSCADA__': Path(sys.argv[0]).absolute(),
17
- '__DIR__': Path('.').absolute()
20
+ '__DIR__': Path('.').absolute(),
21
+ '__HOME__': Path.home(),
22
+ '__USER__': getpass.getuser()
18
23
  }
19
24
 
20
-
21
25
  def make_history():
22
26
  """Make the history folder if missing."""
23
27
  history_dir = PATH['__DIR__'].joinpath('history')
@@ -56,8 +60,7 @@ def make_config(overwrite: bool):
56
60
  if str(target).endswith('service'):
57
61
  rd_bytes = config_file.read_bytes()
58
62
  for k, v in PATH.items():
59
- rd_bytes = rd_bytes.replace(k.encode(),
60
- str(v.absolute()).encode())
63
+ rd_bytes = rd_bytes.replace(k.encode(),str(v).encode())
61
64
  target.write_bytes(rd_bytes)
62
65
  else:
63
66
  target.write_bytes(config_file.read_bytes())
@@ -68,7 +71,7 @@ def read_with_subst(file: Path):
68
71
  rd = file.read_bytes().decode()
69
72
  if str(file).endswith('service'):
70
73
  for k, v in PATH.items():
71
- rd = rd.replace(k, str(v.absolute()))
74
+ rd = rd.replace(k, str(v))
72
75
  lines = rd.splitlines()
73
76
  return lines
74
77
 
@@ -94,7 +97,7 @@ def compare_config():
94
97
 
95
98
  def checkout(overwrite=False, diff=False):
96
99
  """Do it."""
97
- for name in PATH:
100
+ for name in ['__PYTHON__', '__PYMSCADA__', '__DIR__', '__HOME__']:
98
101
  if not PATH[name].exists():
99
102
  raise SystemExit(f'{PATH[name]} is missing')
100
103
  if diff:
@@ -8,7 +8,7 @@ try:
8
8
  import termios
9
9
  import tty
10
10
  except ModuleNotFoundError:
11
- logging.warning("no termios, don't use console")
11
+ logging.error('console not available')
12
12
 
13
13
 
14
14
  class EC:
@@ -1,6 +1,6 @@
1
1
  bus_ip: 127.0.0.1
2
2
  bus_port: 1324
3
- path: /home/mscada/pymscada
3
+ path: __HOME__/pymscada
4
4
  files:
5
5
  - path: pdf/one.pdf
6
6
  desc: PDF
@@ -1,3 +1,3 @@
1
1
  bus_ip: 127.0.0.1
2
2
  bus_port: 1324
3
- path: history
3
+ path: __HOME__/history
@@ -1,5 +1,5 @@
1
1
  bus_ip: 127.0.0.1
2
2
  bus_port: 1324
3
3
  rta_tag: __opnotes__
4
- db: /home/mscada/pymscada/pymscada.sqlite
4
+ db: __HOME__/pymscada/pymscada.sqlite
5
5
  table: opnotes
@@ -6,8 +6,8 @@
6
6
  ExecStart=__PYMSCADA__ bus --config __DIR__/config/bus.yaml
7
7
  Restart=always
8
8
  RestartSec=5
9
- User=mscada
10
- Group=mscada
9
+ User=__USER__
10
+ Group=__USER__
11
11
 
12
12
  [Install]
13
13
  WantedBy=multi-user.target
@@ -8,8 +8,8 @@
8
8
  ExecStart=__PYTHON__ __DIR__/config/modbus_plc.py
9
9
  Restart=always
10
10
  RestartSec=5
11
- User=mscada
12
- Group=mscada
11
+ User=__USER__
12
+ Group=__USER__
13
13
 
14
14
  [Install]
15
15
  WantedBy=multi-user.target
@@ -8,8 +8,8 @@
8
8
  ExecStart=__PYMSCADA__ files --config __DIR__/config/files.yaml
9
9
  Restart=always
10
10
  RestartSec=5
11
- User=mscada
12
- Group=mscada
11
+ User=__USER__
12
+ Group=__USER__
13
13
 
14
14
  [Install]
15
15
  WantedBy=multi-user.target
@@ -8,8 +8,8 @@
8
8
  ExecStart=__PYMSCADA__ history --config __DIR__/config/history.yaml --tags __DIR__/config/tags.yaml
9
9
  Restart=always
10
10
  RestartSec=5
11
- User=mscada
12
- Group=mscada
11
+ User=__USER__
12
+ Group=__USER__
13
13
  KillSignal=SIGINT
14
14
 
15
15
  [Install]
@@ -8,8 +8,8 @@
8
8
  ExecStart=__PYMSCADA__ logixclient --config __DIR__/config/logixclient.yaml
9
9
  Restart=always
10
10
  RestartSec=5
11
- User=mscada
12
- Group=mscada
11
+ User=__USER__
12
+ Group=__USER__
13
13
 
14
14
  [Install]
15
15
  WantedBy=multi-user.target
@@ -8,8 +8,8 @@
8
8
  ExecStart=__PYMSCADA__ modbusclient --config __DIR__/config/modbusclient.yaml
9
9
  Restart=always
10
10
  RestartSec=5
11
- User=mscada
12
- Group=mscada
11
+ User=__USER__
12
+ Group=__USER__
13
13
 
14
14
  [Install]
15
15
  WantedBy=multi-user.target
@@ -8,8 +8,8 @@
8
8
  ExecStart=__PYMSCADA__ modbusserver --config __DIR__/config/modbusserver.yaml
9
9
  Restart=always
10
10
  RestartSec=5
11
- User=mscada
12
- Group=mscada
11
+ User=__USER__
12
+ Group=__USER__
13
13
 
14
14
  [Install]
15
15
  WantedBy=multi-user.target
@@ -8,8 +8,8 @@
8
8
  ExecStart=__PYMSCADA__ ping --config __DIR__/config/ping.yaml
9
9
  Restart=always
10
10
  RestartSec=5
11
- User=mscada
12
- Group=mscada
11
+ User=__USER__
12
+ Group=__USER__
13
13
 
14
14
  [Install]
15
15
  WantedBy=multi-user.target
@@ -8,8 +8,8 @@
8
8
  ExecStart=__PYMSCADA__ snmpclient --config __DIR__/config/snmpclient.yaml
9
9
  Restart=always
10
10
  RestartSec=5
11
- User=mscada
12
- Group=mscada
11
+ User=__USER__
12
+ Group=__USER__
13
13
 
14
14
  [Install]
15
15
  WantedBy=multi-user.target
@@ -8,8 +8,8 @@
8
8
  ExecStart=__PYMSCADA__ opnotes --config __DIR__/config/opnotes.yaml
9
9
  Restart=always
10
10
  RestartSec=5
11
- User=mscada
12
- Group=mscada
11
+ User=__USER__
12
+ Group=__USER__
13
13
  KillSignal=SIGINT
14
14
 
15
15
  [Install]
@@ -8,8 +8,8 @@
8
8
  ExecStart=__PYMSCADA__ wwwserver --config __DIR__/config/wwwserver.yaml --tags __DIR__/config/tags.yaml
9
9
  Restart=always
10
10
  RestartSec=5
11
- User=mscada
12
- Group=mscada
11
+ User=__USER__
12
+ Group=__USER__
13
13
 
14
14
  [Install]
15
15
  WantedBy=multi-user.target
@@ -2,8 +2,8 @@ bus_ip: 127.0.0.1
2
2
  bus_port: 1324
3
3
  ip: 0.0.0.0
4
4
  port: 8324
5
- get_path: /home/mscada/Documents/angmscada/dist/angmscada
6
- serve_path: /home/mscada/pymscada
5
+ get_path: __HOME__/angmscada/dist/angmscada
6
+ serve_path: __HOME__/pymscada
7
7
  pages:
8
8
  - name: Default Main
9
9
  parent:
@@ -35,6 +35,12 @@ pages:
35
35
  - Four
36
36
  - Five
37
37
  - {tagname: FloatSelect, type: selectdict, opts: {type: float, dp: 2}}
38
+ - name: Notes
39
+ parent:
40
+ items:
41
+ - type: opnote
42
+ site: [Site 1, Site 2]
43
+ by: [B1, B2]
38
44
  - name: Trends
39
45
  parent: Dropdown
40
46
  items:
@@ -81,7 +87,7 @@ pages:
81
87
  - name: Files
82
88
  parent: Dropdown
83
89
  items:
84
- - {tagname: __files__, type: files}
90
+ - type: files
85
91
  - name: Temperature
86
92
  parent: Weather
87
93
  items:
@@ -1,9 +1,12 @@
1
1
  """Poll SNMP OIDs from devices."""
2
2
  import logging
3
- import pysnmp.hlapi.asyncio as snmp
4
3
  from pymscada.bus_client import BusClient
5
4
  from pymscada.periodic import Periodic
6
5
  from pymscada.iodrivers.snmp_map import SnmpMaps
6
+ try:
7
+ import pysnmp.hlapi.asyncio as snmp
8
+ except ModuleNotFoundError:
9
+ logging.error('snmp_client not available.')
7
10
 
8
11
 
9
12
  class SnmpClientConnector:
@@ -27,15 +27,16 @@ class OpNotes:
27
27
  self.cursor = self.connection.cursor()
28
28
  query = (
29
29
  'CREATE TABLE IF NOT EXISTS ' + self.table +
30
- '(oid INTEGER PRIMARY KEY ASC, '
31
- 'datetime INTEGER, '
30
+ '(id INTEGER PRIMARY KEY ASC, '
31
+ 'date_ms INTEGER, '
32
32
  'site TEXT, '
33
- 'operator TEXT, '
33
+ 'by TEXT, '
34
34
  'note TEXT)'
35
35
  )
36
36
  self.cursor.execute(query)
37
37
  self.busclient = BusClient(bus_ip, bus_port, module='OpNotes')
38
38
  self.rta = Tag(rta_tag, dict)
39
+ self.rta.value = {}
39
40
  self.busclient.add_callback_rta(rta_tag, self.rta_cb)
40
41
 
41
42
  def rta_cb(self, request):
@@ -44,64 +45,78 @@ class OpNotes:
44
45
  logging.warning(f'rta_cb malformed {request}')
45
46
  elif request['action'] == 'ADD':
46
47
  try:
48
+ logging.info(f'add {request}')
47
49
  with self.connection:
48
50
  self.cursor.execute(
49
- f'INSERT INTO {self.table} (datetime, site, operator, '
50
- 'note) VALUES(:datetime, :site, :operator, :note) '
51
- 'RETURNING *;',
51
+ f'INSERT INTO {self.table} (date_ms, site, by, note) '
52
+ 'VALUES(:date_ms, :site, :by, :note) RETURNING *;',
52
53
  request)
53
54
  res = self.cursor.fetchone()
54
55
  self.rta.value = {
55
- 'oid': res[0],
56
- 'datetime': res[1],
56
+ 'id': res[0],
57
+ 'date_ms': res[1],
57
58
  'site': res[2],
58
- 'operator': res[3],
59
+ 'by': res[3],
59
60
  'note': res[4]
60
61
  }
61
62
  except sqlite3.IntegrityError as error:
62
63
  logging.warning(f'OpNotes rta_cb {error}')
63
64
  elif request['action'] == 'MODIFY':
64
65
  try:
66
+ logging.info(f'modify {request}')
65
67
  with self.connection:
66
68
  self.cursor.execute(
67
- f'REPLACE INTO {self.table} VALUES(:oid, :datetime, '
68
- ':site, :operator, :note) RETURNING *;', request)
69
+ f'REPLACE INTO {self.table} VALUES(:id, :date_ms, '
70
+ ':site, :by, :note) RETURNING *;', request)
69
71
  res = self.cursor.fetchone()
70
72
  self.rta.value = {
71
- 'oid': res[0],
72
- 'datetime': res[1],
73
+ 'id': res[0],
74
+ 'date_ms': res[1],
73
75
  'site': res[2],
74
- 'operator': res[3],
76
+ 'by': res[3],
75
77
  'note': res[4]
76
78
  }
77
79
  except sqlite3.IntegrityError as error:
78
80
  logging.warning(f'OpNotes rta_cb {error}')
79
81
  elif request['action'] == 'DELETE':
80
82
  try:
83
+ logging.info(f'delete {request}')
81
84
  with self.connection:
82
85
  self.cursor.execute(
83
- f'DELETE FROM {self.table} WHERE oid = :oid;', request)
84
- self.rta.value = {'oid': request['oid']}
86
+ f'DELETE FROM {self.table} WHERE id = :id;', request)
87
+ self.rta.value = {'id': request['id']}
85
88
  except sqlite3.IntegrityError as error:
86
89
  logging.warning(f'OpNotes rta_cb {error}')
87
90
  elif request['action'] == 'HISTORY':
88
- tag = Tag(request['reply_tag'], dict)
89
91
  try:
92
+ logging.info(f'history {request}')
90
93
  with self.connection:
91
94
  self.cursor.execute(
92
- f'SELECT * FROM {self.table} WHERE datetime < '
93
- ':datetime ORDER BY ABS(datetime - :datetime) '
94
- 'LIMIT 2;', request)
95
+ f'SELECT * FROM {self.table} WHERE date_ms > :date_ms '
96
+ 'ORDER BY (date_ms - :date_ms);', request)
95
97
  for res in self.cursor.fetchall():
96
- tag.value = {
97
- 'oid': res[0],
98
- 'datetime': res[1],
98
+ self.rta.value = {
99
+ '__rta_id__': request['__rta_id__'],
100
+ 'id': res[0],
101
+ 'date_ms': res[1],
99
102
  'site': res[2],
100
- 'operator': res[3],
103
+ 'by': res[3],
101
104
  'note': res[4]
102
105
  }
103
106
  except sqlite3.IntegrityError as error:
104
107
  logging.warning(f'OpNotes rta_cb {error}')
108
+ elif request['action'] == 'BULK HISTORY':
109
+ try:
110
+ logging.info(f'bulk history {request}')
111
+ with self.connection:
112
+ self.cursor.execute(
113
+ f'SELECT * FROM {self.table} WHERE date_ms > :date_ms '
114
+ 'ORDER BY -date_ms;', request)
115
+ results = list(self.cursor.fetchall())
116
+ self.rta.value = {'__rta_id__': request['__rta_id__'],
117
+ 'data': results}
118
+ except sqlite3.IntegrityError as error:
119
+ logging.warning(f'OpNotes rta_cb {error}')
105
120
 
106
121
  async def start(self):
107
122
  """Async startup."""
@@ -31,7 +31,7 @@ class WSHandler():
31
31
  This are transitory, lasting for a given web browser client.
32
32
  """
33
33
 
34
- ids = set(range(1, 100))
34
+ ids = set(range(1, 1000))
35
35
 
36
36
  def __init__(self, ws: web.WebSocketResponse, pages: dict,
37
37
  tag_info: dict[str, Tag], do_rta, interface: Interface):
@@ -57,8 +57,10 @@ class WSHandler():
57
57
  while True:
58
58
  as_bytes, message = await self.queue.get()
59
59
  if as_bytes:
60
+ # logging.debug(f'{self.rta_id} as bytes {message}')
60
61
  await self.ws.send_bytes(message)
61
62
  else:
63
+ # logging.debug(f'{self.rta_id} as json {message}')
62
64
  await self.ws.send_json(message)
63
65
  except asyncio.CancelledError:
64
66
  logging.warn(f'{self.rta_id}: send queue error, close '
@@ -103,7 +105,7 @@ class WSHandler():
103
105
  tag.time_us, # Uint64
104
106
  asbytes # Char as needed
105
107
  )))
106
- elif tag.type == bytes:
108
+ elif tag.type is bytes:
107
109
  rta_id = unpack_from('>H', tag.value)[0]
108
110
  if rta_id in [0, self.rta_id]:
109
111
  self.queue.put_nowait((True, pack(
@@ -115,7 +117,19 @@ class WSHandler():
115
117
  )))
116
118
  else:
117
119
  logging.info(f'{self.rta_id}: {tag.name} bytes mismatch id')
118
- elif tag.type in [dict, list]:
120
+ elif tag.type is list:
121
+ self.queue.put_nowait((False, {
122
+ 'type': 'tag',
123
+ 'payload': {
124
+ 'tagid': tag.id,
125
+ 'time_us': tag.time_us,
126
+ 'value': tag.value
127
+ }
128
+ }))
129
+ elif tag.type is dict:
130
+ if '__rta_id__' in tag.value:
131
+ if tag.value['__rta_id__'] != self.rta_id:
132
+ return
119
133
  self.queue.put_nowait((False, {
120
134
  'type': 'tag',
121
135
  'payload': {
@@ -174,8 +188,8 @@ class WSHandler():
174
188
  value['_file_data'] = file.data
175
189
  value['__rta_id__'] = self.rta_id
176
190
  self.do_rta(tagname, value)
177
- elif action == 'request_to_author':
178
- self.interface.ask(command)
191
+ # elif action == 'request_to_author':
192
+ # self.interface.ask(command)
179
193
  elif action == 'sub': # pc.CMD_SUB
180
194
  self.do_sub(tagname)
181
195
  elif action == 'get': # pc.CMD_GET
@@ -33,24 +33,24 @@ def test_db_and_tag(opnotes_db, opnotes_tag):
33
33
  tag = opnotes_tag # OpNotes sets the tag value for www clients.
34
34
  record = {
35
35
  'action': 'ADD',
36
- 'oid': 15,
36
+ 'id': 15,
37
37
  'site': 'Aniwhenua',
38
- 'operator': 'Jamie Walton',
39
- 'datetime': 1234567890123,
38
+ 'by': 'Jamie Walton',
39
+ 'date': 1234567890123,
40
40
  'note': 'Note °±²³😖.'
41
41
  }
42
42
  db.rta_cb(record)
43
- assert tag.value['oid'] == 1
43
+ assert tag.value['id'] == 1
44
44
  assert tag.value['note'] == 'Note °±²³😖.'
45
- record['oid'] = tag.value['oid']
45
+ record['id'] = tag.value['id']
46
46
  record['action'] = 'MODIFY'
47
47
  record['note'] = 'hi'
48
48
  db.rta_cb(record)
49
- assert tag.value['oid'] == 1
49
+ assert tag.value['id'] == 1
50
50
  assert tag.value['note'] == 'hi'
51
51
  record['action'] = 'DELETE'
52
52
  db.rta_cb(record)
53
- assert tag.value == {'oid': 1}
53
+ assert tag.value == {'id': 1}
54
54
 
55
55
 
56
56
  def test_history_queries(opnotes_db, opnotes_tag, reply_tag):
@@ -70,20 +70,20 @@ def test_history_queries(opnotes_db, opnotes_tag, reply_tag):
70
70
  o_tag.add_callback(o_cb, 999)
71
71
  r_tag.add_callback(r_cb, 999)
72
72
  record = {'action': 'ADD',
73
- 'datetime': 12345,
73
+ 'date': 12345,
74
74
  'site': 'Site',
75
- 'operator': 'Me',
75
+ 'by': 'Me',
76
76
  'note': 'hi'}
77
77
  for i in range(10):
78
- record['datetime'] -= 1
79
- db.rta_cb(record) # oid 1-10
80
- assert o_values[9]['oid'] == 10
78
+ record['date'] -= 1
79
+ db.rta_cb(record) # id 1-10
80
+ assert o_values[9]['id'] == 10
81
81
  rq = {'action': 'HISTORY',
82
- 'datetime': 12345 - 3,
82
+ 'date': 12345 - 3,
83
83
  'reply_tag': '__wwwserver__'}
84
84
  db.rta_cb(rq)
85
- assert r_values[1]['datetime'] == 12340
86
- for i in range(1, 11): # sqlite3 oid counts from 1
87
- rq = {'action': 'DELETE', 'oid': i}
85
+ assert r_values[1]['date'] == 12340
86
+ for i in range(1, 11): # sqlite3 id counts from 1
87
+ rq = {'action': 'DELETE', 'id': i}
88
88
  db.rta_cb(rq)
89
- o_values[19] == {'oid': 10}
89
+ o_values[19] == {'id': 10}
pymscada-0.1.2/PKG-INFO DELETED
@@ -1,88 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: pymscada
3
- Version: 0.1.2
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
- ```
pymscada-0.1.2/README.md DELETED
@@ -1,67 +0,0 @@
1
- # pymscada
2
- #### [Docs](https://github.com/jamie0walton/pymscada/blob/main/docs/README.md)
3
-
4
- #### [@Github](https://github.com/jamie0walton/pymscada/blob/main/README.md)
5
-
6
- ## Python Mobile SCADA
7
-
8
- ```pymscada``` read / write to Modbus and Logix PLCs. Read SNMP OIDs.
9
- Collect history values and provide the ability to set values and trends
10
- and issue commands.
11
-
12
- User interface is via a web client embedded in this package. Examples included
13
- for securing with Apache as a proxy.
14
-
15
- Configuration with text yaml files, including the web page which are
16
- procedurally built.
17
-
18
- # See also
19
-
20
- - The angular project [angmscada](https://github.com/jamie0walton/angmscada)
21
- - Python container for the compiled angular pages [pymscada-html](https://github.com/jamie0walton/pymscada-html)
22
-
23
- # Licence
24
-
25
- ```pymscada``` is distributed under the GPLv3 [license](./LICENSE).
26
-
27
- # Use
28
- Checkout the example files.
29
- ```bash
30
- mscada@raspberrypi:~/test $ pymscada checkout
31
- making 'history' folder
32
- making pdf dir
33
- making config dir
34
- Creating /home/mscada/test/config/modbusclient.yaml
35
- Creating /home/mscada/test/config/pymscada-history.service
36
- Creating /home/mscada/test/config/wwwserver.yaml
37
- Creating /home/mscada/test/config/pymscada-demo-modbus_plc.service
38
- Creating /home/mscada/test/config/files.yaml
39
- Creating /home/mscada/test/config/pymscada-modbusserver.service
40
- Creating /home/mscada/test/config/pymscada-wwwserver.service
41
- Creating /home/mscada/test/config/simulate.yaml
42
- Creating /home/mscada/test/config/tags.yaml
43
- Creating /home/mscada/test/config/history.yaml
44
- Creating /home/mscada/test/config/pymscada-files.service
45
- Creating /home/mscada/test/config/bus.yaml
46
- Creating /home/mscada/test/config/modbusserver.yaml
47
- Creating /home/mscada/test/config/modbus_plc.py
48
- Creating /home/mscada/test/config/pymscada-modbusclient.service
49
- Creating /home/mscada/test/config/pymscada-bus.service
50
- Creating /home/mscada/test/config/README.md
51
- mscada@raspberrypi:~/test $ pymscada validate
52
- WARNING:root:pymscada 0.1.0 starting
53
- Config files in ./ valid.
54
- ```
55
-
56
- Runs on a Raspberry Pi and includes preconfigured systemd files to
57
- automate running the services. Mostly works on Windows, works better
58
- on linux.
59
-
60
- Modules can be run from the command line, although you need
61
- a terminal for each running module (better with systemd).
62
- ```bash
63
- pymscada bus --config bus.yaml
64
- pymscada wwwserver --config wwwserver.yaml --tags tags.yaml
65
- pymscada history --config history.yaml --tags tags.yaml
66
- python weather.py
67
- ```
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes