Initial commit
This commit is contained in:
140
secop.py
Normal file
140
secop.py
Normal file
@ -0,0 +1,140 @@
|
||||
import re
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import socket
|
||||
from select import select
|
||||
from streams import Stream, Base
|
||||
|
||||
IDN = re.compile('.*ISSE.*,SEC[oO]P,')
|
||||
DESCRIBING = re.compile(r'describing \S* (.*)$')
|
||||
UPDATE = re.compile(r'(update|error_update) (\S*) (.*)$')
|
||||
|
||||
|
||||
class EnumConvert(dict):
|
||||
def __call__(self, value):
|
||||
return float(self[value])
|
||||
|
||||
|
||||
class SecopStream(Stream):
|
||||
ping_time = 0
|
||||
|
||||
def init(self):
|
||||
self._buffer = []
|
||||
print('send idn', 'tmo:', self.socket.timeout)
|
||||
self.send('*IDN?')
|
||||
resend = True
|
||||
messages = []
|
||||
for msg in self.get_lines():
|
||||
if IDN.match(msg):
|
||||
break
|
||||
if resend:
|
||||
self.send('*IDN?')
|
||||
resend = False
|
||||
messages.append(msg)
|
||||
else:
|
||||
raise ValueError(f'missing identifier, got {messages} instead')
|
||||
self.send('describe')
|
||||
for msg in self.get_lines():
|
||||
match = DESCRIBING.match(msg)
|
||||
if match:
|
||||
break
|
||||
else:
|
||||
raise ValueError('missing describing message')
|
||||
self.descr = json.loads(match.group(1))
|
||||
self.device = self.descr['equipment_id']
|
||||
if self.device.endswith('psi.ch'):
|
||||
self.device[-6:] = []
|
||||
self.tags['device'] = self.device
|
||||
self.modules = self.descr['modules']
|
||||
self.convert = {}
|
||||
for mod, moddesc in self.modules.items():
|
||||
for param, desc in moddesc['accessibles'].items():
|
||||
dt = desc['datainfo']
|
||||
if dt['type'] in ('double', 'int', 'enum'):
|
||||
self.convert[mod, param] = float
|
||||
self.send('activate')
|
||||
|
||||
def ping(self):
|
||||
self.send('ping')
|
||||
|
||||
def events(self):
|
||||
try:
|
||||
cnt = 0
|
||||
for msg in self.get_lines():
|
||||
match = UPDATE.match(msg)
|
||||
if match:
|
||||
cmd, id, data = match.groups()
|
||||
key = tuple(id.split(':'))
|
||||
cvt = self.convert.get(key)
|
||||
if cvt:
|
||||
data = json.loads(data)
|
||||
if cmd == 'error_update':
|
||||
error = ': '.join(data[0:2])
|
||||
print(msg, repr(error))
|
||||
ts = data[2].get('t', time.time())
|
||||
value = None
|
||||
else:
|
||||
error = None
|
||||
ts = data[1].get('t', time.time())
|
||||
value = cvt(data[0])
|
||||
cnt += 1
|
||||
yield key, value, error, ts
|
||||
elif msg == 'active':
|
||||
# from now on, no more waiting
|
||||
self.notimeout()
|
||||
#print('SECOP', self.uri, 'cnt=', cnt)
|
||||
|
||||
except Exception as e:
|
||||
print(self.uri, repr(e))
|
||||
|
||||
|
||||
SECOP_UDP_PORT = 10767
|
||||
|
||||
|
||||
class UdpStream(Base):
|
||||
socket = None
|
||||
|
||||
def events(self):
|
||||
while select([self.socket], [], [], 0.5)[0]:
|
||||
try:
|
||||
msg, addr = self.socket.recvfrom(1024)
|
||||
except socket.error: # pragma: no cover
|
||||
return None
|
||||
msg = json.loads(msg.decode('utf-8'))
|
||||
if msg['SECoP'] != 'node':
|
||||
continue
|
||||
uri = f"{addr[0]}:{msg['port']}"
|
||||
yield SecopStream, uri, msg['equipment_id']
|
||||
|
||||
|
||||
class ScanReply(UdpStream):
|
||||
def __init__(self):
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||
# send a general broadcast
|
||||
try:
|
||||
sock.sendto(json.dumps(dict(SECoP='discover')).encode('utf-8'),
|
||||
('255.255.255.255', SECOP_UDP_PORT))
|
||||
except OSError as e:
|
||||
print('could not send the broadcast:', e)
|
||||
self.socket = sock
|
||||
self.select_dict[sock.fileno()] = self
|
||||
|
||||
|
||||
class ScanStream(UdpStream):
|
||||
def __init__(self):
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.settimeout(1)
|
||||
if os.name == 'nt':
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
else:
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||
sock.bind(('0.0.0.0', SECOP_UDP_PORT))
|
||||
self.socket = sock
|
||||
self.select_dict[sock.fileno()] = self
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user