Exemplo n.º 1
0
    def actor_fn(self, pipe, arg):
        internal_pipe = Zsock(pipe, False) # We don't own the pipe, so False.
        arg = string_at(arg)

        global port

        zyre_node = Zyre(None)
        zyre_node.set_header (b"TS-PORT", str(port).encode());
        zyre_node.start()
        zyre_node.join(b"broadcast")
        zyre_node.join(b"sync")
        zyre_pipe = zyre_node.socket()

        poller = Zpoller(zyre_pipe, internal_pipe)

        internal_pipe.signal(0)

        print('ZYRE Node started')
        terminated = False
        while not terminated:
            # time.sleep(1)
            # sock = poller.wait(1000)
            # if not sock:
            #     continue
            #
            #
            # ZYRE receive
            #
            # if sock == zyre_pipe:
                e = ZyreEvent(zyre_node)

                # ANY
                if e.type() != b"EVASIVE":
                    e.print()
                    pass

                # ENTER: add to phonebook for external contact (i.e. TimeSync)
                if e.type() == b"ENTER":
                    self.timebook.newpeer(e.peer_uuid(), e.peer_addr(), e.header(b"TS-PORT"))

                # EXIT
                elif e.type() == b"EXIT":
                    print( "ZYRE Node: peer is gone..")

                # JOIN
                elif e.type() == b"JOIN":

                    # SYNC clocks
                    if e.group() == b"sync":
                        pass
                        self.timebook.sync(e.peer_uuid())

                # LEAVE
                elif e.type() == b"LEAVE":
                    print("ZYRE Node: peer left a group..")

                # SHOUT -> process event
                elif e.type() == b"SHOUT" or e.type() == b"WHISPER":

                    # Parsing message
                    data = json.loads(e.msg().popstr().decode())
                    data['from'] = e.peer_uuid()

                    # add group
                    if e.type() == b"SHOUT": data['group'] = e.group().decode()
                    else: data['group'] = 'whisper'

                    self.eventProc(data)
            #
            #
            # #
            # # INTERNAL commands
            # #
            # elif sock == internal_pipe:
            #     msg = Zmsg.recv(internal_pipe)
            #     if not msg: break
            #
            #     command = msg.popstr()
            #     if command == b"$TERM":
            #         print('ZYRE Node TERM')
            #         break
            #
            #     elif command == b"JOIN":
            #         group = msg.popstr()
            #         zyre_node.join(group)
            #
            #     elif command == b"LEAVE":
            #         group = msg.popstr()
            #         zyre_node.leave(group)
            #
            #     elif command == b"SHOUT":
            #         group = msg.popstr()
            #         data = msg.popstr()
            #         zyre_node.shouts(group, data)
            #
            #         # if own group -> send to self too !
            #         groups = zlist_strlist( zyre_node.own_groups() )
            #         if group.decode() in groups:
            #             data = json.loads(data.decode())
            #             data['from'] = 'self'
            #             data['group'] = group.decode()
            #             self.eventProc(data)


        # zyre_node.stop()  # HANGS !
        internal_pipe.__del__()
        zyre_node.__del__()
        print('ZYRE Node stopped')   # WEIRD: print helps the closing going smoothly..
        self.done = True
Exemplo n.º 2
0
class ZyreNode():
    def __init__(self, interface, netiface=None):
        self.interface = interface

        # Peers book
        self.book = {}
        self.topics = []

        # Publisher
        self.pub_cache = {}
        self.publisher = Zsock.new_xpub(("tcp://*:*").encode())

        # TimeServer
        self.timereply = Zsock.new_rep(("tcp://*:*").encode())

        # Zyre
        self.zyre = Zyre(None)
        if netiface:
            self.zyre.set_interface(string_at(netiface))
            self.interface.log("ZYRE Node forced netiface: ",
                               string_at(netiface))

        self.zyre.set_name(str(self.interface.hplayer.name()).encode())
        self.zyre.set_header(b"TS-PORT",
                             str(get_port(self.timereply)).encode())
        self.zyre.set_header(b"PUB-PORT",
                             str(get_port(self.publisher)).encode())

        self.zyre.set_interval(PING_PEER)
        self.zyre.set_evasive_timeout(PING_PEER * 3)
        self.zyre.set_silent_timeout(PING_PEER * 5)
        self.zyre.set_expired_timeout(PING_PEER * 10)

        self.zyre.start()
        self.zyre.join(b"broadcast")
        self.zyre.join(b"sync")

        # Add self to book
        self.book[self.zyre.uuid()] = Peer(
            self, {
                'uuid': self.zyre.uuid(),
                'name': self.zyre.name().decode(),
                'ip': '127.0.0.1',
                'ts_port': get_port(self.timereply),
                'pub_port': get_port(self.publisher)
            })
        self.book[self.zyre.uuid()].subscribe(self.topics)

        # Start Poller
        self._actor_fn = zactor_fn(
            self.actor_fn
        )  # ctypes function reference must live as long as the actor.
        if netiface:
            netiface = create_string_buffer(str.encode(netiface))
        self.actor = Zactor(self._actor_fn, netiface)
        self.done = False

    # ZYRE Zactor
    def actor_fn(self, pipe, netiface):

        # Internal
        internal_pipe = Zsock(pipe, False)  # We don't own the pipe, so False.

        # Poller
        poller = Zpoller(self.zyre.socket(), internal_pipe, self.publisher,
                         self.timereply, None)

        # RUN
        self.interface.log('Node started')
        internal_pipe.signal(0)

        while True:
            sock = poller.wait(500)
            if not sock:
                continue

            #
            # ZYRE receive
            #
            if sock == self.zyre.socket():
                e = ZyreEvent(self.zyre)
                uuid = e.peer_uuid()

                # self.interface.log("ZYREmsg", uuid, e.peer_name().decode(), e.type().decode())

                # ENTER: add to book for external contact (i.e. TimeSync)
                if e.type() == b"ENTER":
                    newpeer = Peer(self, e)
                    existing = None

                    if uuid in self.book:
                        # print ('UUID already exist: replacing')  ## PROBLEM : Same name may appear with different uuid (not a real problem, only if crash and restart with new uuid in a short time..)
                        self.book[uuid].stop()
                        existing = uuid

                    for p in self.book.values():
                        if p.name == newpeer.name:
                            # print ('Name already exist: replacing')
                            p.stop()
                            existing = p.uuid

                    if existing:
                        del self.book[existing]

                    self.book[uuid] = newpeer
                    self.book[uuid].subscribe(self.topics)

                # EVASIVE
                elif e.type() == b"EVASIVE":
                    # if uuid in self.book:
                    #     self.book[uuid].linker(2)
                    pass

                # SILENT
                elif e.type() == b"SILENT":
                    if uuid in self.book:
                        self.book[uuid].linker(1)

                # EXIT
                elif e.type() == b"EXIT":
                    if uuid in self.book:
                        self.book[uuid].linker(0)
                        self.book[uuid].stop()
                        del self.book[uuid]

                # JOIN
                elif e.type() == b"JOIN":
                    # self.interface.log("peer join a group..", e.peer_name(), e.group().decode())

                    # SYNC clocks
                    if e.group() == b"sync":
                        if self.peer(uuid):
                            self.peer(uuid).sync()

                # LEAVE
                elif e.type() == b"LEAVE":
                    # self.interface.log("peer left a group..")
                    pass

                # SHOUT -> process event
                elif e.type() == b"SHOUT" or e.type() == b"WHISPER":

                    # Parsing message
                    data = json.loads(e.msg().popstr().decode())
                    data['from'] = uuid

                    # add group
                    if e.type() == b"SHOUT": data['group'] = e.group().decode()
                    else: data['group'] = 'whisper'

                    self.preProcessor1(data)

            #
            # PUBLISHER event
            #
            elif sock == self.publisher:
                msg = Zmsg.recv(self.publisher)
                if not msg: break
                topic = msg.popstr()

                # Somebody subscribed: push Last Value Cache !
                if len(topic) > 0 and topic[0] == 1:
                    topic = topic[1:]
                    if topic in self.pub_cache:
                        # self.interface.log('XPUB lvc send for', topic.decode())
                        msg = Zmsg.dup(self.pub_cache[topic])
                        Zmsg.send(msg, self.publisher)

                    # else:
                    #     self.interface.log('XPUB lvc empty for', topic.decode())

            #
            # TIMESERVER event
            #
            elif sock == self.timereply:
                msgin = Zmsg.recv(self.timereply)
                msg = Zmsg()
                msg.addstr(str(int(time.time() * PRECISION)).encode())
                Zmsg.send(msg, self.timereply)

            #
            # INTERNAL commands
            #
            elif sock == internal_pipe:
                msg = Zmsg.recv(internal_pipe)
                if not msg or msg.popstr() == b"$TERM":
                    # print('ZYRE Node TERM')
                    break

        internal_pipe.__del__()
        self.interface.log(' - node stopped'
                           )  # WEIRD: print helps the closing going smoothly..
        self.done = True

    def stop(self):
        for peer in self.book.values():
            peer.stop()

        self.actor.sock().send(b"ss", b"$TERM")
        retry = 0
        while not self.done and retry < 10:
            sleep(0.1)
            retry += 1

        # self.zyre.stop()        # HANGS !
        self.zyre.__del__()
        self.publisher.__del__()
        self.timereply.__del__()

    def peer(self, uuid):
        if uuid in self.book and self.book[uuid].active:
            return self.book[uuid]

    def peerByName(self, name):
        for peer in self.book.values():
            if peer.active and peer.name == name:
                return peer

    #
    # PUB/SUB
    #

    def subscribe(self, topics):
        if not isinstance(topics, list): topics = [topics]
        self.topics = list(set(self.topics)
                           | set(topics))  # merge lists and remove duplicates
        for peer in self.book.values():
            peer.subscribe(self.topics)

    def publish(self, topic, args=None):
        topic = topic.encode()

        msg = Zmsg()
        msg.addstr(topic)
        msg.addstr(self.zyre.uuid())
        msg.addstr(json.dumps(args).encode())

        self.pub_cache[topic] = Zmsg.dup(msg)
        Zmsg.send(msg, self.publisher)

    #
    # ZYRE send messages
    #

    def makeMsg(self, event, args=None, delay_ms=0, at=0):
        data = {}
        data['event'] = event
        data['args'] = []
        if args:
            if not isinstance(args, list):
                # self.interface.log('NOT al LIST', args)
                args = [args]
            data['args'] = args

        # at time
        if at > 0:
            data['at'] = at

        # add delay
        if delay_ms > 0:
            if not 'at' in data:
                data['at'] = 0
            data['at'] += int(time.time() * PRECISION +
                              delay_ms * PRECISION / 1000)

        return json.dumps(data).encode()

    def whisper(self, uuid, event, args=None, delay_ms=0, at=0):
        data = self.makeMsg(event, args, delay_ms, at)
        if uuid == self.zyre.uuid():
            data = json.loads(data.decode())
            data['from'] = 'self'
            data['group'] = 'whisper'
            self.preProcessor1(data)
        else:
            self.zyre.whispers(uuid, data)

    def shout(self, group, event, args=None, delay_ms=0, at=0):
        data = self.makeMsg(event, args, delay_ms, at)
        self.zyre.shouts(group.encode(), data)

        # if own group -> send to self too !
        groups = zlist_strlist(self.zyre.own_groups())
        if group in groups:
            data = json.loads(data.decode())
            data['from'] = 'self'
            data['group'] = group
            self.preProcessor1(data)

    def broadcast(self, event, args=None, delay_ms=0, at=0):
        self.shout('broadcast', event, args, delay_ms, at)

    def join(self, group):
        self.zyre.join(group.encode())

    def leave(self, group):
        self.zyre.leave(group.encode())

    #
    # ZYRE messages processor
    #

    def preProcessor1(self, data):
        # if a programmed time is provided, correct it with peer CS
        # Set timer
        if 'at' in data:
            if self.peer(data['from']):
                data['at'] -= self.peer(data['from']).clockshift()
            delay = (data['at']) / PRECISION - time.time()

            if delay <= -10000:
                self.interface.log('WARNING event already passed by', delay,
                                   's, its too late !! discarding... ')
            elif delay <= 0:
                self.interface.log('WARNING event already passed by', delay,
                                   's, playing late... ')
                self.preProcessor2(data)
            elif delay > 3000:
                self.interface.log('WARNING event in', delay,
                                   's, thats weird, playing now... ')
                self.preProcessor2(data)
            else:
                self.interface.log('programmed event in', delay, 's')
                t = Timer(delay, self.preProcessor2, args=[data])
                t.start()
                self.interface.emit('planned', data)

        else:
            self.preProcessor2(data)

    def preProcessor2(self, data):
        self.interface.emit('event', *[data])
        self.interface.emit(data['event'], *data['args'])