pymscada 0.1.1a2__tar.gz → 0.1.2__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.1.1a2 → pymscada-0.1.2}/PKG-INFO +1 -1
- {pymscada-0.1.1a2 → pymscada-0.1.2}/pyproject.toml +1 -1
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/console.py +60 -44
- {pymscada-0.1.1a2 → pymscada-0.1.2}/LICENSE +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/README.md +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/__init__.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/__main__.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/bus_client.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/bus_server.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/checkout.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/config.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/README.md +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/__init__.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/bus.yaml +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/files.yaml +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/history.yaml +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/logixclient.yaml +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/modbus_plc.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/modbusclient.yaml +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/modbusserver.yaml +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/opnotes.yaml +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/ping.yaml +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/pymscada-bus.service +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/pymscada-demo-modbus_plc.service +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/pymscada-files.service +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/pymscada-history.service +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/pymscada-io-logixclient.service +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/pymscada-io-modbusclient.service +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/pymscada-io-modbusserver.service +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/pymscada-io-ping.service +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/pymscada-io-snmpclient.service +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/pymscada-opnotes.service +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/pymscada-wwwserver.service +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/snmpclient.yaml +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/tags.yaml +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/demo/wwwserver.yaml +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/files.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/history.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/iodrivers/__init__.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/iodrivers/logix_client.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/iodrivers/logix_map.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/iodrivers/modbus_client.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/iodrivers/modbus_map.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/iodrivers/modbus_server.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/iodrivers/ping_client.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/iodrivers/ping_map.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/iodrivers/snmp_client.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/iodrivers/snmp_map.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/main.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/misc.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/opnotes.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/pdf/__init__.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/pdf/one.pdf +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/pdf/two.pdf +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/periodic.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/protocol_constants.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/samplers.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/tag.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/tools/snmp_client2.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/tools/walk.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/validate.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/src/pymscada/www_server.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/tests/__init__.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/tests/bus_echo.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/tests/iodrivers/test_logix.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/tests/iodrivers/test_modbus.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/tests/test_assets/busserver.yaml +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/tests/test_assets/db.sqlite +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/tests/test_assets/hist_tag_0_0.dat +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/tests/test_assets/hist_tag_0_10_2.dat +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/tests/test_assets/hist_tag_0_15.dat +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/tests/test_assets/hist_tag_0_26.dat +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/tests/test_assets/hist_tag_0_50.dat +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/tests/test_bus_server.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/tests/test_config.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/tests/test_history.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/tests/test_misc.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/tests/test_opnotes.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/tests/test_periodic.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/tests/test_samplers.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/tests/test_tag.py +0 -0
- {pymscada-0.1.1a2 → pymscada-0.1.2}/tests/test_validate.py +0 -0
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
import asyncio
|
|
3
3
|
import logging
|
|
4
4
|
import sys
|
|
5
|
-
import termios
|
|
6
|
-
import tty
|
|
7
5
|
from pymscada.bus_client import BusClient
|
|
8
6
|
from pymscada.tag import Tag, tag_for_web
|
|
7
|
+
try:
|
|
8
|
+
import termios
|
|
9
|
+
import tty
|
|
10
|
+
except ModuleNotFoundError:
|
|
11
|
+
logging.warning("no termios, don't use console")
|
|
9
12
|
|
|
10
13
|
|
|
11
14
|
class EC:
|
|
@@ -22,12 +25,24 @@ class EC:
|
|
|
22
25
|
home = b'\x1b[H'
|
|
23
26
|
cr_clr = b'\x1b[2K\x1b[G' # clear line and move start
|
|
24
27
|
clear_line = b'\x1b[2K'
|
|
28
|
+
mv_start = b'\x1b[G'
|
|
25
29
|
mv_left = b'\x1b[1000D'
|
|
26
|
-
move_cursor_up = b'\x1b[1A'
|
|
27
|
-
insert_line = b'\x1b[1L'
|
|
28
30
|
|
|
29
31
|
|
|
30
|
-
class
|
|
32
|
+
class CustomHandler(logging.StreamHandler):
|
|
33
|
+
"""Control the cursor position."""
|
|
34
|
+
|
|
35
|
+
def emit(self, record):
|
|
36
|
+
"""Write to the console adding a carriage return."""
|
|
37
|
+
try:
|
|
38
|
+
msg = self.format(record)
|
|
39
|
+
self.stream.write(msg + '\r\n')
|
|
40
|
+
self.stream.flush()
|
|
41
|
+
except Exception:
|
|
42
|
+
self.handleError(record)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class KeypressProtocol(asyncio.Protocol):
|
|
31
46
|
"""Handle key presses, one at a time."""
|
|
32
47
|
|
|
33
48
|
def __init__(self, edit_line, process_command):
|
|
@@ -39,6 +54,7 @@ class KeypressReaderProtocol(asyncio.Protocol):
|
|
|
39
54
|
self.line = None # not editing a line (yet)
|
|
40
55
|
self.cursor = 0 # cursor position in line
|
|
41
56
|
self.stash = None # nothing stashed
|
|
57
|
+
self.connection_lost_future = asyncio.Future()
|
|
42
58
|
|
|
43
59
|
def data_received(self, data):
|
|
44
60
|
"""Got keypress, update edit line, send to writer."""
|
|
@@ -98,26 +114,9 @@ class KeypressReaderProtocol(asyncio.Protocol):
|
|
|
98
114
|
self.cursor = len(self.line)
|
|
99
115
|
self.edit_line(self.line, self.cursor)
|
|
100
116
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
def __init__(self):
|
|
106
|
-
"""Save terminal state and init stdin."""
|
|
107
|
-
self.fd = sys.stdin.fileno()
|
|
108
|
-
self.old_attr = termios.tcgetattr(self.fd)
|
|
109
|
-
tty.setraw(self.fd)
|
|
110
|
-
|
|
111
|
-
async def start_connection(self, edit_line, process):
|
|
112
|
-
"""Connect protocol."""
|
|
113
|
-
self.transport, self.protocol = \
|
|
114
|
-
await asyncio.get_event_loop().connect_read_pipe(
|
|
115
|
-
lambda: KeypressReaderProtocol(edit_line, process),
|
|
116
|
-
sys.stdin)
|
|
117
|
-
|
|
118
|
-
def __del__(self):
|
|
119
|
-
"""Reset the terminal."""
|
|
120
|
-
termios.tcsetattr(self.fd, termios.TCSADRAIN, self.old_attr)
|
|
117
|
+
def connection_lost(self, exc):
|
|
118
|
+
"""Let parent know protocol transport has disconnected."""
|
|
119
|
+
self.connection_lost_future.set_result(True)
|
|
121
120
|
|
|
122
121
|
|
|
123
122
|
class ConsoleWriter:
|
|
@@ -127,8 +126,6 @@ class ConsoleWriter:
|
|
|
127
126
|
"""Init."""
|
|
128
127
|
self.edit = None
|
|
129
128
|
self.cursor = 0
|
|
130
|
-
self.fd = sys.stdout.fileno()
|
|
131
|
-
self.old_attr = termios.tcgetattr(self.fd)
|
|
132
129
|
|
|
133
130
|
def write(self, data: bytes):
|
|
134
131
|
"""Stream writer, primarily for logging."""
|
|
@@ -154,10 +151,6 @@ class ConsoleWriter:
|
|
|
154
151
|
sys.stdout.buffer.write(ln)
|
|
155
152
|
sys.stdout.flush()
|
|
156
153
|
|
|
157
|
-
def __del__(self):
|
|
158
|
-
"""Reset the terminal."""
|
|
159
|
-
termios.tcsetattr(self.fd, termios.TCSADRAIN, self.old_attr)
|
|
160
|
-
|
|
161
154
|
|
|
162
155
|
class Console:
|
|
163
156
|
"""Provide a text console to interact with a Bus."""
|
|
@@ -169,15 +162,26 @@ class Console:
|
|
|
169
162
|
|
|
170
163
|
Event loop must be running.
|
|
171
164
|
"""
|
|
172
|
-
self.
|
|
165
|
+
self.fdin = sys.stdin.fileno()
|
|
166
|
+
self.fdout = sys.stdout.fileno()
|
|
167
|
+
self.fdin_attr = termios.tcgetattr(self.fdin)
|
|
168
|
+
self.fdout_attr = termios.tcgetattr(self.fdout)
|
|
169
|
+
tty.setraw(self.fdin)
|
|
173
170
|
self.writer = ConsoleWriter()
|
|
174
|
-
|
|
171
|
+
self.protocol = None
|
|
172
|
+
self.transport = None
|
|
173
|
+
# all to add '\r\n' to the logging output
|
|
174
|
+
logger = logging.getLogger()
|
|
175
|
+
handler = CustomHandler()
|
|
176
|
+
handler.setFormatter(logging.Formatter(
|
|
177
|
+
'%(levelname)s:%(name)s:%(message)s'))
|
|
178
|
+
logger.handlers.clear()
|
|
179
|
+
logger.addHandler(handler)
|
|
175
180
|
self.busclient = BusClient(bus_ip, bus_port, module='Console')
|
|
176
181
|
self.tags: dict[str, Tag] = {}
|
|
177
182
|
for tagname, tag in tag_info.items():
|
|
178
183
|
tag_for_web(tagname, tag)
|
|
179
184
|
self.tags[tagname] = Tag(tagname, tag['type'])
|
|
180
|
-
self.quit = asyncio.Event()
|
|
181
185
|
|
|
182
186
|
def write_tag(self, tag: Tag):
|
|
183
187
|
"""Append or insert tag value through writer."""
|
|
@@ -186,20 +190,24 @@ class Console:
|
|
|
186
190
|
|
|
187
191
|
def process(self, command: bytes):
|
|
188
192
|
"""Execute command."""
|
|
193
|
+
if command is None:
|
|
194
|
+
return
|
|
189
195
|
cmd, var, val = (command.split(b' ') + [None] * 3)[:3]
|
|
196
|
+
tagnames = self.tags.keys()
|
|
190
197
|
if var is not None:
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
tagnames = self.tags.keys()
|
|
194
|
-
else:
|
|
195
|
-
tagnames = [x for x in self.tags.keys() if var in x]
|
|
198
|
+
tagname = var.decode()
|
|
199
|
+
tagnames = [x for x in self.tags.keys() if tagname in x]
|
|
196
200
|
if cmd in [b'q', b'quit']:
|
|
197
|
-
self.
|
|
201
|
+
self.transport.close()
|
|
198
202
|
elif cmd in [b'g', b'get']:
|
|
199
203
|
for tagname in tagnames:
|
|
200
204
|
self.write_tag(self.tags[tagname])
|
|
201
|
-
elif cmd == b'set':
|
|
202
|
-
|
|
205
|
+
elif cmd == b'set' and tagname in self.tags:
|
|
206
|
+
try:
|
|
207
|
+
typed_val = self.tags[tagname].type(val.decode())
|
|
208
|
+
self.tags[tagname].value = typed_val
|
|
209
|
+
except ValueError as e:
|
|
210
|
+
logging.warning(f'error setting {tagname}: {e}')
|
|
203
211
|
elif cmd in [b's', b'sub']:
|
|
204
212
|
for tagname in tagnames:
|
|
205
213
|
self.tags[tagname].add_callback(self.write_tag)
|
|
@@ -226,5 +234,13 @@ class Console:
|
|
|
226
234
|
async def start(self):
|
|
227
235
|
"""Start polling, does not return until finished."""
|
|
228
236
|
await self.busclient.start()
|
|
229
|
-
|
|
230
|
-
|
|
237
|
+
try:
|
|
238
|
+
self.protocol = \
|
|
239
|
+
KeypressProtocol(self.writer.edit_line, self.process)
|
|
240
|
+
self.transport, _ = \
|
|
241
|
+
await asyncio.get_event_loop().connect_read_pipe(
|
|
242
|
+
lambda: self.protocol, sys.stdin)
|
|
243
|
+
await self.protocol.connection_lost_future
|
|
244
|
+
finally:
|
|
245
|
+
termios.tcsetattr(self.fdout, termios.TCSADRAIN, self.fdout_attr)
|
|
246
|
+
termios.tcsetattr(self.fdin, termios.TCSADRAIN, self.fdin_attr)
|
|
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
|
|
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
|