#!/usr/bin/env python3 # msgservice.py # # Class that clients can use to receive requests and issue commands # back to the Superboard import zmq import collections import logging import threading import time log = logging.getLogger(__name__) class SuperError(Exception): pass class SuperService: '''Client registers the set of requests that they want to receive''' def __init__(self,requests=None,addr="tcp://localhost",port=21001): if requests is None: requests = [] if not isinstance(requests,collections.Sequence): requests = [requests] self.requests = requests self.request_addr = "%s:%d" % (addr, port) self.cmd_addr = "%s:%d" % (addr, port+1) self.last_seq = None def connect(self): self.context = zmq.Context() self.request_sock = self.context.socket(zmq.SUB) self.request_sock.connect(self.request_addr) # Subscribe to desired requests if not self.requests: self.request_sock.setsockopt(zmq.SUBSCRIBE,b'') else: for r in self.requests: req = (str(r)+ ' ').encode('ascii') log.debug('Subscribing to %s', req) self.request_sock.setsockopt(zmq.SUBSCRIBE, req) self.cmd_sock = self.context.socket(zmq.REQ) self.cmd_sock.connect(self.cmd_addr) def _get_response(self): ''' Get a response to a previous command. Raise an error if not OK ''' msg = self.cmd_sock.recv() log.debug("Got response: %s", msg) if msg[0:2] != b'OK': raise SuperError(msg.decode('ascii')) return msg[3:] def peek(self,addr,size): if size > 240: raise ValueError("size must be < 240 bytes") cmd = ('PEEK %d %d' % (addr, size)).encode('ascii') log.debug("Sending: %s", cmd) self.cmd_sock.send(cmd) resp = self._get_response() return resp def poke(self,addr,data): if len(data): raise ValueError("data size must be < 240 bytes") cmd = ('POKE %d ' % addr).encode('ascii') + data self.cmd_sock.send(cmd) log.debug("Sending: %s", cmd) self._get_response() def getvar(self,name): cmd = ('GET %s' % name).encode('ascii') self.cmd_sock.send(cmd) log.debug("Sending: %s", cmd) resp = self._get_response() if name.endswith('$'): return resp.decode('latin-1') else: return float(resp) def setvar(self,name,value): cmd = ('SET %s %s' % (name, value)).encode('latin-1') log.debug("Sending: %s", cmd) self.cmd_sock.send(cmd) self._get_response() def run(self): self.sema = threading.Semaphore(1) t = threading.Thread(target=self._run) t.daemon= True t.start() try: while True: time.sleep(1) except KeyboardInterrupt: self.sema.acquire() raise def _run(self): ''' Run the client, waiting for incoming requests. ''' self.connect() while True: # Get an incoming request request = self.request_sock.recv() with self.sema: log.debug("Got request: %s", request) fields = request.split() requestno = int(fields[0]) seq = int(fields[1]) if seq != self.last_seq: # New request self.last_seq = seq # Send an ack back to the driver self.cmd_sock.send(request) resp = self.cmd_sock.recv() log.debug("Got response: %s", resp) if not resp.startswith(b'OK'): continue # Run the handler and get the return code try: retcode = self.handle_request(requestno) except Exception as e: print(e) retcode = 0 if retcode is None: retcode = 0 # Send the Superboard return code self.cmd_sock.send(('RET %d' % retcode).encode('ascii')) resp = self.cmd_sock.recv() def handle_request(self,requestno): print("Got request:", requestno) return 0 if __name__ == '__main__': import sys import threading import time logging.basicConfig(level=logging.DEBUG) requests = [int(v) for v in sys.argv[1:]] c = SuperService(requests) c.run()