pymscada 0.0.13__tar.gz → 0.0.15__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.
- {pymscada-0.0.13 → pymscada-0.0.15}/PKG-INFO +27 -7
- {pymscada-0.0.13 → pymscada-0.0.15}/README.md +25 -6
- {pymscada-0.0.13 → pymscada-0.0.15}/pyproject.toml +2 -1
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/demo/logixclient.yaml +21 -16
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/demo/pymscada-io-logixclient.service +2 -2
- pymscada-0.0.15/src/pymscada/demo/pymscada-io-snmpclient.service +15 -0
- pymscada-0.0.15/src/pymscada/demo/snmpclient.yaml +73 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/demo/tags.yaml +48 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/iodrivers/logix_map.py +61 -46
- pymscada-0.0.15/src/pymscada/iodrivers/snmp_client.py +75 -0
- pymscada-0.0.15/src/pymscada/iodrivers/snmp_client2.py +53 -0
- pymscada-0.0.15/src/pymscada/iodrivers/snmp_map.py +71 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/main.py +5 -0
- pymscada-0.0.15/src/pymscada/tools/walk.py +55 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/LICENSE +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/__init__.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/__main__.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/bus_client.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/bus_server.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/checkout.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/config.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/console.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/demo/README.md +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/demo/__init__.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/demo/bus.yaml +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/demo/files.yaml +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/demo/history.yaml +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/demo/modbus_plc.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/demo/modbusclient.yaml +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/demo/modbusserver.yaml +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/demo/pymscada-bus.service +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/demo/pymscada-demo-modbus_plc.service +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/demo/pymscada-files.service +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/demo/pymscada-history.service +0 -0
- /pymscada-0.0.13/src/pymscada/demo/pymscada-modbusclient.service → /pymscada-0.0.15/src/pymscada/demo/pymscada-io-modbusclient.service +0 -0
- /pymscada-0.0.13/src/pymscada/demo/pymscada-modbusserver.service → /pymscada-0.0.15/src/pymscada/demo/pymscada-io-modbusserver.service +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/demo/pymscada-wwwserver.service +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/demo/simulate.yaml +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/demo/wwwserver.yaml +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/files.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/history.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/iodrivers/__init__.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/iodrivers/logix_client.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/iodrivers/modbus_client.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/iodrivers/modbus_map.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/iodrivers/modbus_server.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/misc.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/pdf/__init__.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/pdf/one.pdf +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/pdf/two.pdf +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/periodic.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/protocol_constants.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/samplers.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/simulate.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/tag.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/validate.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/src/pymscada/www_server.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/tests/__init__.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/tests/bus_echo.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/tests/iodrivers/test_logix.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/tests/iodrivers/test_modbus.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/tests/test_assets/busserver.yaml +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/tests/test_assets/hist_tag_0_0.dat +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/tests/test_assets/hist_tag_0_10_2.dat +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/tests/test_assets/hist_tag_0_15.dat +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/tests/test_assets/hist_tag_0_26.dat +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/tests/test_assets/hist_tag_0_50.dat +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/tests/test_bus_server.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/tests/test_config.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/tests/test_misc.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/tests/test_periodic.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/tests/test_samplers.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/tests/test_tag.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/tests/test_tag_history.py +0 -0
- {pymscada-0.0.13 → pymscada-0.0.15}/tests/test_validate.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pymscada
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.15
|
|
4
4
|
Summary: Shared tag value SCADA with python backup and Angular UI
|
|
5
5
|
Author-Email: Jamie Walton <jamie@walton.net.nz>
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -16,6 +16,7 @@ Requires-Dist: aiohttp>=3.8.5
|
|
|
16
16
|
Requires-Dist: pymscada-html<=0.1.0
|
|
17
17
|
Requires-Dist: cerberus>=1.3.5
|
|
18
18
|
Requires-Dist: pycomm3>=1.2.14
|
|
19
|
+
Requires-Dist: pysnmplib>=5.0.24
|
|
19
20
|
Description-Content-Type: text/markdown
|
|
20
21
|
|
|
21
22
|
# pymscada
|
|
@@ -25,20 +26,39 @@ Description-Content-Type: text/markdown
|
|
|
25
26
|
|
|
26
27
|
## Python Mobile SCADA
|
|
27
28
|
|
|
29
|
+
This is an open source Python SCADA package intended for use over your
|
|
30
|
+
mobile phone. It has been developed to connect custom python scripts to
|
|
31
|
+
Modicon and Rockwell PLCs, and present a web based user interface, with
|
|
32
|
+
minimal coding. ```pymscada``` does expect you to be able to use python.
|
|
33
|
+
|
|
34
|
+
```pymscada``` shares values through a ```Tag``` for all data. Tag values
|
|
35
|
+
are updated by exception through a message bus. Changes are in any
|
|
36
|
+
direction with simple loops stopped with a simplistic _don't send updates
|
|
37
|
+
back to where they came from_ check. It's too simple to stop you defeating
|
|
38
|
+
it (but there are easier ways to break the code).
|
|
39
|
+
|
|
40
|
+
There are primary owner Tag's. A _request to set_ command is passed to the
|
|
41
|
+
author of the Tag, where it is updated. This allows database information
|
|
42
|
+
to pass through the Tag values as well. Tag values are typically float or
|
|
43
|
+
int however they may also be megabyte sized binary or dictionary values.
|
|
44
|
+
|
|
45
|
+
Example ```systemd``` service files, pymscada module config files and
|
|
46
|
+
custom script examples are included. The example scripts include polling
|
|
47
|
+
weather from tommorrow.io and a python script based Modbus TCP PLC.
|
|
48
|
+
|
|
49
|
+
## Introduction
|
|
50
|
+
|
|
28
51
|
This is a small SCADA package that will run on Linux (preferably) or
|
|
29
52
|
Windows. The server runs as several modules on the host, sharing
|
|
30
53
|
information through a message bus. A __subset__ of modules is:
|
|
31
54
|
|
|
32
55
|
- Bus server - shares tag values with by exception updates
|
|
33
56
|
- Modbus client - reads and writes to a PLC using Modbus/TCP
|
|
57
|
+
- Logix client - uses ```pycomm3``` to read / write to Rockwell PLCs
|
|
58
|
+
- SNMP client - polls SNMP OID values
|
|
34
59
|
- History - saves data changes, serves history to web pages
|
|
35
60
|
- Web server - serves web pages which connect with a web socket
|
|
36
|
-
- Web pages -
|
|
37
|
-
|
|
38
|
-
Web pages are responsive and defined procedurally from the
|
|
39
|
-
```wwwserver.yaml``` config file.
|
|
40
|
-
|
|
41
|
-
Trends use [uPlot](https://github.com/leeoniya/uPlot).
|
|
61
|
+
- Web pages - procedurally generated setpoint, indication and trends
|
|
42
62
|
|
|
43
63
|
## Objectives
|
|
44
64
|
|
|
@@ -5,20 +5,39 @@
|
|
|
5
5
|
|
|
6
6
|
## Python Mobile SCADA
|
|
7
7
|
|
|
8
|
+
This is an open source Python SCADA package intended for use over your
|
|
9
|
+
mobile phone. It has been developed to connect custom python scripts to
|
|
10
|
+
Modicon and Rockwell PLCs, and present a web based user interface, with
|
|
11
|
+
minimal coding. ```pymscada``` does expect you to be able to use python.
|
|
12
|
+
|
|
13
|
+
```pymscada``` shares values through a ```Tag``` for all data. Tag values
|
|
14
|
+
are updated by exception through a message bus. Changes are in any
|
|
15
|
+
direction with simple loops stopped with a simplistic _don't send updates
|
|
16
|
+
back to where they came from_ check. It's too simple to stop you defeating
|
|
17
|
+
it (but there are easier ways to break the code).
|
|
18
|
+
|
|
19
|
+
There are primary owner Tag's. A _request to set_ command is passed to the
|
|
20
|
+
author of the Tag, where it is updated. This allows database information
|
|
21
|
+
to pass through the Tag values as well. Tag values are typically float or
|
|
22
|
+
int however they may also be megabyte sized binary or dictionary values.
|
|
23
|
+
|
|
24
|
+
Example ```systemd``` service files, pymscada module config files and
|
|
25
|
+
custom script examples are included. The example scripts include polling
|
|
26
|
+
weather from tommorrow.io and a python script based Modbus TCP PLC.
|
|
27
|
+
|
|
28
|
+
## Introduction
|
|
29
|
+
|
|
8
30
|
This is a small SCADA package that will run on Linux (preferably) or
|
|
9
31
|
Windows. The server runs as several modules on the host, sharing
|
|
10
32
|
information through a message bus. A __subset__ of modules is:
|
|
11
33
|
|
|
12
34
|
- Bus server - shares tag values with by exception updates
|
|
13
35
|
- Modbus client - reads and writes to a PLC using Modbus/TCP
|
|
36
|
+
- Logix client - uses ```pycomm3``` to read / write to Rockwell PLCs
|
|
37
|
+
- SNMP client - polls SNMP OID values
|
|
14
38
|
- History - saves data changes, serves history to web pages
|
|
15
39
|
- Web server - serves web pages which connect with a web socket
|
|
16
|
-
- Web pages -
|
|
17
|
-
|
|
18
|
-
Web pages are responsive and defined procedurally from the
|
|
19
|
-
```wwwserver.yaml``` config file.
|
|
20
|
-
|
|
21
|
-
Trends use [uPlot](https://github.com/leeoniya/uPlot).
|
|
40
|
+
- Web pages - procedurally generated setpoint, indication and trends
|
|
22
41
|
|
|
23
42
|
## Objectives
|
|
24
43
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pymscada"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.15"
|
|
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" },
|
|
@@ -11,6 +11,7 @@ dependencies = [
|
|
|
11
11
|
"pymscada-html<=0.1.0",
|
|
12
12
|
"cerberus>=1.3.5",
|
|
13
13
|
"pycomm3>=1.2.14",
|
|
14
|
+
"pysnmplib>=5.0.24",
|
|
14
15
|
]
|
|
15
16
|
requires-python = ">=3.9"
|
|
16
17
|
readme = "README.md"
|
|
@@ -9,37 +9,42 @@ rtus:
|
|
|
9
9
|
- {addr: Iout, type: 'DINT[]', start: 0, end: 99}
|
|
10
10
|
- {addr: OutVar, type: REAL}
|
|
11
11
|
writeok:
|
|
12
|
-
- {addr: Fin, start: 0, end: 99}
|
|
13
|
-
- {addr: Iin, start: 0, end: 99}
|
|
12
|
+
- {addr: Fin, type: 'REAL[]', start: 0, end: 99}
|
|
13
|
+
- {addr: Iin, type: 'REAL[]', start: 0, end: 99}
|
|
14
14
|
- {addr: InVar, type: REAL}
|
|
15
15
|
tags:
|
|
16
|
+
Ani_Fin_20:
|
|
17
|
+
type: float32
|
|
18
|
+
read: 'Ani:Fout[20]'
|
|
19
|
+
write: 'Ani:Fin[20]'
|
|
16
20
|
Ani_Fout_20:
|
|
17
21
|
type: float32
|
|
18
22
|
addr: 'Ani:Fout[20]'
|
|
23
|
+
Ani_Iin_20:
|
|
24
|
+
type: int32
|
|
25
|
+
read: 'Ani:Iout[20]'
|
|
26
|
+
write: 'Ani:Iin[20]'
|
|
19
27
|
Ani_Iout_20:
|
|
20
28
|
type: int32
|
|
21
29
|
addr: 'Ani:Iout[20]'
|
|
30
|
+
Ani_Iin_21_0:
|
|
31
|
+
type: bool
|
|
32
|
+
read: 'Ani:Iout[21].0'
|
|
33
|
+
write: 'Ani:Iin[21].0'
|
|
22
34
|
Ani_Iout_21_0:
|
|
23
35
|
type: bool
|
|
24
36
|
addr: 'Ani:Iout[21].0'
|
|
37
|
+
Ani_Iin_21_1:
|
|
38
|
+
type: bool
|
|
39
|
+
read: 'Ani:Iout[21].1'
|
|
40
|
+
write: 'Ani:Iin[21].1'
|
|
25
41
|
Ani_Iout_21_1:
|
|
26
42
|
type: bool
|
|
27
43
|
addr: 'Ani:Iout[21].1'
|
|
28
|
-
Ani_Fin_20:
|
|
29
|
-
type: float32
|
|
30
|
-
addr: 'Ani:Fin[20]'
|
|
31
|
-
Ani_Iin_20:
|
|
32
|
-
type: int32
|
|
33
|
-
addr: 'Ani:Iin[20]'
|
|
34
44
|
InVar:
|
|
35
45
|
type: float32
|
|
36
|
-
|
|
46
|
+
read: Ani:OutVar
|
|
47
|
+
write: Ani:InVar
|
|
37
48
|
OutVar:
|
|
38
49
|
type: float32
|
|
39
|
-
addr: Ani:OutVar
|
|
40
|
-
Ani_Iin_21_0:
|
|
41
|
-
type: bool
|
|
42
|
-
addr: 'Ani:Iin[21].0'
|
|
43
|
-
Ani_Iin_21_1:
|
|
44
|
-
type: bool
|
|
45
|
-
addr: 'Ani:Iin[21].1'
|
|
50
|
+
addr: Ani:OutVar
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
After=pymscada-bus.service
|
|
5
5
|
|
|
6
6
|
[Service]
|
|
7
|
-
WorkingDirectory
|
|
8
|
-
ExecStart
|
|
7
|
+
WorkingDirectory=__DIR__
|
|
8
|
+
ExecStart=__PYMSCADA__ logixclient --config __DIR__/config/logixclient.yaml
|
|
9
9
|
Restart=always
|
|
10
10
|
RestartSec=5
|
|
11
11
|
User=mscada
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[Unit]
|
|
2
|
+
Description=pymscada - SNMP client
|
|
3
|
+
BindsTo=pymscada-bus.service
|
|
4
|
+
After=pymscada-bus.service
|
|
5
|
+
|
|
6
|
+
[Service]
|
|
7
|
+
WorkingDirectory=__DIR__
|
|
8
|
+
ExecStart=__PYMSCADA__ snmpclient --config __DIR__/config/snmpclient.yaml
|
|
9
|
+
Restart=always
|
|
10
|
+
RestartSec=5
|
|
11
|
+
User=mscada
|
|
12
|
+
Group=mscada
|
|
13
|
+
|
|
14
|
+
[Install]
|
|
15
|
+
WantedBy=multi-user.target
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
bus_ip: 127.0.0.1
|
|
2
|
+
bus_port: 1324
|
|
3
|
+
rtus:
|
|
4
|
+
- name: router
|
|
5
|
+
ip: 172.26.3.254
|
|
6
|
+
community: public
|
|
7
|
+
rate: 10
|
|
8
|
+
read:
|
|
9
|
+
- '1.3.6.1.2.1.31.1.1.1.6.1'
|
|
10
|
+
- '1.3.6.1.2.1.31.1.1.1.6.2'
|
|
11
|
+
- '1.3.6.1.2.1.31.1.1.1.6.3'
|
|
12
|
+
- '1.3.6.1.2.1.31.1.1.1.6.4'
|
|
13
|
+
- '1.3.6.1.2.1.31.1.1.1.6.5'
|
|
14
|
+
- '1.3.6.1.2.1.31.1.1.1.6.6'
|
|
15
|
+
- '1.3.6.1.2.1.31.1.1.1.6.7'
|
|
16
|
+
- '1.3.6.1.2.1.31.1.1.1.6.8'
|
|
17
|
+
- '1.3.6.1.2.1.31.1.1.1.10.1'
|
|
18
|
+
- '1.3.6.1.2.1.31.1.1.1.10.2'
|
|
19
|
+
- '1.3.6.1.2.1.31.1.1.1.10.3'
|
|
20
|
+
- '1.3.6.1.2.1.31.1.1.1.10.4'
|
|
21
|
+
- '1.3.6.1.2.1.31.1.1.1.10.5'
|
|
22
|
+
- '1.3.6.1.2.1.31.1.1.1.10.6'
|
|
23
|
+
- '1.3.6.1.2.1.31.1.1.1.10.7'
|
|
24
|
+
- '1.3.6.1.2.1.31.1.1.1.10.8'
|
|
25
|
+
tags:
|
|
26
|
+
Router_eth1_bytes_in:
|
|
27
|
+
type: int_roc
|
|
28
|
+
addr: 'router:1.3.6.1.2.1.31.1.1.1.6.1'
|
|
29
|
+
Router_eth1_bytes_out:
|
|
30
|
+
type: int_roc
|
|
31
|
+
addr: 'router:1.3.6.1.2.1.31.1.1.1.10.1'
|
|
32
|
+
Router_eth2_bytes_in:
|
|
33
|
+
type: int_roc
|
|
34
|
+
addr: 'router:1.3.6.1.2.1.31.1.1.1.6.2'
|
|
35
|
+
Router_eth2_bytes_out:
|
|
36
|
+
type: int_roc
|
|
37
|
+
addr: 'router:1.3.6.1.2.1.31.1.1.1.10.2'
|
|
38
|
+
Router_eth3_bytes_in:
|
|
39
|
+
type: int_roc
|
|
40
|
+
addr: 'router:1.3.6.1.2.1.31.1.1.1.6.3'
|
|
41
|
+
Router_eth3_bytes_out:
|
|
42
|
+
type: int_roc
|
|
43
|
+
addr: 'router:1.3.6.1.2.1.31.1.1.1.10.3'
|
|
44
|
+
Router_eth4_bytes_in:
|
|
45
|
+
type: int_roc
|
|
46
|
+
addr: 'router:1.3.6.1.2.1.31.1.1.1.6.4'
|
|
47
|
+
Router_eth4_bytes_out:
|
|
48
|
+
type: int_roc
|
|
49
|
+
addr: 'router:1.3.6.1.2.1.31.1.1.1.10.4'
|
|
50
|
+
Router_eth5_bytes_in:
|
|
51
|
+
type: int_roc
|
|
52
|
+
addr: 'router:1.3.6.1.2.1.31.1.1.1.6.5'
|
|
53
|
+
Router_eth5_bytes_out:
|
|
54
|
+
type: int_roc
|
|
55
|
+
addr: 'router:1.3.6.1.2.1.31.1.1.1.10.5'
|
|
56
|
+
Router_eth6_bytes_in:
|
|
57
|
+
type: int_roc
|
|
58
|
+
addr: 'router:1.3.6.1.2.1.31.1.1.1.6.6'
|
|
59
|
+
Router_eth6_bytes_out:
|
|
60
|
+
type: int_roc
|
|
61
|
+
addr: 'router:1.3.6.1.2.1.31.1.1.1.10.6'
|
|
62
|
+
Router_eth7_bytes_in:
|
|
63
|
+
type: int_roc
|
|
64
|
+
addr: 'router:1.3.6.1.2.1.31.1.1.1.6.7'
|
|
65
|
+
Router_eth7_bytes_out:
|
|
66
|
+
type: int_roc
|
|
67
|
+
addr: 'router:1.3.6.1.2.1.31.1.1.1.10.7'
|
|
68
|
+
Router_eth8_bytes_in:
|
|
69
|
+
type: int_roc
|
|
70
|
+
addr: 'router:1.3.6.1.2.1.31.1.1.1.6.8'
|
|
71
|
+
Router_eth8_bytes_out:
|
|
72
|
+
type: int_roc
|
|
73
|
+
addr: 'router:1.3.6.1.2.1.31.1.1.1.10.8'
|
|
@@ -165,4 +165,52 @@ sim_DateTimeSet:
|
|
|
165
165
|
type: int
|
|
166
166
|
sim_DateTimeVal:
|
|
167
167
|
desc: Simulation tag
|
|
168
|
+
type: int
|
|
169
|
+
Router_eth1_bytes_in:
|
|
170
|
+
desc: eth1 bytes in
|
|
171
|
+
type: int
|
|
172
|
+
Router_eth1_bytes_out:
|
|
173
|
+
desc: eth1 bytes out
|
|
174
|
+
type: int
|
|
175
|
+
Router_eth2_bytes_in:
|
|
176
|
+
desc: eth2 bytes in
|
|
177
|
+
type: int
|
|
178
|
+
Router_eth2_bytes_out:
|
|
179
|
+
desc: eth2 bytes out
|
|
180
|
+
type: int
|
|
181
|
+
Router_eth3_bytes_in:
|
|
182
|
+
desc: eth3 bytes in
|
|
183
|
+
type: int
|
|
184
|
+
Router_eth3_bytes_out:
|
|
185
|
+
desc: eth3 bytes out
|
|
186
|
+
type: int
|
|
187
|
+
Router_eth4_bytes_in:
|
|
188
|
+
desc: eth4 bytes in
|
|
189
|
+
type: int
|
|
190
|
+
Router_eth4_bytes_out:
|
|
191
|
+
desc: eth4 bytes out
|
|
192
|
+
type: int
|
|
193
|
+
Router_eth5_bytes_in:
|
|
194
|
+
desc: eth5 bytes in
|
|
195
|
+
type: int
|
|
196
|
+
Router_eth5_bytes_out:
|
|
197
|
+
desc: eth5 bytes out
|
|
198
|
+
type: int
|
|
199
|
+
Router_eth6_bytes_in:
|
|
200
|
+
desc: eth6 bytes in
|
|
201
|
+
type: int
|
|
202
|
+
Router_eth6_bytes_out:
|
|
203
|
+
desc: eth6 bytes out
|
|
204
|
+
type: int
|
|
205
|
+
Router_eth7_bytes_in:
|
|
206
|
+
desc: eth7 bytes in
|
|
207
|
+
type: int
|
|
208
|
+
Router_eth7_bytes_out:
|
|
209
|
+
desc: eth7 bytes out
|
|
210
|
+
type: int
|
|
211
|
+
Router_eth8_bytes_in:
|
|
212
|
+
desc: eth8 bytes in
|
|
213
|
+
type: int
|
|
214
|
+
Router_eth8_bytes_out:
|
|
215
|
+
desc: eth8 bytes out
|
|
168
216
|
type: int
|
|
@@ -12,42 +12,51 @@ DTYPES = {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
|
|
15
|
+
def tag_split(plc_tag: str):
|
|
16
|
+
separator = plc_tag.find(':')
|
|
17
|
+
arr_start_loc = plc_tag.find('[')
|
|
18
|
+
arr_end_loc = plc_tag.find(']')
|
|
19
|
+
bit_loc = plc_tag.find('.')
|
|
20
|
+
plc = plc_tag[:separator]
|
|
21
|
+
if arr_start_loc == -1 and bit_loc == -1:
|
|
22
|
+
var = plc_tag[separator + 1:]
|
|
23
|
+
elm = None
|
|
24
|
+
bit = None
|
|
25
|
+
elif arr_start_loc == -1:
|
|
26
|
+
var = plc_tag[separator + 1:bit_loc]
|
|
27
|
+
elm = None
|
|
28
|
+
bit = int(plc_tag[bit_loc + 1:])
|
|
29
|
+
elif bit_loc == -1:
|
|
30
|
+
var = plc_tag[separator + 1:arr_start_loc]
|
|
31
|
+
elm = int(plc_tag[arr_start_loc + 1:arr_end_loc])
|
|
32
|
+
bit = None
|
|
33
|
+
else:
|
|
34
|
+
var = plc_tag[separator + 1:arr_start_loc]
|
|
35
|
+
elm = int(plc_tag[arr_start_loc + 1:arr_end_loc])
|
|
36
|
+
bit = int(plc_tag[bit_loc + 1:])
|
|
37
|
+
return plc, var, elm, bit
|
|
38
|
+
|
|
39
|
+
|
|
15
40
|
class LogixMap:
|
|
16
41
|
"""Do value updates for each tag."""
|
|
17
42
|
|
|
18
|
-
def __init__(self, tagname: str, src_type: str,
|
|
43
|
+
def __init__(self, tagname: str, src_type: str, read_tag: str,
|
|
44
|
+
write_tag: str):
|
|
19
45
|
"""Initialise modbus map and Tag."""
|
|
20
46
|
dtype, dmin, dmax = DTYPES[src_type][0:3]
|
|
21
47
|
self.tag = Tag(tagname, dtype)
|
|
22
48
|
self.map_bus = id(self)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
self.var = plc_tag[plc_loc + 1:]
|
|
30
|
-
self.elm = None
|
|
31
|
-
self.bit = None
|
|
32
|
-
elif arr_start_loc == -1:
|
|
33
|
-
self.var = plc_tag[plc_loc + 1:bit_loc]
|
|
34
|
-
self.elm = None
|
|
35
|
-
self.bit = int(plc_tag[bit_loc + 1:])
|
|
36
|
-
elif bit_loc == -1:
|
|
37
|
-
self.var = plc_tag[plc_loc + 1:arr_start_loc]
|
|
38
|
-
self.elm = int(plc_tag[arr_start_loc + 1:arr_end_loc])
|
|
39
|
-
self.bit = None
|
|
40
|
-
else:
|
|
41
|
-
self.var = plc_tag[plc_loc + 1:arr_start_loc]
|
|
42
|
-
self.elm = int(plc_tag[arr_start_loc + 1:arr_end_loc])
|
|
43
|
-
self.bit = int(plc_tag[bit_loc + 1:])
|
|
44
|
-
self.plc_tag = plc_tag
|
|
49
|
+
self.read_plc, self.read_var, self.read_elm, self.read_bit = \
|
|
50
|
+
tag_split(read_tag)
|
|
51
|
+
self.plc_read_tag = read_tag
|
|
52
|
+
self.write_plc, self.write_var, self.write_elm, self.write_bit = \
|
|
53
|
+
tag_split(write_tag)
|
|
54
|
+
self.plc_write_tag = write_tag
|
|
45
55
|
self.callback = None
|
|
46
56
|
if dmin is not None:
|
|
47
57
|
self.tag.value_min = dmin
|
|
48
58
|
if dmax is not None:
|
|
49
59
|
self.tag.value_max = dmax
|
|
50
|
-
self.write_cb = None # used?
|
|
51
60
|
|
|
52
61
|
def set_callback(self, callback):
|
|
53
62
|
"""Add tag callback interface."""
|
|
@@ -56,8 +65,8 @@ class LogixMap:
|
|
|
56
65
|
|
|
57
66
|
def set_tag_value(self, value, time_us):
|
|
58
67
|
"""Pass update from IO driver to tag value."""
|
|
59
|
-
if self.
|
|
60
|
-
if value & 1 << self.
|
|
68
|
+
if self.read_bit is not None:
|
|
69
|
+
if value & 1 << self.read_bit:
|
|
61
70
|
value = 1
|
|
62
71
|
else:
|
|
63
72
|
value = 0
|
|
@@ -66,18 +75,18 @@ class LogixMap:
|
|
|
66
75
|
|
|
67
76
|
def tag_value_changed(self, tag: Tag):
|
|
68
77
|
"""Pass update from tag value to IO driver."""
|
|
69
|
-
if self.
|
|
70
|
-
addr = self.
|
|
71
|
-
elif self.
|
|
72
|
-
addr = f'{self.
|
|
73
|
-
elif self.
|
|
74
|
-
addr = f'{self.
|
|
78
|
+
if self.write_elm is None and self.write_bit is None:
|
|
79
|
+
addr = self.write_var
|
|
80
|
+
elif self.write_elm is None:
|
|
81
|
+
addr = f'{self.write_var}.{self.write_bit}'
|
|
82
|
+
elif self.write_bit is None:
|
|
83
|
+
addr = f'{self.write_var}[{self.write_elm}]'
|
|
75
84
|
else:
|
|
76
|
-
addr = f'{self.
|
|
85
|
+
addr = f'{self.write_var}[{self.write_elm}].{self.write_bit}'
|
|
77
86
|
self.callback(addr, tag.value)
|
|
78
87
|
|
|
79
88
|
|
|
80
|
-
class LogixMaps
|
|
89
|
+
class LogixMaps:
|
|
81
90
|
"""Link tags with protocol connector."""
|
|
82
91
|
|
|
83
92
|
def __init__(self, tags: dict):
|
|
@@ -85,16 +94,21 @@ class LogixMaps():
|
|
|
85
94
|
# use the tagname to access the map.
|
|
86
95
|
self.tag_map: dict[str, LogixMap] = {}
|
|
87
96
|
# use the plc_name then variable name to access a list of maps.
|
|
88
|
-
self.
|
|
97
|
+
self.read_var_map: dict[str, dict[str, list[LogixMap]]] = {}
|
|
89
98
|
for tagname, v in tags.items():
|
|
90
|
-
addr
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
99
|
+
if 'addr' in v:
|
|
100
|
+
read_addr = v['addr']
|
|
101
|
+
write_addr = v['addr']
|
|
102
|
+
else:
|
|
103
|
+
read_addr = v['read']
|
|
104
|
+
write_addr = v['write']
|
|
105
|
+
map = LogixMap(tagname, v['type'], read_addr, write_addr)
|
|
106
|
+
if map.read_plc not in self.read_var_map:
|
|
107
|
+
self.read_var_map[map.read_plc] = {}
|
|
108
|
+
if map.read_var not in self.read_var_map[map.read_plc]:
|
|
95
109
|
# make a list so multiple bits can map to a word
|
|
96
|
-
self.
|
|
97
|
-
self.
|
|
110
|
+
self.read_var_map[map.read_plc][map.read_var] = []
|
|
111
|
+
self.read_var_map[map.read_plc][map.read_var].append(map)
|
|
98
112
|
self.tag_map[map.tag.name] = map
|
|
99
113
|
|
|
100
114
|
def add_write_callback(self, plcname, writeok, callback):
|
|
@@ -110,7 +124,8 @@ class LogixMaps():
|
|
|
110
124
|
# where the mapped tag uses a valid address, add callback to
|
|
111
125
|
# the connection writer
|
|
112
126
|
for map in self.tag_map.values():
|
|
113
|
-
if map.
|
|
127
|
+
if map.write_plc == plcname and \
|
|
128
|
+
(map.write_var, map.write_elm) in write_set:
|
|
114
129
|
map.set_callback(callback)
|
|
115
130
|
|
|
116
131
|
def polled_data(self, plcname, polls):
|
|
@@ -121,12 +136,12 @@ class LogixMaps():
|
|
|
121
136
|
logging.error(poll.error)
|
|
122
137
|
arr_start_loc = poll.tag.find('[')
|
|
123
138
|
if arr_start_loc == -1:
|
|
124
|
-
for map in self.
|
|
139
|
+
for map in self.read_var_map[plcname][poll.tag]:
|
|
125
140
|
map.set_tag_value(poll.value, time_us)
|
|
126
141
|
else:
|
|
127
142
|
var = poll.tag[:arr_start_loc]
|
|
128
143
|
elm = int(poll.tag[arr_start_loc + 1: -1])
|
|
129
|
-
for map in self.
|
|
130
|
-
elm_offset = map.
|
|
144
|
+
for map in self.read_var_map[plcname][var]:
|
|
145
|
+
elm_offset = map.read_elm - elm
|
|
131
146
|
if elm_offset > 0 and elm_offset < len(poll.value):
|
|
132
147
|
map.set_tag_value(poll.value[elm_offset], time_us)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import pysnmp.hlapi.asyncio as snmp
|
|
3
|
+
from pymscada.bus_client import BusClient
|
|
4
|
+
from pymscada.periodic import Periodic
|
|
5
|
+
from pymscada.iodrivers.snmp_map import SnmpMaps
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SnmpClientConnector:
|
|
9
|
+
"""Poll snmp devices, write and traps are not implemented."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, name: str, ip:str, rate: float, read: list,
|
|
12
|
+
community: str, mapping: SnmpMaps):
|
|
13
|
+
"""Set up polling client."""
|
|
14
|
+
self.snmp_name = name
|
|
15
|
+
self.ip = ip
|
|
16
|
+
self.community = community
|
|
17
|
+
self.read_oids = [snmp.ObjectType(snmp.ObjectIdentity(x))
|
|
18
|
+
for x in read]
|
|
19
|
+
self.mapping = mapping
|
|
20
|
+
self.periodic = Periodic(self.poll, rate)
|
|
21
|
+
self.snmp_engine = snmp.SnmpEngine()
|
|
22
|
+
|
|
23
|
+
async def poll(self):
|
|
24
|
+
"""Poll data."""
|
|
25
|
+
r = await snmp.getCmd(
|
|
26
|
+
self.snmp_engine,
|
|
27
|
+
snmp.CommunityData(self.community),
|
|
28
|
+
snmp.UdpTransportTarget((self.ip, 161)),
|
|
29
|
+
snmp.ContextData(),
|
|
30
|
+
*self.read_oids
|
|
31
|
+
)
|
|
32
|
+
errorIndication, errorStatus, errorIndex, varBinds = r
|
|
33
|
+
if errorIndication:
|
|
34
|
+
logging.error(errorIndication)
|
|
35
|
+
elif errorStatus:
|
|
36
|
+
logging.error('%s at %s' % (
|
|
37
|
+
errorStatus.prettyPrint(),
|
|
38
|
+
errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
|
|
39
|
+
else:
|
|
40
|
+
self.mapping.polled_data(self.snmp_name, varBinds)
|
|
41
|
+
|
|
42
|
+
async def start(self):
|
|
43
|
+
"""Start polling."""
|
|
44
|
+
await self.periodic.start()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class SnmpClient:
|
|
48
|
+
|
|
49
|
+
def __init__(self, bus_ip: str = '127.0.0.1', bus_port: int = 1324,
|
|
50
|
+
rtus: dict = {}, tags: dict = {}) -> None:
|
|
51
|
+
"""
|
|
52
|
+
Connect to bus on bus_ip:bus_port, connect to snmp devices.
|
|
53
|
+
|
|
54
|
+
Event loop must be running.
|
|
55
|
+
"""
|
|
56
|
+
self.busclient = None
|
|
57
|
+
if bus_ip is not None:
|
|
58
|
+
self.busclient = BusClient(bus_ip, bus_port)
|
|
59
|
+
self.mapping = SnmpMaps(tags)
|
|
60
|
+
self.connections: list[SnmpClientConnector] = []
|
|
61
|
+
for rtu in rtus:
|
|
62
|
+
connection = SnmpClientConnector(**rtu, mapping=self.mapping)
|
|
63
|
+
self.connections.append(connection)
|
|
64
|
+
|
|
65
|
+
async def _poll(self):
|
|
66
|
+
"""For testing."""
|
|
67
|
+
for connection in self.connections:
|
|
68
|
+
await connection.poll()
|
|
69
|
+
|
|
70
|
+
async def start(self):
|
|
71
|
+
"""Start bus connection and PLC polling."""
|
|
72
|
+
if self.busclient is not None:
|
|
73
|
+
await self.busclient.start()
|
|
74
|
+
for connection in self.connections:
|
|
75
|
+
await connection.start()
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from pysnmp.hlapi.asyncio import *
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
async def run():
|
|
6
|
+
base = [
|
|
7
|
+
'1.3.6.1.2.1.2.2.1.2.', # name
|
|
8
|
+
# '.1.3.6.1.2.1.2.2.1.4.', # mtu
|
|
9
|
+
# '.1.3.6.1.2.1.2.2.1.6.', # mac address
|
|
10
|
+
# '.1.3.6.1.2.1.2.2.1.7.', # admin status
|
|
11
|
+
# '.1.3.6.1.2.1.2.2.1.8.', # oper status
|
|
12
|
+
'1.3.6.1.2.1.31.1.1.1.6.', # bytes in
|
|
13
|
+
# '.1.3.6.1.2.1.31.1.1.1.7.', # packets in
|
|
14
|
+
# '.1.3.6.1.2.1.2.2.1.13.', # discards in
|
|
15
|
+
# '.1.3.6.1.2.1.2.2.1.14.', # errors in
|
|
16
|
+
'1.3.6.1.2.1.31.1.1.1.10.', # bytes out
|
|
17
|
+
# '.1.3.6.1.2.1.31.1.1.1.11.', # packets out
|
|
18
|
+
# '.1.3.6.1.2.1.2.2.1.19.', # discards out
|
|
19
|
+
# '.1.3.6.1.2.1.2.2.1.20.', # errors out
|
|
20
|
+
]
|
|
21
|
+
oids = []
|
|
22
|
+
for i in range(1, 9):
|
|
23
|
+
for b in base:
|
|
24
|
+
oids.append(ObjectType(ObjectIdentity(f'{b}{i}')))
|
|
25
|
+
ip_address = '172.26.3.254'
|
|
26
|
+
community = 'public'
|
|
27
|
+
|
|
28
|
+
snmp_engine = SnmpEngine()
|
|
29
|
+
|
|
30
|
+
r = await getCmd(
|
|
31
|
+
snmp_engine,
|
|
32
|
+
CommunityData(community),
|
|
33
|
+
UdpTransportTarget((ip_address, 161)),
|
|
34
|
+
ContextData(),
|
|
35
|
+
*oids
|
|
36
|
+
)
|
|
37
|
+
errorIndication, errorStatus, errorIndex, varBinds = r
|
|
38
|
+
if errorIndication:
|
|
39
|
+
print(errorIndication)
|
|
40
|
+
elif errorStatus:
|
|
41
|
+
print('%s at %s' % (
|
|
42
|
+
errorStatus.prettyPrint(),
|
|
43
|
+
errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
|
|
44
|
+
else:
|
|
45
|
+
for varBind in varBinds:
|
|
46
|
+
oid, value = varBind
|
|
47
|
+
print(str(oid), type(oid), str(value), type(value))
|
|
48
|
+
# snmp_engine.transportDispatcher.closeDispatcher()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
if __name__ == '__main__':
|
|
52
|
+
"""Starts with creating an event loop."""
|
|
53
|
+
asyncio.run(run())
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Map between snmp MIB and Tag."""
|
|
2
|
+
import logging
|
|
3
|
+
from time import time
|
|
4
|
+
from pymscada.tag import Tag
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# data types for MIBs
|
|
8
|
+
DTYPES = {
|
|
9
|
+
'int_roc': [int]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SnmpMap:
|
|
14
|
+
"""Do value updates for each tag."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, tagname: str, src_type: str, plc_tag: str):
|
|
17
|
+
"""initialise MIB map and Tag."""
|
|
18
|
+
dtype = DTYPES[src_type][0]
|
|
19
|
+
self.last_value = None
|
|
20
|
+
self.tag = Tag(tagname, dtype)
|
|
21
|
+
self.map_bus = id(self)
|
|
22
|
+
separator = plc_tag.find(':')
|
|
23
|
+
self.plc = plc_tag[:separator]
|
|
24
|
+
self.var = plc_tag[separator + 1:]
|
|
25
|
+
|
|
26
|
+
def set_tag_value(self, value, time_us):
|
|
27
|
+
"""Pass update from IO driver to tag value."""
|
|
28
|
+
vtype = type(value).__name__
|
|
29
|
+
if vtype == 'Counter64':
|
|
30
|
+
v = int(value)
|
|
31
|
+
if self.last_value is None:
|
|
32
|
+
self.last_value = v
|
|
33
|
+
return
|
|
34
|
+
d = v - self.last_value
|
|
35
|
+
self.last_value = v
|
|
36
|
+
if d < 0:
|
|
37
|
+
d += 2**64
|
|
38
|
+
if self.tag.value != d:
|
|
39
|
+
self.tag.value = d, time_us, self.map_bus
|
|
40
|
+
else:
|
|
41
|
+
logging.warning(
|
|
42
|
+
f'SnmpMap: {self.tag.name} {vtype} not implemented')
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class SnmpMaps:
|
|
46
|
+
"""Link tags with protocol connector."""
|
|
47
|
+
|
|
48
|
+
def __init__(self, tags: dict):
|
|
49
|
+
"""Collect maps based on a tag dictionary."""
|
|
50
|
+
# use the tagname to access the map.
|
|
51
|
+
self.tag_map: dict[str, SnmpMap] = {}
|
|
52
|
+
# use the plc_name then variable name to access a list of maps.
|
|
53
|
+
self.var_map: dict[str, dict[str, list[SnmpMap]]] = {}
|
|
54
|
+
for tagname, v in tags.items():
|
|
55
|
+
addr = v['addr']
|
|
56
|
+
map = SnmpMap(tagname, v['type'], addr)
|
|
57
|
+
if map.plc not in self.var_map:
|
|
58
|
+
self.var_map[map.plc] = {}
|
|
59
|
+
if map.var not in self.var_map[map.plc]:
|
|
60
|
+
# make a list so multiple bits can map to a word
|
|
61
|
+
self.var_map[map.plc][map.var] = []
|
|
62
|
+
self.var_map[map.plc][map.var].append(map)
|
|
63
|
+
self.tag_map[map.tag.name] = map
|
|
64
|
+
|
|
65
|
+
def polled_data(self, plcname, polls):
|
|
66
|
+
"""Pass updates read from the PLC to the tags."""
|
|
67
|
+
time_us = int(time() * 1e6)
|
|
68
|
+
for poll in polls:
|
|
69
|
+
oid, value = poll
|
|
70
|
+
for map in self.var_map[plcname][str(oid)]:
|
|
71
|
+
map.set_tag_value(value, time_us)
|
|
@@ -11,6 +11,7 @@ from pymscada.history import History
|
|
|
11
11
|
from pymscada.iodrivers.logix_client import LogixClient
|
|
12
12
|
from pymscada.iodrivers.modbus_client import ModbusClient
|
|
13
13
|
from pymscada.iodrivers.modbus_server import ModbusServer
|
|
14
|
+
from pymscada.iodrivers.snmp_client import SnmpClient
|
|
14
15
|
from pymscada.simulate import Simulate
|
|
15
16
|
from pymscada.www_server import WwwServer
|
|
16
17
|
from pymscada.validate import validate
|
|
@@ -26,6 +27,7 @@ def args():
|
|
|
26
27
|
commands = ['bus', 'console', 'wwwserver', 'history', 'files',
|
|
27
28
|
'logixclient',
|
|
28
29
|
'modbusserver', 'modbusclient',
|
|
30
|
+
'snmpclient',
|
|
29
31
|
'simulate', 'checkout',
|
|
30
32
|
'validate']
|
|
31
33
|
parser.add_argument('module', type=str, choices=commands, metavar='action',
|
|
@@ -77,6 +79,9 @@ async def run():
|
|
|
77
79
|
elif options.module == 'modbusserver':
|
|
78
80
|
config = Config(options.config)
|
|
79
81
|
module = ModbusServer(**config)
|
|
82
|
+
elif options.module == 'snmpclient':
|
|
83
|
+
config = Config(options.config)
|
|
84
|
+
module = SnmpClient(**config)
|
|
80
85
|
elif options.module == 'simulate':
|
|
81
86
|
config = Config(options.config)
|
|
82
87
|
tag_info = dict(Config(options.tags))
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Walk whole MIB
|
|
3
|
+
++++++++++++++
|
|
4
|
+
|
|
5
|
+
Send a series of SNMP GETNEXT requests using the following options:
|
|
6
|
+
|
|
7
|
+
* with SNMPv3, user 'usr-md5-none', MD5 authentication, no privacy
|
|
8
|
+
* over IPv4/UDP
|
|
9
|
+
* to an Agent at demo.snmplabs.com:161
|
|
10
|
+
* for all OIDs in IF-MIB
|
|
11
|
+
|
|
12
|
+
Functionally similar to:
|
|
13
|
+
|
|
14
|
+
| $ snmpwalk -v3 -lauthPriv -u usr-md5-none -A authkey1 -X privkey1 demo.snmplabs.com IF-MIB::
|
|
15
|
+
|
|
16
|
+
"""#
|
|
17
|
+
from pysnmp.hlapi import *
|
|
18
|
+
|
|
19
|
+
mibs = {
|
|
20
|
+
'MIKROTIK-MIB': '1.3.6.1.4.1.14988',
|
|
21
|
+
'MIB-2': '1.3.6.1.2.1',
|
|
22
|
+
'HOST-RESOURCES-MIB': '1.3.6.1.2.1.25',
|
|
23
|
+
'IF-MIB': '1.3.6.1.2.1.2',
|
|
24
|
+
'IP-MIB': '1.3.6.1.2.1.4',
|
|
25
|
+
'IP-FORWARD-MIB': '1.3.6.1.2.1.4.21',
|
|
26
|
+
'IPV6-MIB': '1.3.6.1.2.1.55',
|
|
27
|
+
'BRIDGE-MIB': '1.3.6.1.2.1.17',
|
|
28
|
+
'DHCP-SERVER-MIB': '1.3.6.1.4.1.14988.1.1.8',
|
|
29
|
+
'CISCO-AAA-SESSION-MIB': '1.3.6.1.4.1.9.9.39',
|
|
30
|
+
'ENTITY-MIB': '1.3.6.1.2.1.47',
|
|
31
|
+
'UPS-MIB': '1.3.6.1.2.1.33',
|
|
32
|
+
'SQUID-MIB': '1.3.6.1.4.1.3495',
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
iterator = nextCmd(
|
|
36
|
+
SnmpEngine(),
|
|
37
|
+
UsmUserData('public'),
|
|
38
|
+
UdpTransportTarget(('172.26.3.254', 161)),
|
|
39
|
+
ContextData(),
|
|
40
|
+
# ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.0')),
|
|
41
|
+
# ObjectType(ObjectIdentity('1.3.6.1.2.1.1.6.0'))
|
|
42
|
+
ObjectType(ObjectIdentity(mibs['MIB-2']))
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
for errorIndication, errorStatus, errorIndex, varBinds in iterator:
|
|
46
|
+
if errorIndication:
|
|
47
|
+
print(errorIndication)
|
|
48
|
+
break
|
|
49
|
+
elif errorStatus:
|
|
50
|
+
print('%s at %s' % (errorStatus.prettyPrint(),
|
|
51
|
+
errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
|
|
52
|
+
break
|
|
53
|
+
else:
|
|
54
|
+
for varBind in varBinds:
|
|
55
|
+
print(' = '.join([x.prettyPrint() for x in varBind]))
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|