seaweb.py: JSONencode, SECoP requests, terminate
- make class SecopMsg json encodable - fix handling of SECoP requests - terminate properly on SIGTERM
This commit is contained in:
95
seaweb.py
95
seaweb.py
@ -51,13 +51,12 @@ def guess_mimetype(filename):
|
|||||||
# SSE 'protocol' is described here: http://mzl.la/UPFyxY
|
# SSE 'protocol' is described here: http://mzl.la/UPFyxY
|
||||||
def to_json_sse(msg):
|
def to_json_sse(msg):
|
||||||
txt = json.dumps(msg, separators=(',',': '))
|
txt = json.dumps(msg, separators=(',',': '))
|
||||||
# print 'MSG(size='+str(len(txt))+')', txt[:256]
|
|
||||||
return 'data: %s\n\n' % txt
|
return 'data: %s\n\n' % txt
|
||||||
|
|
||||||
app = flask.Flask(__name__)
|
app = flask.Flask(__name__)
|
||||||
|
|
||||||
update_rider = circularlog.Rider("upd")
|
update_rider = circularlog.Rider("upd")
|
||||||
pollinterval = 0.1
|
pollinterval = 0.2
|
||||||
|
|
||||||
@app.route('/update')
|
@app.route('/update')
|
||||||
def get_update(path=None):
|
def get_update(path=None):
|
||||||
@ -83,7 +82,6 @@ def get_update(path=None):
|
|||||||
yield to_json_sse(msg)
|
yield to_json_sse(msg)
|
||||||
if messages:
|
if messages:
|
||||||
lastmsg = time.time()
|
lastmsg = time.time()
|
||||||
gevent.sleep(pollinterval)
|
|
||||||
else:
|
else:
|
||||||
if time.time() > lastmsg + 30:
|
if time.time() > lastmsg + 30:
|
||||||
if not client.info():
|
if not client.info():
|
||||||
@ -170,7 +168,7 @@ def reply():
|
|||||||
logging.error('%s', traceback.format_exc())
|
logging.error('%s', traceback.format_exc())
|
||||||
circularlog.log()
|
circularlog.log()
|
||||||
msg = dict(type='error', request=path[1:], error=repr(e))
|
msg = dict(type='error', request=path[1:], error=repr(e))
|
||||||
resp = flask.Response(json.dumps(msg), mimetype='application/json')
|
resp = flask.Response(json.dumps(msg, cls=MyEncoder), mimetype='application/json')
|
||||||
resp.headers['Access-Control-Allow-Origin'] = '*'
|
resp.headers['Access-Control-Allow-Origin'] = '*'
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
@ -236,7 +234,6 @@ def sea_request_reply(socket, command, tmo=5):
|
|||||||
t += 0.1
|
t += 0.1
|
||||||
if t > 5:
|
if t > 5:
|
||||||
logging.warning('unusual wait time %.4g (before command %s)', t, command)
|
logging.warning('unusual wait time %.4g (before command %s)', t, command)
|
||||||
#print(command)
|
|
||||||
socket.busy = True
|
socket.busy = True
|
||||||
socket.send_line("fulltransact "+command)
|
socket.send_line("fulltransact "+command)
|
||||||
data = []
|
data = []
|
||||||
@ -379,7 +376,6 @@ class SeaInstrument(Instrument):
|
|||||||
gobj = self.groups[path]
|
gobj = self.groups[path]
|
||||||
now = time.time()
|
now = time.time()
|
||||||
if now < gobj.lastpoll + 0.5:
|
if now < gobj.lastpoll + 0.5:
|
||||||
# print 'too fast', path
|
|
||||||
continue # do not poll before 500 ms have passed
|
continue # do not poll before 500 ms have passed
|
||||||
gobj.lastreq = now
|
gobj.lastreq = now
|
||||||
gobj.lastpoll = now
|
gobj.lastpoll = now
|
||||||
@ -457,9 +453,6 @@ class SeaInstrument(Instrument):
|
|||||||
gobj.components = components
|
gobj.components = components
|
||||||
gobj.empty_values = values
|
gobj.empty_values = values
|
||||||
gobj.version += 1
|
gobj.version += 1
|
||||||
#print 'changed',path, gobj.version
|
|
||||||
#print 'FROM', gobj.components
|
|
||||||
#print 'TO ', components
|
|
||||||
if grouptitle != None:
|
if grouptitle != None:
|
||||||
gobj.grouptitle = grouptitle
|
gobj.grouptitle = grouptitle
|
||||||
|
|
||||||
@ -805,12 +798,10 @@ class DummyClient(SeaGraph):
|
|||||||
msg = json.loads(line)
|
msg = json.loads(line)
|
||||||
if msg['type'] in self.asynch:
|
if msg['type'] in self.asynch:
|
||||||
t = 0
|
t = 0
|
||||||
# print 'PUSH',msg, replytype
|
|
||||||
self.queue.append(msg)
|
self.queue.append(msg)
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
if t >= tmo:
|
if t >= tmo:
|
||||||
# print 'TIMEOUT'
|
|
||||||
raise Exception("timeout")
|
raise Exception("timeout")
|
||||||
gevent.sleep(0.1)
|
gevent.sleep(0.1)
|
||||||
t += 0.1
|
t += 0.1
|
||||||
@ -892,6 +883,15 @@ class SecopMsg:
|
|||||||
value = value[:50] + '...'
|
value = value[:50] + '...'
|
||||||
return "SecopMsg('%s %s %s')" % (self.type, self.par, value)
|
return "SecopMsg('%s %s %s')" % (self.type, self.par, value)
|
||||||
|
|
||||||
|
def toJSON(self):
|
||||||
|
return json.dumps([self.type, self.par, self.value])
|
||||||
|
|
||||||
|
|
||||||
|
class MyEncoder(json.JSONEncoder):
|
||||||
|
def default(self, obj):
|
||||||
|
toJSON = getattr(obj, 'toJSON')
|
||||||
|
return toJSON() if toJSON else super().default(obj)
|
||||||
|
|
||||||
|
|
||||||
def SecopEncode(cmd, par=None, value=None):
|
def SecopEncode(cmd, par=None, value=None):
|
||||||
line = cmd
|
line = cmd
|
||||||
@ -954,21 +954,19 @@ class SecNodeClient:
|
|||||||
msg = self.syncreply.pop(0)
|
msg = self.syncreply.pop(0)
|
||||||
break
|
break
|
||||||
line = self.linesocket.get_line()
|
line = self.linesocket.get_line()
|
||||||
if line is not None:
|
if line is None:
|
||||||
if not line.startswith(('update', 'error_update')):
|
if t >= tmo:
|
||||||
self.consolequeue.append(dict(type='reply',line=line,origin='other'))
|
raise Exception("timeout")
|
||||||
if self.out: self.out.write("<"+line+"\n")
|
gevent.sleep(delta)
|
||||||
msg = SecopMsg(line)
|
t += delta
|
||||||
if msg.asynch: # and replytype != msg['type'] + "=" + msg.par:
|
continue
|
||||||
t = 0
|
if not line.startswith(('update', 'error_update')):
|
||||||
self.queue.append(msg)
|
self.consolequeue.append(dict(type='reply',line=line,origin='other'))
|
||||||
else:
|
if self.out: self.out.write("<"+line+"\n")
|
||||||
break
|
msg = SecopMsg(line)
|
||||||
if t >= tmo:
|
if not msg.asynch:
|
||||||
raise Exception("timeout")
|
break
|
||||||
gevent.sleep(delta)
|
self.queue.append(msg)
|
||||||
t += delta
|
|
||||||
delta += 0.001
|
|
||||||
if not replytype.startswith(msg.type):
|
if not replytype.startswith(msg.type):
|
||||||
logging.error('REPLY MISMATCH %s <> %s', replytype, repr(msg))
|
logging.error('REPLY MISMATCH %s <> %s', replytype, repr(msg))
|
||||||
self.replytype = ""
|
self.replytype = ""
|
||||||
@ -979,19 +977,23 @@ class SecNodeClient:
|
|||||||
messages = self.consolequeue
|
messages = self.consolequeue
|
||||||
self.consolequeue = []
|
self.consolequeue = []
|
||||||
return messages
|
return messages
|
||||||
if self.queue:
|
messages = self.queue
|
||||||
messages = convert_event(self.queue)
|
self.queue = []
|
||||||
self.queue = []
|
while 1:
|
||||||
return messages
|
line = self.linesocket.get_line()
|
||||||
line = self.linesocket.get_line()
|
if not line:
|
||||||
if line:
|
break
|
||||||
if not line.startswith(('update', 'error_update')):
|
if not line.startswith(('update', 'error_update')):
|
||||||
self.consolequeue.append(dict(type='reply',line=line,origin='other'))
|
self.consolequeue.append(dict(type='reply',line=line,origin='other'))
|
||||||
if self.out: self.out.write("<"+line+"\n")
|
if self.out: self.out.write("<"+line+"\n")
|
||||||
msg = SecopMsg(line)
|
msg = SecopMsg(line)
|
||||||
if msg.asynch: # and self.replytype != msg['type'] + "=" + msg.par:
|
if msg.asynch: # and self.replytype != msg['type'] + "=" + msg.par:
|
||||||
return convert_event(SecopMsg(line))
|
messages.append(msg)
|
||||||
self.syncreply.append(msg)
|
else:
|
||||||
|
self.syncreply.append(msg)
|
||||||
|
break
|
||||||
|
if messages:
|
||||||
|
return convert_event(messages)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
@ -1024,7 +1026,7 @@ class SecopClient:
|
|||||||
else:
|
else:
|
||||||
node = self.node_map[path]
|
node = self.node_map[path]
|
||||||
module = node.description['modules'][path]
|
module = node.description['modules'][path]
|
||||||
logging.info('MP %r %r', path)
|
logging.info('MP %r', path)
|
||||||
parameters = dict(module["accessibles"])
|
parameters = dict(module["accessibles"])
|
||||||
components = []
|
components = []
|
||||||
for name in SecopClient.skip_par:
|
for name in SecopClient.skip_par:
|
||||||
@ -1060,9 +1062,20 @@ class SecopClient:
|
|||||||
logging.info('SENDCOMMAND %r', command)
|
logging.info('SENDCOMMAND %r', command)
|
||||||
if not command.strip():
|
if not command.strip():
|
||||||
return dict(type='accept-command')
|
return dict(type='accept-command')
|
||||||
# cmd = "change " + command
|
scmd = command.split(' ')
|
||||||
cmd = command
|
if scmd[1:2]:
|
||||||
return self.nodes[0].cmd_reply(cmd, 'event ' + command.split(' ')[0])
|
module = scmd[1].split(':')[0]
|
||||||
|
node = self.node_map[module]
|
||||||
|
else:
|
||||||
|
node = self.nodes[0]
|
||||||
|
print('no module given, send command to first node', command)
|
||||||
|
if scmd[0] == 'change':
|
||||||
|
replytype = f'changed {scmd[1]}'
|
||||||
|
else:
|
||||||
|
replytype = None
|
||||||
|
# cmd = "change " + command
|
||||||
|
print(command, replytype)
|
||||||
|
return node.cmd_reply(command, replytype)
|
||||||
|
|
||||||
def poll(self):
|
def poll(self):
|
||||||
messages = []
|
messages = []
|
||||||
@ -1135,8 +1148,14 @@ def handle_pdb(sig, frame):
|
|||||||
pdb.Pdb().set_trace(frame)
|
pdb.Pdb().set_trace(frame)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_term(sig, _):
|
||||||
|
server.stop()
|
||||||
|
server.close()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
signal.signal(signal.SIGUSR1, handle_pdb)
|
signal.signal(signal.SIGUSR1, handle_pdb)
|
||||||
|
signal.signal(signal.SIGTERM, handle_term)
|
||||||
print('PID', os.getpid())
|
print('PID', os.getpid())
|
||||||
|
|
||||||
if len(sys.argv) > 3:
|
if len(sys.argv) > 3:
|
||||||
|
Reference in New Issue
Block a user