Merge remote-tracking branch 'refs/remotes/origin/master'
This commit is contained in:
191
src/cristallina/SEA_GraphClient.py
Normal file
191
src/cristallina/SEA_GraphClient.py
Normal file
@@ -0,0 +1,191 @@
|
||||
"""client for SEA GraphServer
|
||||
|
||||
Usage:
|
||||
|
||||
# open a client to host 'samenv', port 8764
|
||||
# the port number for the graph server may be retrived by
|
||||
# the command 'sea list' on the samenv machine
|
||||
|
||||
client = GraphClient('samenv:8764')
|
||||
|
||||
# get one curve
|
||||
|
||||
tlist, vlist, period = client.get_curves(start, end, name)
|
||||
|
||||
or
|
||||
|
||||
# get all important curves
|
||||
curves = client.get_curves(start, end)
|
||||
|
||||
|
||||
# where:
|
||||
|
||||
start, end: interval (unix time, as retrieved from time.time())
|
||||
name: the name of a curve (if no name or a list of names
|
||||
is given, the result is a curves dict)
|
||||
curves: dict <name> of [tlist, vlist, period]
|
||||
tlist: time axis (unix time)
|
||||
vlist: values (y-axis)
|
||||
period: the expected resolution (a hint for graphic clients,
|
||||
saying that for a time step t(n) - t(n-1) significantly
|
||||
bigger than period, an additional point should be added
|
||||
at t(n) - period)
|
||||
"""
|
||||
|
||||
import socket
|
||||
import time
|
||||
|
||||
|
||||
FINISH = b'\nTRANSACTIONFINISHED'
|
||||
START = b'TRANSACTIONSTART'
|
||||
FMIN = min(FINISH[1:])
|
||||
FMAX = max(FINISH[1:])
|
||||
|
||||
|
||||
def expect_reply(sock, expected):
|
||||
while expected:
|
||||
got = sock.recv(8192)
|
||||
if not expected.startswith(got):
|
||||
raise ValueError('expected %r but got %r' % (expected, got))
|
||||
expected = expected[len(got):]
|
||||
|
||||
|
||||
def raw_sics_client(hostport, login):
|
||||
if ':' in hostport:
|
||||
host, port = hostport.split(':')
|
||||
hostport = (host, int(port))
|
||||
sock = socket.create_connection(hostport, timeout=3)
|
||||
bbuf = b''
|
||||
expect_reply(sock, b'OK\n')
|
||||
sock.sendall(login.encode('latin-1') + b'\n')
|
||||
expect_reply(sock, b'Login OK\n')
|
||||
request = yield None
|
||||
while True:
|
||||
sock.sendall(b'fulltransact %s\n' % request.encode('latin-1'))
|
||||
try:
|
||||
reply = sock.recv(8192)
|
||||
if not reply:
|
||||
sock.close()
|
||||
return
|
||||
except socket.timeout:
|
||||
sock.shutdown(socket.SHUT_RDWR)
|
||||
sock.close()
|
||||
raise
|
||||
before, tag, after = reply.partition(FINISH)
|
||||
if tag:
|
||||
result = bbuf + before
|
||||
elif FMIN <= reply[0] <= FMAX: # the FINISH tag may have been cut
|
||||
bbuf, tag, after = (bbuf + reply).partition(FINISH)
|
||||
if not tag:
|
||||
continue
|
||||
result = bbuf
|
||||
else:
|
||||
bbuf += before
|
||||
continue
|
||||
bbuf = after[1:]
|
||||
before, tag, result = result.rpartition(START)
|
||||
if tag:
|
||||
before, nl, result = result.partition(b'\n')
|
||||
try:
|
||||
request = yield result.decode('latin-1')
|
||||
except GeneratorExit:
|
||||
sock.shutdown(socket.SHUT_RDWR)
|
||||
sock.close()
|
||||
return
|
||||
|
||||
|
||||
def sics_client(hostport, command=None, login='Spy 007'):
|
||||
sics = raw_sics_client(hostport, login)
|
||||
next(sics)
|
||||
if command is None:
|
||||
return sics
|
||||
result = sics.send(command)
|
||||
sics.close()
|
||||
return result
|
||||
|
||||
|
||||
class GraphClient:
|
||||
def __init__(self, hostport):
|
||||
self.sc = sics_client(hostport)
|
||||
|
||||
def close(self):
|
||||
self.sc.close()
|
||||
|
||||
def get_raw(self, start, end, *args):
|
||||
"""get raw curves (values as text)"""
|
||||
arglist = ' '.join(args)
|
||||
try:
|
||||
reply = self.sc.send(f'graph {start} {end} {arglist}')
|
||||
except StopIteration:
|
||||
raise ConnectionError('connection closed')
|
||||
lines = reply.split('\n')
|
||||
curve = None
|
||||
result = {}
|
||||
t = 0
|
||||
for line in lines[1:]: # skip first line
|
||||
if line.startswith('*'):
|
||||
spl = line[1:].split()
|
||||
key = spl[0]
|
||||
if key in ('0', '1'):
|
||||
break
|
||||
tlist = []
|
||||
vlist = []
|
||||
curve = [tlist, vlist, 1]
|
||||
if len(spl) >= 3 and spl[1] == 'period':
|
||||
curve[2] = float(spl[2])
|
||||
result[key] = curve
|
||||
t = 0
|
||||
else:
|
||||
tdif, _, value = line.partition(' ')
|
||||
try:
|
||||
t += float(tdif)
|
||||
except ValueError:
|
||||
print(lines)
|
||||
tlist.append(t)
|
||||
vlist.append(value)
|
||||
return result
|
||||
|
||||
def get_names(self, start, end=None):
|
||||
"""get names and properties of curves configured to be display on SEA GUI graphics"""
|
||||
end = start if end is None else end
|
||||
result = self.get_raw(start, end, 'text', 'vars')
|
||||
curves = {}
|
||||
for vlist in result['vars'][1]: # text values
|
||||
for item in vlist.split():
|
||||
item = item.split('|')
|
||||
curves[item[0]] = item[1:] + [''] * (4 - len(item))
|
||||
return curves
|
||||
|
||||
def get_curves(self, start, end, name=None, none_value=None, nmax=None):
|
||||
"""get curves
|
||||
|
||||
start, end: interval (unix time, as retrieved from time.time())
|
||||
non positive values are taken relative to the current time
|
||||
name: a single name or a list of names or None to get all curves (as shown in the SEA GUI)
|
||||
none_value: replacement when no value is defined
|
||||
nmax: max. number of points per curve
|
||||
|
||||
when name is a string, returns [tlist, vlist, period]
|
||||
when name is None or a list of strings (names) returns a dict <name> of [tlist, vlist, period]
|
||||
|
||||
tlist: time axis (unix time)
|
||||
vlist: values (y-axis)
|
||||
period: the expected resolution (a hint for graphic clients)
|
||||
"""
|
||||
if isinstance(name, str):
|
||||
names = [name]
|
||||
elif name is None:
|
||||
names = self.get_names(start, end)
|
||||
else: # assume names is a list of strings
|
||||
names = name
|
||||
args = ['np', str(nmax)] + names if nmax else names
|
||||
result = self.get_raw(start, end, *args)
|
||||
for key, curve in result.items():
|
||||
vlist = curve[1]
|
||||
for i, v in enumerate(vlist):
|
||||
try:
|
||||
vlist[i] = float(v)
|
||||
except ValueError:
|
||||
vlist[i] = none_value
|
||||
return result[name] if isinstance(name, str) else result
|
||||
|
||||
Reference in New Issue
Block a user