Пример #1
0
class GuiClient(Client):
    def __init__(self):
        super(GuiClient, self).__init__(ACTNODE_TOPICS)
        self.nodedata = dict()
        self.timer = None
        self.ref_nodedata = nodeData()
        self.discovery_timer = None
        self.timer = QTimer()
        self.timer.timeout.connect(self.receive)
        self.timer.start(20)
        self.subscribe(b'SIMINFO')
        self.subscribe(b'PLOT' + self.client_id)

        # Signals
        self.actnodedata_changed = Signal()

    def start_discovery(self):
        super(GuiClient, self).start_discovery()
        self.discovery_timer = QTimer()
        self.discovery_timer.timeout.connect(self.discovery.send_request)
        self.discovery_timer.start(3000)

    def stop_discovery(self):
        self.discovery_timer.stop()
        self.discovery_timer = None
        super(GuiClient, self).stop_discovery()

    def event(self, name, data, sender_id):
        sender_data = self.get_nodedata(sender_id)
        data_changed = []
        if name == b'RESET':
            sender_data.clear_scen_data()
            data_changed = list(UPDATE_ALL)
        elif name == b'SHAPE':
            sender_data.update_poly_data(**data)
            data_changed.append('SHAPE')
        elif name == b'DEFWPT':
            sender_data.defwpt(**data)
            data_changed.append('CUSTWPT')
        elif name == b'DISPLAYFLAG':
            sender_data.setflag(**data)
        elif name == b'ECHO':
            sender_data.echo(**data)
            data_changed.append('ECHOTEXT')
        elif name == b'PANZOOM':
            sender_data.panzoom(**data)
            data_changed.append('PANZOOM')
        elif name == b'SIMSTATE':
            sender_data.siminit(**data)
            data_changed = list(UPDATE_ALL)
        else:
            super(GuiClient, self).event(name, data, sender_id)

        if sender_id == self.act and data_changed:
            self.actnodedata_changed.emit(sender_id, sender_data, data_changed)

    def actnode_changed(self, newact):
        self.actnodedata_changed.emit(newact, self.get_nodedata(newact),
                                      UPDATE_ALL)

    def get_nodedata(self, nodeid=None):
        nodeid = nodeid or self.act
        if not nodeid:
            return self.ref_nodedata

        data = self.nodedata.get(nodeid)
        if not data:
            # If this is a node we haven't addressed yet: create dataset and
            # request node settings
            self.nodedata[nodeid] = data = nodeData()
            self.send_event(b'GETSIMSTATE', target=nodeid)

        return data
Пример #2
0
class Client(object):
    def __init__(self, actnode_topics=b''):
        ctx = zmq.Context.instance()
        self.event_io = ctx.socket(zmq.DEALER)
        self.stream_in = ctx.socket(zmq.SUB)
        self.poller = zmq.Poller()
        self.host_id = b''
        self.client_id = b'\x00' + os.urandom(4)
        self.sender_id = b''
        self.servers = dict()
        self.act = b''
        self.actroute = []
        self.acttopics = actnode_topics
        self.discovery = None

        # Signals
        self.nodes_changed = Signal()
        self.server_discovered = Signal()
        self.signal_quit = Signal()
        self.event_received = Signal()
        self.stream_received = Signal()

        # Tell bluesky that this client will manage the network I/O
        bluesky.net = self

    def start_discovery(self):
        if not self.discovery:
            self.discovery = Discovery(self.client_id)
            self.poller.register(self.discovery.handle, zmq.POLLIN)
            self.discovery.send_request()

    def stop_discovery(self):
        if self.discovery:
            self.poller.unregister(self.discovery.handle)
            self.discovery = None

    def get_hostid(self):
        return self.host_id

    def sender(self):
        return self.sender_id

    def event(self, name, data, sender_id):
        ''' Default event handler for Client. Override this function for added
            functionality. '''
        self.event_received.emit(name, data, sender_id)

    def stream(self, name, data, sender_id):
        ''' Default stream handler for Client. Override this function for added
            functionality. '''
        self.stream_received.emit(name, data, sender_id)

    def actnode_changed(self, newact):
        ''' Default actnode change handler for Client. Override or monkey-patch this function
            to implement actual actnode change handling. '''
        print('Client active node changed.')

    def subscribe(self, streamname, node_id=b''):
        ''' Subscribe to a stream. '''
        self.stream_in.setsockopt(zmq.SUBSCRIBE, streamname + node_id)

    def unsubscribe(self, streamname, node_id=b''):
        ''' Unsubscribe from a stream. '''
        self.stream_in.setsockopt(zmq.UNSUBSCRIBE, streamname + node_id)

    def connect(self, hostname='localhost', event_port=0, stream_port=0, protocol='tcp'):
        conbase = '{}://{}'.format(protocol, hostname)
        econ = conbase + (':{}'.format(event_port) if event_port else '')
        scon = conbase + (':{}'.format(stream_port) if stream_port else '')
        self.event_io.setsockopt(zmq.IDENTITY, self.client_id)
        self.event_io.connect(econ)
        self.send_event(b'REGISTER')
        self.host_id = self.event_io.recv_multipart()[0]
        print('Client {} connected to host {}'.format(self.client_id, self.host_id))
        self.stream_in.connect(scon)

        self.poller.register(self.event_io, zmq.POLLIN)
        self.poller.register(self.stream_in, zmq.POLLIN)

    def receive(self, timeout=0):
        ''' Poll for incoming data from Server, and receive if available.
            Arguments:
            timeout: The polling timeout in milliseconds. '''
        try:
            socks = dict(self.poller.poll(timeout))
            if socks.get(self.event_io) == zmq.POLLIN:
                msg = self.event_io.recv_multipart()
                # Remove send-to-all flag if present
                if msg[0] == b'*':
                    msg.pop(0)
                route, eventname, data = msg[:-2], msg[-2], msg[-1]
                self.sender_id = route[0]
                route.reverse()
                pydata = msgpack.unpackb(data, object_hook=decode_ndarray, encoding='utf-8')
                if eventname == b'NODESCHANGED':
                    self.servers.update(pydata)
                    self.nodes_changed.emit(pydata)

                    # If this is the first known node, select it as active node
                    nodes_myserver = next(iter(pydata.values())).get('nodes')
                    if not self.act and nodes_myserver:
                        self.actnode(nodes_myserver[0])
                elif eventname == b'QUIT':
                    self.signal_quit.emit()
                else:
                    self.event(eventname, pydata, self.sender_id)

            if socks.get(self.stream_in) == zmq.POLLIN:
                msg = self.stream_in.recv_multipart()

                strmname = msg[0][:-5]
                sender_id = msg[0][-5:]
                pydata = msgpack.unpackb(msg[1], object_hook=decode_ndarray, encoding='utf-8')
                self.stream(strmname, pydata, sender_id)

            # If we are in discovery mode, parse this message
            if self.discovery and socks.get(self.discovery.handle.fileno()):
                dmsg = self.discovery.recv_reqreply()
                if dmsg.conn_id != self.client_id and dmsg.is_server:
                    self.server_discovered.emit(dmsg.conn_ip, dmsg.ports)
        except zmq.ZMQError:
            return False

    def _getroute(self, target):
        for srv in self.servers.values():
            if target in srv['nodes']:
                return srv['route']
        return None

    def actnode(self, newact=None):
        if newact:
            route = self._getroute(newact)
            if route is None:
                print('Error selecting active node (unknown node)')
                return None
            # Unsubscribe from previous node, subscribe to new one.
            if newact != self.act:
                for topic in self.acttopics:
                    if self.act:
                        self.unsubscribe(topic, self.act)
                    self.subscribe(topic, newact)
                self.actroute = route
                self.act = newact
                self.actnode_changed(newact)

        return self.act

    def addnodes(self, count=1):
        self.send_event(b'ADDNODES', count)

    def send_event(self, name, data=None, target=None):
        pydata = msgpack.packb(data, default=encode_ndarray, use_bin_type=True)
        if not target:
            self.event_io.send_multipart(self.actroute + [self.act, name, pydata])
        elif target == b'*':
            self.event_io.send_multipart([target, name, pydata])
        else:
            self.event_io.send_multipart(self._getroute(target) + [target, name, pydata])
Пример #3
0
class GuiClient(Client):
    def __init__(self):
        super(GuiClient, self).__init__(ACTNODE_TOPICS)
        self.nodedata = dict()
        self.timer = None
        self.ref_nodedata = nodeData()
        self.discovery_timer = None
        self.timer = QTimer()
        self.timer.timeout.connect(self.receive)
        self.timer.start(20)
        self.subscribe(b'SIMINFO')
        self.subscribe(b'PLOT' + self.client_id)

        # Signals
        self.actnodedata_changed = Signal()

    def start_discovery(self):
        super(GuiClient, self).start_discovery()
        self.discovery_timer = QTimer()
        self.discovery_timer.timeout.connect(self.discovery.send_request)
        self.discovery_timer.start(3000)

    def stop_discovery(self):
        self.discovery_timer.stop()
        self.discovery_timer = None
        super(GuiClient, self).stop_discovery()

    def event(self, name, data, sender_id):
        sender_data = self.get_nodedata(sender_id)
        data_changed = []
        if name == b'RESET':
            sender_data.clear_scen_data()
            data_changed = list(UPDATE_ALL)
        elif name == b'SHAPE':
            sender_data.update_poly_data(**data)
            data_changed.append('SHAPE')
        elif name == b'DEFWPT':
            sender_data.defwpt(**data)
            data_changed.append('CUSTWPT')
        elif name == b'DISPLAYFLAG':
            sender_data.setflag(**data)
        elif name == b'ECHO':
            sender_data.echo(**data)
            data_changed.append('ECHOTEXT')
        elif name == b'PANZOOM':
            sender_data.panzoom(**data)
            data_changed.append('PANZOOM')
        elif name == b'SIMSTATE':
            sender_data.siminit(**data)
            data_changed = list(UPDATE_ALL)
        else:
            super(GuiClient, self).event(name, data, sender_id)

        if sender_id == self.act and data_changed:
            self.actnodedata_changed.emit(sender_id, sender_data, data_changed)

    def actnode_changed(self, newact):
        self.actnodedata_changed.emit(newact, self.get_nodedata(newact), UPDATE_ALL)

    def get_nodedata(self, nodeid=None):
        nodeid = nodeid or self.act
        if not nodeid:
            return self.ref_nodedata

        data = self.nodedata.get(nodeid)
        if not data:
            # If this is a node we haven't addressed yet: create dataset and
            # request node settings
            self.nodedata[nodeid] = data = nodeData()
            self.send_event(b'GETSIMSTATE', target=nodeid)

        return data