class Network(): def __init__(self): self.node = Pyre("GAME_NODE") self.node.set_header("HELLO", "ABC") self.node.start() self.node.join("world:position") self.node.join("world:combat") self.poller = zmq.Poller() self.poller.register(self.node.socket(), zmq.POLLIN) def poll(self): return dict(self.poller.poll(0)) def peers(self): return self.node.peers() def stop(self): self.node.stop() def get_events(self): changes = self.poll() if self.node.socket() in changes and changes[ self.node.socket()] == zmq.POLLIN: events = self.node.recent_events() return events
class Network(): def __init__(self): self.node = Pyre("GAME_NODE") self.node.set_header("AUTHORITY", "FALSE") self.node.set_header("NAME", "") self.node.start() self.node.join("world:position") self.node.join("world:combat") self.node.join("ctf:teams") self.node.join("ctf:dropflag") self.node.join("ctf:gotflag") self.node.join("players:whois") self.node.join("player:name") self.node.join("ctf:scores") self.node.join("ctf:status") self.poller = zmq.Poller() self.poller.register(self.node.socket(), zmq.POLLIN) def poll(self): return dict(self.poller.poll(0)) def peers(self): return self.node.peers() def stop(self): self.node.stop() def get_events(self): changes = self.poll() if self.node.socket() in changes and changes[self.node.socket()] == zmq.POLLIN: events = self.node.recent_events() return events
def _thread_loop(self,context,pipe): n = Pyre(self.name) n.join(self.group) n.start() poller = zmq.Poller() poller.register(pipe, zmq.POLLIN) poller.register(n.socket(), zmq.POLLIN) front,back = zhelper.zcreate_pipe(context) poller.register(back, zmq.POLLIN) def wake_up(): #on app close this timer calls a closed socket. We simply catch it here. try: front.send('wake_up') except Exception as e: logger.debug('Orphaned timer thread raised error: %s'%e) if self._time_grandmaster: t = Timer(0.1, wake_up) else: t = Timer(self.time_sync_announce_interval, wake_up) t.daemon = True t.start() while(True): try: #this should not fail but it does sometimes. We need to clean this out. # I think we are not treating sockets correclty as they are not thread-save. items = dict(poller.poll()) except zmq.ZMQError: logger.warning('Socket fail.') continue if back in items and items[back] == zmq.POLLIN: self._handle_wake_up(back,n,wake_up) if pipe in items and items[pipe] == zmq.POLLIN: if self._handle_internal(pipe,n): break if n.socket() in items and items[n.socket()] == zmq.POLLIN: self._handle_network(n) else: pass logger.debug('_thread_loop closing.') self.thread_pipe = None n.stop()
def gaze_exchange_task(self, ctx, pipe): """ Task for exchanging messages Args: ctx(zmq.Context): the zmq context pipe(zmq.PAIR pipe): the pipe for exchanging messages Returns: (zmq.PAIR pipe) the pipe """ n = Pyre("GAZE_EXCHANGE") self.publisher_id = n.uuid() n.join(GROUP_GAZE_EXCHANGE) n.start() poller = zmq.Poller() # noinspection PyUnresolvedReferences poller.register(pipe, zmq.POLLIN) # noinspection PyUnresolvedReferences poller.register(n.socket(), zmq.POLLIN) while not self.stopped: items = dict(poller.poll()) print(n.socket(), items) # noinspection PyUnresolvedReferences if pipe in items and items[pipe] == zmq.POLLIN: message = pipe.recv() # message to quit message = message.decode('utf-8') if message == STOP_MESSAGE: break print("GAZE_EXCHANGE_TASK: {}".format(message)) self.save_gaze_from_message(message) n.shouts(GROUP_GAZE_EXCHANGE, message) else: cmds = n.recv() msg_type = cmds.pop(0) print("NODE_MSG TYPE: %s" % msg_type) print("NODE_MSG PEER: %s" % uuid.UUID(bytes=cmds.pop(0))) print("NODE_MSG NAME: %s" % cmds.pop(0)) if msg_type.decode('utf-8') == "SHOUT": print("NODE_MSG GROUP: %s" % cmds.pop(0)) message = cmds.pop(0).decode("utf-8") self.save_gaze_from_message(message) elif msg_type.decode('utf-8') == "ENTER": headers = json.loads(cmds.pop(0).decode('utf-8')) print("NODE_MSG HEADERS: %s" % headers) for key in headers: print("key = {0}, value = {1}".format( key, headers[key])) print("NODE_MSG CONT: %s" % cmds) n.stop()
def network_thread(self, ctx, pipe): print("Network thread started..") n = Pyre(self.name) n.join("Car2X") n.start() node_socket = n.socket() poller = zmq.Poller() poller.register(pipe, zmq.POLLIN) poller.register(node_socket, zmq.POLLIN) while not self.stopped: items = dict(poller.poll(1000)) if pipe in items and items[pipe] == zmq.POLLIN: # handle outgoing messages message = pipe.recv() n.shouts("Car2X", message.decode('utf-8')) elif node_socket in items: cmds = n.recv() msg_type = cmds.pop(0) msg_uuid = cmds.pop(0) msg_name = cmds.pop(0) if msg_type == "JOIN": print("{} joined!".format(msg_name)) elif msg_type == "EXIT": print("{} left!".format(msg_name)) elif msg_type == "SHOUT": # handling incoming information msg_channel = cmds.pop(0) msg_str = cmds.pop(0) self.process_data(msg_str) n.stop()
class Client(object): def __exit__(self, exc_type, exc_val, exc_tb): self.engine.stop() def __init__(self, channel=ZYRE_CHANNEL, *args, **kvargs): self.logger = logging.getLogger('pyre') self.channel = channel self.engine = Pyre(self.channel) self.id = self.engine.uuid() def start(self, ctx, pipe): self.logger.info('joining channel') self.engine.join(self.channel) self.logger.info('starting engine...') self.engine.start() self.logger.info('id is: {}'.format(self.id)) poller = zmq.Poller() poller.register(pipe, zmq.POLLIN) poller.register(self.engine.socket(), zmq.POLLIN) while True: items = dict(poller.poll()) if pipe in items and items[pipe] == zmq.POLLIN: message = pipe.recv() # message to quit if message.decode('utf-8') == "$$STOP": break self.logger.info("CHAT_TASK: %s" % message) self.engine.shouts(self.channel, message.decode('utf-8')) else: cmds = self.engine.recv() self.logger.info('HMMM {}'.format(cmds)) msg_type = cmds.pop(0) self.logger.info("NODE_MSG TYPE: %s" % msg_type) self.logger.info("NODE_MSG PEER: %s" % uuid.UUID(bytes=cmds.pop(0))) self.logger.info("NODE_MSG NAME: %s" % cmds.pop(0)) if msg_type.decode('utf-8') == "SHOUT": self.logger.info("NODE_MSG GROUP: %s" % cmds.pop(0)) elif msg_type.decode('utf-8') == "ENTER": headers = json.loads(cmds.pop(0).decode('utf-8')) self.logger.info("NODE_MSG HEADERS: %s" % headers) for key in headers: self.logger.info("key = {0}, value = {1}".format( key, headers[key])) self.logger.info("NODE_MSG CONT: %s" % cmds) self.engine.stop()
class Client(object): def __exit__(self, exc_type, exc_val, exc_tb): self.engine.stop() def __init__(self, channel=ZYRE_CHANNEL, *args, **kvargs): self.logger = logging.getLogger('pyre') self.channel = channel self.engine = Pyre(self.channel) self.id = self.engine.uuid() def start(self, ctx, pipe): self.logger.info('joining channel') self.engine.join(self.channel) self.logger.info('starting engine...') self.engine.start() self.logger.info('id is: {}'.format(self.id)) poller = zmq.Poller() poller.register(pipe, zmq.POLLIN) poller.register(self.engine.socket(), zmq.POLLIN) while True: items = dict(poller.poll()) if pipe in items and items[pipe] == zmq.POLLIN: message = pipe.recv() # message to quit if message.decode('utf-8') == "$$STOP": break self.logger.info("CHAT_TASK: %s" % message) self.engine.shouts(self.channel, message.decode('utf-8')) else: cmds = self.engine.recv() self.logger.info('HMMM {}'.format(cmds)) msg_type = cmds.pop(0) self.logger.info("NODE_MSG TYPE: %s" % msg_type) self.logger.info("NODE_MSG PEER: %s" % uuid.UUID(bytes=cmds.pop(0))) self.logger.info("NODE_MSG NAME: %s" % cmds.pop(0)) if msg_type.decode('utf-8') == "SHOUT": self.logger.info("NODE_MSG GROUP: %s" % cmds.pop(0)) elif msg_type.decode('utf-8') == "ENTER": headers = json.loads(cmds.pop(0).decode('utf-8')) self.logger.info("NODE_MSG HEADERS: %s" % headers) for key in headers: self.logger.info("key = {0}, value = {1}".format(key, headers[key])) self.logger.info("NODE_MSG CONT: %s" % cmds) self.engine.stop()
class ZyreToZMQModule: """ The ZyreToZMQModule Base Class """ def __init__(self,ctx = None,name = "Default",headers=None,groups=None,config_file=None,polling_timeout = 1,sim_time=2): self.logger = logging.getLogger(name) self.logger.setLevel(logging.DEBUG) # create the console output logging sh = logging.StreamHandler() sh.setLevel(logging.INFO) # create a logger writing into a log file fh = logging.FileHandler(name + ".log") fh.setLevel(logging.DEBUG) # create formatter and add it to the handlers # see https://docs.python.org/2/library/logging.html#logrecord-attributes for what can be logged fh.setFormatter(logging.Formatter('Name: %(name)s, ProcessName: %(processName)s, ThreadName: %(threadName)s, Module: %(module)s, FunctionName: %(funcName)s, LineNo: %(lineno)d, Level: %(levelname)s, Msg: %(message)s')) #sh.setFormatter(logging.Formatter('Module: %(module)s, FunctionName: %(funcName)s, LineNo: %(lineno)d, Level: %(levelname)s, Msg: %(message)s')) sh.setFormatter(logging.Formatter('Name: %(name)s, %(module)s/%(funcName)s[%(levelname)s]: %(message)s (lineno:%(lineno)d)')) # add the handlers to logger self.logger.addHandler(sh) self.logger.addHandler(fh) self.logger.propagate = False # don't pass messages to parent loggers if not type(ctx) == zmq.sugar.context.Context: self.logger.warn("Created ZMQ context. Please supply a ZMQ context next time.") ctx = zmq.Context() self.ctx = ctx if not type(name) == str: name = "Default" self.logger.warn("Please provide a the name as a string. Setting name to default: %s." % name) self.name = name if not headers: self.logger.warn("Please provide a model and other header information.") headers = {} self.headers = headers if not groups: groups = ["SHERPA"] self.logger.warn("Please provide a list with group names to which it will listen. Will set it to default: %s" %groups) self.groups = groups if not config_file: # will be opened during configuration self.logger.error("No config file provided for initial configuration") exit() self.config_file = config_file self.config = None self.alive = True # used for exiting once the node is stopped self.line_controller = None # stores uid of line controller this module is listening to (to avoid several line controllers) self.com_timeout = polling_timeout # time_out in msec after which poller returns self.sim_time = sim_time # time in msec self.inbox = [] self.outbox = [] # Node setup self.node = Pyre(self.name) for key in self.headers: self.node.set_header(key,self.headers[key]) for group in self.groups: self.node.join(group) #self.node.set_verbose() self.node.start() # Socket setup self.poller = zmq.Poller() self.poller.register(self.node.socket(), zmq.POLLIN) port = "12911" self.zmq_socket = self.ctx.socket(zmq.PUB) self.zmq_socket.bind("tcp://*:%s" % port) self.run() def run(self): """ Running loop of this process: [COMMUNICATION] Check communication channels and parse all messages. """ while self.alive: try: #[COMMUNICATION] self.communication() #[LOGGING] #self.logging() time.sleep(1) except (KeyboardInterrupt, SystemExit): break self.node.stop() def communication(self): """ This function handles the communication """ # Check if communication is ready if not self.poller: self.logger.warn("No communication channels yet.") # Process input items = dict(self.poller.poll(self.com_timeout)) # Get input from Pyre socket and put it in inbox which is processed in the following steps #logger.debug("Start processing incoming messages") if self.node.socket() in items and items[self.node.socket()] == zmq.POLLIN: self.logger.info("Received %d msgs." % len(items)) #print "msg arrived. inbox len %d" % len(self.inbox) msg_frame = self.node.recv() msg_type = msg_frame.pop(0) peer_uid = uuid.UUID(bytes=msg_frame.pop(0)) peer_name = msg_frame.pop(0) self.logger.info("Msg type: %s" % msg_type) self.logger.info("Sender: %s" % peer_name) # For shout and whisper take message payload and put it into inbox assuming it is correct # For the other zyre msgs create a JSON obj and put that into inbox # TODO: add validation if msg_type == "SHOUT": group = msg_frame.pop(0) msg = json.loads(msg_frame.pop(0)) msg["sender_UUID"] = peer_uid msg["sender_name"] = peer_name # self.inbox.append(msg) self.outbox.append(msg) elif msg_type == "WHISPER": msg = json.loads(msg_frame.pop(0)) msg["sender_UUID"] = peer_uid msg["sender_name"] = peer_name self.inbox.append(msg) # don't care about join and leave messages; if necessary, split to have separate events # elif msg_type == "LEAVE" or msg_type == "JOIN": # group = msg_frame.pop(0) # event = json.dumps({"metamodel": "http://www.sherpa-project.eu/examples/sherpa_msgs", # "model": "http://www.sherpa-project.eu/examples/sherpa_msgs/zyre_events", # "type": "leave/join", # "sender_UUID": peer_uid, # "sender_name": peer_name, # "payload": {} # }) # self.inbox.append(event) elif msg_type == "ENTER": headers = json.loads(msg_frame.pop(0)) event = {"metamodel": "http://www.sherpa-project.eu/examples/sherpa_msgs", "model": "http://www.sherpa-project.eu/examples/sherpa_msgs/zyre_events", "type": "enter", "sender_UUID": peer_uid, "sender_name": peer_name, "payload": {"header": headers} } self.inbox.append(event) elif msg_type == "EXIT": event = {"metamodel": "http://www.sherpa-project.eu/examples/sherpa_msgs", "model": "http://www.sherpa-project.eu/examples/sherpa_msgs/zyre_events", "type": "exit", "sender_UUID": peer_uid, "sender_name": peer_name, "payload": {} } self.inbox.append(event) # Process output and send all msgs there until it is empty, but only if a line controller is connected. if self.outbox: #only do if there really is something in the outbox #if self.lcsm.sm.curr_state._name == "Idle" or self.lcsm.sm.curr_state._name == "Busy": if true: self.logger.info("Start processing messages in outbox. Number of msgs in outbox: %d" % len(self.outbox)) while self.outbox: # since we assume all msgs conform to the sherpa_msgs JSON model, simply shout them out # TODO: add validation msg = self.outbox.pop() if type(msg) == dict: self.logger.info("outbox msg type: " + msg["type"]) self.node.shout("SHERPA", json.dumps(msg)) #WM message msg_wm_update = { "@worldmodeltype": "RSGUpdate", "operation": "CREATE", "node": { "@graphtype": "Group", "id": "ff483c43-4a36-4197-be49-de829cdd66c8", "attributes": [ {"key": "name", "value": "Task Specification Tree"}, {"key": "tst:envtst", "value": msg }, ], }, "parentId": "e379121f-06c6-4e21-ae9d-ae78ec1986a1", } print ("===") print (json.dumps(msg_wm_update)) self.zmq_socket.send_string(json.dumps(msg_wm_update)) else: self.logger.warn("Message is not a dict! Serialization is done here! Msg: \n" + str(msg)) else: self.logger.info("Can't send messages in outbox. Module not ready.")
def chat_task(ctx,pipe): print("communication started") GROUPNAME = "Quizzzzz" StopCmnd = "$$quit" OPPONENT = "opponent" print("name: %s" %NAME) connected_players = 1 # set up node for the user n = Pyre(GROUPNAME) n.set_header('Name', NAME) # join the groupchat n.join(GROUPNAME) #print("UUID %s" %n.uuid()) # start broadcasting signal n.start() # set up poller poller = zmq.Poller() poller.register(pipe,zmq.POLLIN) poller.register(n.socket(),zmq.POLLIN) # looping constantly to recieve messages while True: items = dict(poller.poll()) if pipe in items: message = pipe.recv() if message.decode('utf-8') == StopCmnd: break # uppercase letters for question to whole group elif message.decode('utf-8').isupper() == True: print("Question is: %s" %message) n.shouts(GROUPNAME,"Question to group is: %s" %message.decode('utf-8')) answer = 0 # lowercase to last person who asked question in the group elif message.decode('utf-8').islower() == True: message = NAME + "'s answer is=" + message n.whisper(PEER,message) else: print("please don't mix up or lowercase or use not only numbers") else: msgrecv = n.recv() #print(msgrecv) msg_type = msgrecv.pop(0) msg_sender = uuid.UUID(bytes=msgrecv.pop(0)) PEER = msg_sender msg_name = msgrecv.pop(0) if msg_type.decode('utf-8') == "ENTER": headers = json.loads(msgrecv.pop(0).decode('utf-8')) print("New player discovered in network") print("New player = %s " %headers.get("Name")) elif msg_type.decode('utf-8') == "JOIN": print("New player has joined the group") print("New player = %s" %headers.get("Name")) connected_players += 1 print("#players = %s" %connected_players) elif msg_type.decode('utf-8') == "SHOUT": print(msgrecv.pop(1)) elif msg_type.decode('utf-8') == "WHISPER": if msgrecv[0] == "You have to ask next question": print("You have to ask next question") n.shout(GROUPNAME, "%s is new quizmaster" %NAME) answer = 0 else: print(msgrecv.pop(0)) answer +=1 if answer == connected_players -1: #choosing new quizmaster randomly players = n.peers() next_master = players.pop(random.randint(0,connected_players-2)) n.whisper(next_master,"You have to ask next question") print("Left current game") n.stop()
def thread_loop(self, context, pipe): n = Pyre(self.name) n.join(self.group) n.start() poller = zmq.Poller() poller.register(pipe, zmq.POLLIN) logger.debug(n.socket()) poller.register(n.socket(), zmq.POLLIN) while (True): try: #this should not fail but it does sometimes. We need to clean this out. # I think we are not treating sockets correclty as they are not thread-save. items = dict(poller.poll()) except zmq.ZMQError: logger.warning('Socket fail.') # print(n.socket(), items) if pipe in items and items[pipe] == zmq.POLLIN: message = pipe.recv() # message to quit if message.decode('utf-8') == "EXIT_THREAD": break logger.debug("Emitting to '%s' to '%s' " % (message, self.group)) n.shouts(self.group, message) if n.socket() in items and items[n.socket()] == zmq.POLLIN: cmds = n.recv() msg_type = cmds.pop(0) msg_type = msg_type.decode('utf-8') if msg_type == "SHOUT": uid, name, group, msg = cmds logger.debug("'%s' shouts '%s'." % (name, msg)) self.handle_msg(name, msg) elif msg_type == "WHISPER": pass # uid,name,group,msg = cmds # logger.debug("'%s' whispers '%s'."%(name,msg)) # self.handle_msg(name,msg) elif msg_type == "JOIN": uid, name, group = cmds if group == self.group: self.group_members[uid] = name self.update_gui() elif msg_type == "EXIT": uid, name = cmds try: del self.group_members[uid] except KeyError: pass else: self.update_gui() # elif msg_type == "LEAVE": # uid,name,group = cmds # elif msg_type == "ENTER": # uid,name,headers,ip = cmds logger.debug('thread_loop closing.') self.thread_pipe = None n.stop()
class ZyreToZMQModule: """ The ZyreToZMQModule Base Class """ def __init__(self, ctx=None, name="Default", headers=None, groups=None, config_file=None, polling_timeout=1, sim_time=2): self.logger = logging.getLogger(name) self.logger.setLevel(logging.DEBUG) # create the console output logging sh = logging.StreamHandler() sh.setLevel(logging.INFO) # create a logger writing into a log file fh = logging.FileHandler(name + ".log") fh.setLevel(logging.DEBUG) # create formatter and add it to the handlers # see https://docs.python.org/2/library/logging.html#logrecord-attributes for what can be logged fh.setFormatter( logging.Formatter( 'Name: %(name)s, ProcessName: %(processName)s, ThreadName: %(threadName)s, Module: %(module)s, FunctionName: %(funcName)s, LineNo: %(lineno)d, Level: %(levelname)s, Msg: %(message)s' )) #sh.setFormatter(logging.Formatter('Module: %(module)s, FunctionName: %(funcName)s, LineNo: %(lineno)d, Level: %(levelname)s, Msg: %(message)s')) sh.setFormatter( logging.Formatter( 'Name: %(name)s, %(module)s/%(funcName)s[%(levelname)s]: %(message)s (lineno:%(lineno)d)' )) # add the handlers to logger self.logger.addHandler(sh) self.logger.addHandler(fh) self.logger.propagate = False # don't pass messages to parent loggers if not type(ctx) == zmq.sugar.context.Context: self.logger.warn( "Created ZMQ context. Please supply a ZMQ context next time.") ctx = zmq.Context() self.ctx = ctx if not type(name) == str: name = "Default" self.logger.warn( "Please provide a the name as a string. Setting name to default: %s." % name) self.name = name if not headers: self.logger.warn( "Please provide a model and other header information.") headers = {} self.headers = headers if not groups: groups = ["SHERPA"] self.logger.warn( "Please provide a list with group names to which it will listen. Will set it to default: %s" % groups) self.groups = groups if not config_file: # will be opened during configuration self.logger.error( "No config file provided for initial configuration") exit() self.config_file = config_file self.config = None self.alive = True # used for exiting once the node is stopped self.line_controller = None # stores uid of line controller this module is listening to (to avoid several line controllers) self.com_timeout = polling_timeout # time_out in msec after which poller returns self.sim_time = sim_time # time in msec self.inbox = [] self.outbox = [] # Node setup self.node = Pyre(self.name) for key in self.headers: self.node.set_header(key, self.headers[key]) for group in self.groups: self.node.join(group) #self.node.set_verbose() self.node.start() # Socket setup self.poller = zmq.Poller() self.poller.register(self.node.socket(), zmq.POLLIN) port = "12911" self.zmq_socket = self.ctx.socket(zmq.PUB) self.zmq_socket.bind("tcp://*:%s" % port) self.run() def run(self): """ Running loop of this process: [COMMUNICATION] Check communication channels and parse all messages. """ while self.alive: try: #[COMMUNICATION] self.communication() #[LOGGING] #self.logging() time.sleep(1) except (KeyboardInterrupt, SystemExit): break self.node.stop() def communication(self): """ This function handles the communication """ # Check if communication is ready if not self.poller: self.logger.warn("No communication channels yet.") # Process input items = dict(self.poller.poll(self.com_timeout)) # Get input from Pyre socket and put it in inbox which is processed in the following steps #logger.debug("Start processing incoming messages") if self.node.socket() in items and items[ self.node.socket()] == zmq.POLLIN: self.logger.info("Received %d msgs." % len(items)) #print "msg arrived. inbox len %d" % len(self.inbox) msg_frame = self.node.recv() msg_type = msg_frame.pop(0) peer_uid = uuid.UUID(bytes=msg_frame.pop(0)) peer_name = msg_frame.pop(0) self.logger.info("Msg type: %s" % msg_type) self.logger.info("Sender: %s" % peer_name) # For shout and whisper take message payload and put it into inbox assuming it is correct # For the other zyre msgs create a JSON obj and put that into inbox # TODO: add validation if msg_type == "SHOUT": group = msg_frame.pop(0) msg = json.loads(msg_frame.pop(0)) msg["sender_UUID"] = peer_uid msg["sender_name"] = peer_name # self.inbox.append(msg) self.outbox.append(msg) elif msg_type == "WHISPER": msg = json.loads(msg_frame.pop(0)) msg["sender_UUID"] = peer_uid msg["sender_name"] = peer_name self.inbox.append(msg) # don't care about join and leave messages; if necessary, split to have separate events # elif msg_type == "LEAVE" or msg_type == "JOIN": # group = msg_frame.pop(0) # event = json.dumps({"metamodel": "http://www.sherpa-project.eu/examples/sherpa_msgs", # "model": "http://www.sherpa-project.eu/examples/sherpa_msgs/zyre_events", # "type": "leave/join", # "sender_UUID": peer_uid, # "sender_name": peer_name, # "payload": {} # }) # self.inbox.append(event) elif msg_type == "ENTER": headers = json.loads(msg_frame.pop(0)) event = { "metamodel": "http://www.sherpa-project.eu/examples/sherpa_msgs", "model": "http://www.sherpa-project.eu/examples/sherpa_msgs/zyre_events", "type": "enter", "sender_UUID": peer_uid, "sender_name": peer_name, "payload": { "header": headers } } self.inbox.append(event) elif msg_type == "EXIT": event = { "metamodel": "http://www.sherpa-project.eu/examples/sherpa_msgs", "model": "http://www.sherpa-project.eu/examples/sherpa_msgs/zyre_events", "type": "exit", "sender_UUID": peer_uid, "sender_name": peer_name, "payload": {} } self.inbox.append(event) # Process output and send all msgs there until it is empty, but only if a line controller is connected. if self.outbox: #only do if there really is something in the outbox #if self.lcsm.sm.curr_state._name == "Idle" or self.lcsm.sm.curr_state._name == "Busy": if true: self.logger.info( "Start processing messages in outbox. Number of msgs in outbox: %d" % len(self.outbox)) while self.outbox: # since we assume all msgs conform to the sherpa_msgs JSON model, simply shout them out # TODO: add validation msg = self.outbox.pop() if type(msg) == dict: self.logger.info("outbox msg type: " + msg["type"]) self.node.shout("SHERPA", json.dumps(msg)) #WM message msg_wm_update = { "@worldmodeltype": "RSGUpdate", "operation": "CREATE", "node": { "@graphtype": "Group", "id": "ff483c43-4a36-4197-be49-de829cdd66c8", "attributes": [ { "key": "name", "value": "Task Specification Tree" }, { "key": "tst:envtst", "value": msg }, ], }, "parentId": "e379121f-06c6-4e21-ae9d-ae78ec1986a1", } print("===") print(json.dumps(msg_wm_update)) self.zmq_socket.send_string(json.dumps(msg_wm_update)) else: self.logger.warn( "Message is not a dict! Serialization is done here! Msg: \n" + str(msg)) else: self.logger.info( "Can't send messages in outbox. Module not ready.")
class Transport(): '''Message transport mechanisms for LCAS''' def send(self, dest, ntuple): '''Send given ntuple to Transport named dest. If dest isn't listening for messages from this Transport, the message will (currently) be silently ignored.''' if self._prefix is not None: dest = self._prefix + dest self._pyre.shout(dest, json.dumps(ntuple).encode('utf-8')) # send() # Notes on subscribe # # The callback is called in the same thread that listens for pyre # messages, so the callback should start a new thread if it's # going to block or take a long time to run. # # The callback must take one positional argument, the tuple, and # can OPTIONALLY take a keyword argument (e.g. **kw). I use the # inspect module to detect this. May be too clever for my own # good. # # There can be only one callback for a given remote. If you call # subscribe again with the same remote, it raises an error. def subscribe(self, remote, callback): '''When a message is sent from a Transport named remote to this transport, call the passed callback with the ntuple as the first argument. If the callback takes **kw, it will also pass additional metadata such as the Transport name, UUID, and IP of the sender.''' if self._prefix is not None: remote = self._prefix + remote if remote in self._subscribers: raise TransportError(self, 'Transport.subscribe() was called a second time with the same remote (\"%s\"). You must call Transport.unsubscribe() before setting a new callback.'%(remote)) self._subscribers[remote] = callback # subscribe() def unsubscribe(self, remote): '''Stop listening for messages from remote.''' if self._prefix is not None: remote = self._prefix + remote if remote in self._subscribers: del self._subscribers[remote] # unsubscribe() def subscribe_all(self, callback): '''Call callback every time a message is sent from any remote Transport to this Transport.''' if self._subscribe_all is not None: raise TransportError(self, 'Transport.subscribe_all() was called a second time. You must call Transport.unsubscribe_all() before setting a new callback.') self._subscribe_all = callback # subscribe_all() def unsubscribe_all(self): self._subscribe_all = None # unsubscribe_all() # Notes on get() # # If you already subscribe to remote, temporarly overrides # the subscribe. The subscribed callback will NOT be called. # The subscription is replaced after get() returns. def get(self, remote): '''Block waiting for a message from a Transport named remote. Returns python namedtuple containing fields object, uuid, name, ip, datetime.''' if self._prefix is not None: remote = self._prefix + remote # The final python namedtuple to be returned needs to be shared # between get_callback() and get(). In python3, you can use # nonlocal, but in python2 you need a trick (storing in a # data structure). The actual value to be returned will # be ret[0]. ret = [ None ] # The event e will get set when a message is read by the # readthread. e = threading.Event() # This function is a callback used to detect the next message. # It stores the message in a Python namedtuple and sets the # event. def get_callback(tup, **kw): ret[0] = collections.namedtuple('TransportEnvelope', ['object', 'uuid', 'name', 'ip', 'datetime'])(tup, kw['uuid'], kw['name'], kw['ip'], kw['datetime']) # Inform get() that ret is ready to be returned. e.set() # get_callback() # Store the old callback, if any oldcb = self._subscribers.get(remote, None) # Set the subscription self._subscribers[remote] = get_callback # Wait for the callback to be called. e.wait() # Restore the old subscription, if any. if oldcb is not None: self._subscribers[remote] = oldcb else: del self._subscribers[remote] # Return the namedtuple. return ret[0] # get() def quit_federation(self): '''Send a quit message to all agents in this federation, and then close down the Transport.''' if self._run: self._pyre.shouts(self._globalchannel, u"QUIT") self._run = False # Wait for the readthread to finish self._readthread.join() # Tell Pyre to shut down self._pyre.stop() def is_running(self): '''Return the status of this Transport. If the Transport isn't running, you should not send it messages and the callbacks will not be called.''' return self._run ###################################################################### # All private methods below here def __init__(self, myname, port=None, prefix=None): # NOTE: Seems to be a bug in Pyre where you can't set the port. if port is not None: raise NotImplementedError('There is a bug in Pyre that prevents setting of the discovery port. If you require multiple federations of Pyre components, use prefix instead of port in Transport constructor.') # dict of remote name to callback. See subscribe method above. self._subscribers = {} # Callback for all message (or None if none registered) self._subscribe_all = None self._prefix = prefix # Attach the federation name as a prefix to both this channel # and the global channel. The global channel is currently # just used for QUIT messages. if prefix is not None: myname = prefix + myname self._globalchannel = prefix + "GLOBAL" else: self._globalchannel = "GLOBAL" self._pyre = Pyre(myname) if port is not None: self._pyre.set_port(port) self._pyre.join(myname) self._pyre.join(self._globalchannel) self._pyre.start() # Dict of (UUIDs => IP addresses) that have sent a valid ENTER message self._uuid2ip = {} self._run = True self._readthread = threading.Thread(target=self._readworker) self._readthread.start() # __init__() # Handle pyre messages. Run in self._readthread def _readworker(self): '''This method is called in a separate thread to handle messages sent over pyre. It dispataches to methods named for the pyre events (e.g. _ENTER).''' # Set up a poller so recv doesn't block. Possibly not needed # since we'll always get an event when the other agents quit, # but just in case something goes wrong, we want to be sure to # close down. poller = zmq.Poller() sock = self._pyre.socket() poller.register(sock, zmq.POLLIN) while self._run: # Wait until a message is received OR one second timeout. items = dict(poller.poll(1000)) if not (sock in items and items[sock] == zmq.POLLIN): # This should only happen if we time out. continue # There's an event waiting. Read and process it. event = self._pyre.recv() logger.debug('Transport %s-%s received event %s'%(self._pyre.uuid(), self._pyre.name(), event)) eventtype = event[0].decode('utf-8') # Sender's uuid and name sid = uuid.UUID(bytes=event[1]) name = event[2].decode('utf-8') # Make sure we've seen matching ENTER for all events if eventtype != 'ENTER' and sid not in self._uuid2ip: raise TransportProtocolError(self, 'Received event %s with no matching ENTER.'%(event)) continue if eventtype == 'ENTER': # Changed url = event[4].decode('utf-8') self._ENTER(sid, name, url) elif eventtype == 'JOIN': channel = event[3].decode('utf-8') self._JOIN(sid, name, channel) elif eventtype == 'SHOUT': channel = event[3].decode('utf-8') message = event[4].decode('utf-8') if channel == self._globalchannel and message == "QUIT": # Set ourself to stop running, close down pyre, exit # worker thread. self._run = False self._pyre.stop() break else: self._SHOUT(sid, name, channel, message) elif eventtype == 'WHISPER': message = event[3].decode('utf-8') self._WHISPER(sid, name, message) elif eventtype == 'LEAVE': channel = event[3].decode('utf-8') self._LEAVE(sid, name, channel) elif eventtype == 'EXIT': self._EXIT(sid, name) else: raise TransportProtocolError(self, 'Illegal event type in event %s'%(event)) # _readworker() # The following methods are named for the pyre event that this # instance has received. They are called automatically from the # worker thread that's listening for events. def _ENTER(self, sid, name, url): # We expect all connections to be tcp on some port. This regular # expression is used to extract the ip part. urlmatch = re.match('tcp://([0-9.]+):[0-9]+$', url) if urlmatch: ip = urlmatch.group(1) if is_valid_ip(ip): # Everything looks good. Add to list of valid uuids. self._uuid2ip[sid] = ip else: raise TransportSecurityError(self, 'Message from invalid IP address %s in ENTER %s %s %s. Check the function is_valid_ip() in Transport.py.'%(ip, sid, name, url)) else: raise TransportProtocolError(self, 'Malformed URL in ENTER %s %s %s'%(sid, name, url)) # _ENTER() def _JOIN(self, sid, name, channel): pass # _JOIN() def _SHOUT(self, sid, name, channel, message): now = datetime.datetime.now() logger.debug('In _SHOUT with %s %s %s %s'%(sid, name, channel, message)) #??? if name in self._subscribers: logger.debug('got a subscription') cb = self._subscribers[name] self._call_callback(cb, sid, name, channel, message, now) if self._subscribe_all is not None: cb = self._subscribe_all self._call_callback(cb, sid, name, channel, message, now) # _SHOUT() def _call_callback(self, cb, sid, name, channel, message, now): if inspect.getargspec(cb).keywords is None: cb(json.loads(message)) else: cb(message, uuid=sid, name=name, ip=self._uuid2ip[sid], datetime=now) # _call_callback def _WHISPER(self, sid, name, message): raise TransportProtocolError(self, 'Unexpected WHISPER from %s %s'%(sid, name)) # _WHISPER() def _LEAVE(self, sid, name, channel): pass # _LEAVE() def _EXIT(self, sid, name): # Remove sid from list of valid uuids. This should # never be an error since we check in _readworker(). del self._uuid2ip[sid]
class Bridge(object): """docstring for Bridge""" def __init__(self, uvc_id): super(Bridge, self).__init__() self.data_seq = 0 self.note_seq = 0 # init capture self.cap = uvc.Capture(uvc_id) logger.info('Initialised uvc device %s'%self.cap.name) # init pyre self.network = Pyre(socket.gethostname()+self.cap.name[-4:]) self.network.start() logger.info('Bridging under "%s"'%self.network.name()) # init sensor sockets ctx = zmq.Context() generic_url = 'tcp://*:*' public_ep = self.network.endpoint() self.note, self.note_url = self.bind(ctx, zmq.PUB , generic_url, public_ep) self.data, self.data_url = self.bind(ctx, zmq.PUB , generic_url, public_ep,set_hwm=1) self.cmd , self.cmd_url = self.bind(ctx, zmq.PULL, generic_url, public_ep) def loop(self): logger.info('Entering bridging loop...') self.network.shout('pupil-mobile', self.sensor_attach_json()) try: while True: self.poll_network() self.poll_cmd_socket() self.publish_frame() except KeyboardInterrupt: pass except Exception: import traceback traceback.print_exc() finally: self.network.shout('pupil-mobile', json.dumps({ 'subject' : 'detach', 'sensor_uuid': self.network.uuid().hex })) logger.info('Leaving bridging loop...') def publish_frame(self): frame = self.cap.get_frame_robust() now = int(time.time()*1000000) index = self.data_seq self.data_seq += 1 self.data_seq %= sequence_limit jpeg_buffer = frame.jpeg_buffer meta_data = struct.pack('<LLLLQLL', 0x10, frame.width, frame.height, index, now, jpeg_buffer.size, 0) self.data.send_multipart([self.network.uuid().hex, meta_data, jpeg_buffer]) def poll_network(self): while has_data(self.network.socket()): event = PyreEvent(self.network) if event.type == 'JOIN' and event.group == 'pupil-mobile': self.network.whisper(event.peer_uuid, self.sensor_attach_json()) def poll_cmd_socket(self): while has_data(self.cmd): sensor, cmd_str = self.cmd.recv_multipart() try: cmd = json.loads(cmd_str) except Exception as e: logger.debug('Could not parse received cmd: %s'%cmd_str) else: logger.debug('Received cmd: %s'%cmd) if cmd.get('action') == 'refresh_controls': self.publish_controls() elif cmd.get('action') == 'set_control_value': val = cmd.get('value', 0) if cmd.get('control_id') == 'CAM_RATE': self.cap.frame_rate = self.cap.frame_rates[val] elif cmd.get('control_id') == 'CAM_RES': self.cap.frame_size = self.cap.frame_sizes[val] self.publish_controls() def __del__(self): self.note.close() self.data.close() self.cmd.close() self.network.stop() def publish_controls(self): self.note.send_multipart([ self.network.uuid().hex, self.frame_size_control_json()]) self.note.send_multipart([ self.network.uuid().hex, self.frame_rate_control_json()]) def sensor_attach_json(self): sensor = { "subject" : "attach", "sensor_name" : self.cap.name, "sensor_uuid" : self.network.uuid().hex, "sensor_type" : 'video', "notify_endpoint" : self.note_url, "command_endpoint": self.cmd_url, "data_endpoint" : self.data_url } return json.dumps(sensor) def frame_size_control_json(self): index = self.note_seq self.note_seq += 1 self.note_seq %= sequence_limit curr_fs = self.cap.frame_sizes.index(self.cap.frame_size) return json.dumps({ "subject" : "update", "control_id" : "CAM_RES", "seq" : index, "changes" : { "value" : curr_fs, "dtype" : 'intmapping', "min" : None, "max" : None, "res" : None, "def" : 0, "caption" : 'Resolution', "readonly" : False, "map" : [{ 'value' : idx, 'caption': '%ix%i'%fs } for idx,fs in enumerate(self.cap.frame_sizes)] } }) def frame_rate_control_json(self): index = self.note_seq self.note_seq += 1 self.note_seq %= sequence_limit curr_fr = self.cap.frame_rates.index(self.cap.frame_rate) return json.dumps({ "subject" : "update", "control_id" : "CAM_RATE", "seq" : index, "changes" : { "value" : curr_fr, "dtype" : 'intmapping', "min" : None, "max" : None, "res" : None, "def" : 0, "caption" : 'Frame Rate', "readonly" : False, "map" : [{ 'value' : idx, 'caption': '%.1f Hz'%fr } for idx,fr in enumerate(self.cap.frame_rates)] } }) def bind(self, ctx, sock_type, url, public_ep, set_hwm=None): sock = ctx.socket(sock_type) if set_hwm: sock.set_hwm(set_hwm) sock.bind(url) ep = sock.last_endpoint port = ep.split(':')[-1] public_ep.split(':')[-1] public_addr = public_ep.split(':')[:-1] return sock, ':'.join(public_addr+[port])
class Network: hosting: bool = False def __init__(self): self.open() def get_all_groups(self) -> List[str]: """Get the names of groups that can be joined.""" groups = [] for peer in self.node.peers(): group = self.node.peer_header_value(peer, 'hosting') if group != None and len(group) > 0: groups.append(group) return groups def get_our_group(self) -> Union[str, None]: """What is the name of the group we're in or hosting?""" our_groups = self.node.own_groups() our_groups.remove('untangled2018') if len(our_groups) == 0: return None return our_groups[0] def is_in_group(self) -> bool: """Are we in or hosting a group?""" return self.get_our_group() != None def is_hosting(self) -> bool: """Are we hosting?""" return self.hosting def join_group(self, group: str) -> None: """Join a group of given name (assumes you are not in a group already).""" if group not in self.get_all_groups(): raise ValueError('Group named "%s" does not exist'.format(group)) if self.is_in_group(): raise ValueError( 'You must leave the previous group before you join another') self.node.join(group) def leave_group(self) -> None: """Leave any joined group or stop hosting.""" self.hosting = False if not self.is_in_group(): raise ValueError('You are not in a group') for group in self.node.own_groups(): self.node.leave(group) def host_group(self, name: str) -> None: """Host a group of given name.""" if name in self.get_all_groups(): raise ValueError('A group of the given name already exists') if self.is_in_group(): raise ValueError('Cannot host whilst in a group') self.node.set_header('hosting', name) self.hosting = True self.node.join(name) def open(self) -> None: """Create a new pyre instance and join untangled.""" self.node = Pyre() self.node.start() self.node.join('untangled2018') # used to get our messages self.poller = zmq.Poller() self.poller.register(self.node.socket(), zmq.POLLIN) def get_id(self) -> str: """Get our id, as a unique node on the network.""" return self.node.uuid() def is_me(self, player_id) -> bool: """See if a given id is ours.""" return self.get_id() == player_id def close(self) -> None: """Disconnect from everything""" self.node.stop() def get_messages(self): """See what has been sent to us: who has joined, what have people said, etc""" # what has changed changes = dict(self.poller.poll(0)) # are these the changes we subscribed for if self.node.socket() in changes and changes[ self.node.socket()] == zmq.POLLIN: msgs = self.node.recent_events() return msgs # nothing to return return [] def pull_game(self, game): """Update our game state based on what other people tell us.""" for msg in self.get_messages(): # is it relevant to us? if msg.group != self.get_our_group(): continue if msg.type == 'SHOUT': entities = bson.loads(msg.msg[0]) if 'ids' in entities: keys = entities['ids'] cur_keys = list(game.entities.keys()) diff = list(set(cur_keys) - set(keys)) for key in diff: del game.entities[key] entities = entities['components'] for key, changed_comps in entities.items(): key = uuid.UUID(key) if key not in game.entities: game.entities[key] = {} entity = game.entities[key] for compname, component in changed_comps.items(): try: clas = components.__dict__[compname] if clas in entity: entity[clas] = entity[clas].replace( **component) else: entity[clas] = clas(**component) entity[clas].observed_changes() except Exception: print( 'Error updating component, is everyone in the group on the same version?', file=sys.stdout) elif self.is_hosting(): if msg.type == 'JOIN': game.on_player_join(msg.peer_uuid) self.push_game(game, initial=True) elif msg.type == 'EXIT' or msg.type == "LEAVE": game.on_player_quit(msg.peer_uuid) def push_game(self, game, initial=False): """Tell others how we've changed the game state.""" if len(self.node.peers_by_group(self.get_our_group())) == 0: # Nobody else to talk to return entities = {'components': {}} if self.is_hosting(): entities = {'ids': [], 'components': {}} for key, entity in game.entities.items(): changed_comps = {} for component in entity.values(): if component.is_networked() and (initial or component.has_changed()): changed_comps[component.get_name()] = component.as_dict() component.observed_changes() if 'ids' in entities: entities['ids'].append(key) entities['components'][str(key)] = changed_comps self.node.shout(self.get_our_group(), bson.dumps(entities))
def thread_loop(self, context, pipe): n = Pyre(self.name) n.join(self.group) n.start() poller = zmq.Poller() poller.register(pipe, zmq.POLLIN) logger.debug(n.socket()) poller.register(n.socket(), zmq.POLLIN) while (True): try: #this should not fail but it does sometimes. We need to clean this out. # I think we are not treating sockets correclty as they are not thread-save. items = dict(poller.poll()) except zmq.ZMQError: logger.warning('Socket fail.') # print(n.socket(), items) if pipe in items and items[pipe] == zmq.POLLIN: message = pipe.recv() # message to quit if message.decode('utf-8') == "EXIT_THREAD": break logger.debug("Emitting to '%s' to '%s' " % (message, self.group)) n.shouts(self.group, message) if n.socket() in items and items[n.socket()] == zmq.POLLIN: cmds = n.recv() msg_type = cmds.pop(0) msg_type = msg_type.decode('utf-8') if msg_type == "SHOUT": uid, name, group, msg = cmds logger.debug("'%s' shouts '%s'." % (name, msg)) if start_rec in msg: session_name = msg.replace(start_rec, '') self.notify_all({ 'name': 'rec_should_start', 'session_name': session_name, 'network_propagate': False }) elif stop_rec in msg: self.notify_all({ 'name': 'rec_should_stop', 'network_propagate': False }) elif sync_time in msg: offset = float(msg.replace(sync_time, '')) if self.ok_to_set_timebase(): self.adjust_timebase(offset) elif msg_type == "ENTER": uid, name, headers, ip = cmds elif msg_type == "JOIN": uid, name, group = cmds if group == self.group: self.group_members[uid] = name self.update_gui() elif msg_type == "EXIT": uid, name = cmds try: del self.group_members[uid] except KeyError: pass else: self.update_gui() elif msg_type == "LEAVE": uid, name, group = cmds elif msg_tpye == "WHISPER": pass logger.debug('thread_loop closing.') self.thread_pipe = None n.stop()
def thread_loop(self, context, pipe): n = Pyre(self.name) n.join(self.group) n.start() poller = zmq.Poller() poller.register(pipe, zmq.POLLIN) logger.debug(n.socket()) poller.register(n.socket(), zmq.POLLIN) while (True): try: #this should not fail but it does sometimes. We need to clean this out. # I think we are not treating sockets correclty as they are not thread-save. items = dict(poller.poll(self.timeout)) except zmq.ZMQError: logger.warning('Socket fail.') # print(n.socket(), items) if pipe in items and items[pipe] == zmq.POLLIN: message = pipe.recv() # message to quit if message.decode('utf-8') == exit_thread: break elif message.decode('utf-8') == init_master_sync: self.timeout = 3000 else: logger.debug("Emitting to '%s' to '%s' " % (message, self.group)) n.shouts(self.group, message) if n.socket() in items and items[n.socket()] == zmq.POLLIN: cmds = n.recv() msg_type = cmds.pop(0) msg_type = msg_type.decode('utf-8') if msg_type == "SHOUT": uid, name, group, msg = cmds logger.debug("'%s' shouts '%s'." % (name, msg)) self.handle_msg(name, msg) elif msg_type == "WHISPER": uid, name, msg = cmds logger.debug("'%s/' whispers '%s'." % (name, msg)) self.handle_msg_whisper(uid, name, msg, n) elif msg_type == "JOIN": uid, name, group = cmds if group == self.group: self.group_members[uid] = name self.update_gui() elif msg_type == "EXIT": uid, name = cmds try: del self.group_members[uid] except KeyError: pass else: self.update_gui() # elif msg_type == "LEAVE": # uid,name,group = cmds # elif msg_type == "ENTER": # uid,name,headers,ip = cmds # logger.warning((uid,'name',headers,ip)) elif not items: #timeout events are used for pupil sync. if self.sync_master is self: if self.sync_nodes: node_uid = self.sync_nodes.pop(0) logger.info("Synchonizing node %s" % self.group_members[node_uid]) n.whispers(uuid.UUID(bytes=node_uid), sync_time_init) else: self.timeout = None self.sync_master = None logger.info("All other Pupil nodes are sycronized.") elif self.sync_master: t0 = self.g_pool.capture.get_timestamp() n.whispers(uuid.UUID(bytes=self.sync_master), sync_time_request + '%s' % t0) else: pass logger.debug('thread_loop closing.') self.thread_pipe = None n.stop()
def thread_loop(self,context,pipe): n = Pyre(self.name) n.join(self.group) n.start() poller = zmq.Poller() poller.register(pipe, zmq.POLLIN) logger.debug(n.socket()) poller.register(n.socket(), zmq.POLLIN) while(True): try: #this should not fail but it does sometimes. We need to clean this out. # I think we are not treating sockets correclty as they are not thread-save. items = dict(poller.poll(self.timeout)) except zmq.ZMQError: logger.warning('Socket fail.') # print(n.socket(), items) if pipe in items and items[pipe] == zmq.POLLIN: message = pipe.recv() # message to quit if message.decode('utf-8') == exit_thread: break elif message.decode('utf-8') == init_master_sync: self.timeout = 3000 else: logger.debug("Emitting to '%s' to '%s' " %(message,self.group)) n.shouts(self.group, message) if n.socket() in items and items[n.socket()] == zmq.POLLIN: cmds = n.recv() msg_type = cmds.pop(0) msg_type = msg_type.decode('utf-8') if msg_type == "SHOUT": uid,name,group,msg = cmds logger.debug("'%s' shouts '%s'."%(name,msg)) self.handle_msg(name,msg) elif msg_type == "WHISPER": uid,name,msg = cmds logger.debug("'%s/' whispers '%s'."%(name,msg)) self.handle_msg_whisper(uid,name,msg,n) elif msg_type == "JOIN": uid,name,group = cmds if group == self.group: self.group_members[uid] = name self.update_gui() elif msg_type == "EXIT": uid,name = cmds try: del self.group_members[uid] except KeyError: pass else: self.update_gui() # elif msg_type == "LEAVE": # uid,name,group = cmds # elif msg_type == "ENTER": # uid,name,headers,ip = cmds # logger.warning((uid,'name',headers,ip)) elif not items: #timeout events are used for pupil sync. if self.sync_master is self: if self.sync_nodes: node_uid = self.sync_nodes.pop(0) logger.info("Synchonizing node %s"%self.group_members[node_uid]) n.whispers(uuid.UUID(bytes=node_uid),sync_time_init) else: self.timeout = None self.sync_master = None logger.info("All other Pupil nodes are sycronized.") elif self.sync_master: t0 = self.g_pool.capture.get_timestamp() n.whispers(uuid.UUID(bytes=self.sync_master),sync_time_request+'%s'%t0) else: pass logger.debug('thread_loop closing.') self.thread_pipe = None n.stop()
def thread_loop(self,context,pipe): n = Pyre(self.name) n.join(self.group) n.start() poller = zmq.Poller() poller.register(pipe, zmq.POLLIN) logger.debug(n.socket()) poller.register(n.socket(), zmq.POLLIN) while(True): try: #this should not fail but it does sometimes. We need to clean this out. # I think we are not treating sockets correclty as they are not thread-save. items = dict(poller.poll()) except zmq.ZMQError: logger.warning('Socket fail.') # print(n.socket(), items) if pipe in items and items[pipe] == zmq.POLLIN: message = pipe.recv() # message to quit if message.decode('utf-8') == "EXIT_THREAD": break logger.debug("Emitting to '%s' to '%s' " %(message,self.group)) n.shouts(self.group, message) if n.socket() in items and items[n.socket()] == zmq.POLLIN: cmds = n.recv() msg_type = cmds.pop(0) msg_type = msg_type.decode('utf-8') if msg_type == "SHOUT": uid,name,group,msg = cmds logger.debug("'%s' shouts '%s'."%(name,msg)) if start_rec in msg : session_name = msg.replace(start_rec,'') self.notify_all({'name':'rec_should_start','session_name':session_name,'network_propagate':False}) elif stop_rec in msg: self.notify_all({'name':'rec_should_stop','network_propagate':False}) elif sync_time in msg: offset = float(msg.replace(sync_time,'')) if self.ok_to_set_timebase(): self.adjust_timebase(offset) elif msg_type == "ENTER": uid,name,headers,ip = cmds elif msg_type == "JOIN": uid,name,group = cmds if group == self.group: self.group_members[uid] = name self.update_gui() elif msg_type == "EXIT": uid,name = cmds try: del self.group_members[uid] except KeyError: pass else: self.update_gui() elif msg_type == "LEAVE": uid,name,group = cmds elif msg_tpye == "WHISPER": pass logger.debug('thread_loop closing.') self.thread_pipe = None n.stop()
def thread_loop(self,context,pipe): n = Pyre(self.name) n.join(self.group) n.start() poller = zmq.Poller() poller.register(pipe, zmq.POLLIN) poller.register(n.socket(), zmq.POLLIN) front,back = zhelper.zcreate_pipe(context) poller.register(back, zmq.POLLIN) def wake_up(): #on app close this timer calls a closed socket. We simply catch it here. try: front.send('wake_up') except Exception as e: logger.debug('Orphaned timer thread raised error: %s'%e) t = Timer(self.time_sync_announce_interval, wake_up) t.daemon = True t.start() while(True): try: #this should not fail but it does sometimes. We need to clean this out. # I think we are not treating sockets correclty as they are not thread-save. items = dict(poller.poll()) except zmq.ZMQError: logger.warning('Socket fail.') continue if back in items and items[back] == zmq.POLLIN: back.recv() #timeout events are used for pupil sync. #annouce masterhood every interval time: if isinstance(self.sync_node,Clock_Sync_Master): n.shouts(self.group, SYNC_TIME_MASTER_ANNOUNCE+"%s"%self.clock_master_worthiness()+msg_delimeter+'%s'%self.sync_node.port) # synced slave: see if we should become master if we dont hear annoncement within time. elif isinstance(self.sync_node,Clock_Sync_Follower) and not self.sync_node.offset_remains: if self.get_monotonic_time()-self.last_master_announce > self.time_sync_wait_interval_short: self.sync_node.terminate() self.sync_node = Clock_Sync_Master(time_fn=self.get_time) n.shouts(self.group, SYNC_TIME_MASTER_ANNOUNCE+"%s"%self.clock_master_worthiness()+msg_delimeter+'%s'%self.sync_node.port) # unsynced slave or none should wait longer but eventually take over elif self.get_monotonic_time()-self.last_master_announce > self.time_sync_wait_interval_long: if self.sync_node: self.sync_node.terminate() self.sync_node = Clock_Sync_Master(time_fn=self.get_time) n.shouts(self.group, SYNC_TIME_MASTER_ANNOUNCE+"%s"%self.clock_master_worthiness()+msg_delimeter+'%s'%self.sync_node.port) t = Timer(self.time_sync_announce_interval, wake_up) t.daemon = True t.start() if pipe in items and items[pipe] == zmq.POLLIN: message = pipe.recv() # message to quit if message.decode('utf-8') == exit_thread: break else: logger.debug("Shout '%s' to '%s' " %(message,self.group)) n.shouts(self.group, message) if n.socket() in items and items[n.socket()] == zmq.POLLIN: cmds = n.recv() msg_type = cmds.pop(0) msg_type = msg_type.decode('utf-8') if msg_type == "SHOUT": uuid,name,group,msg = cmds logger.debug("'%s' shouts '%s'."%(name,msg)) self.handle_msg(uuid,name,msg,n) elif msg_type == "WHISPER": uuid,name,msg = cmds logger.debug("'%s/' whispers '%s'."%(name,msg)) self.handle_msg_whisper(uuid,name,msg,n) elif msg_type == "JOIN": uuid,name,group = cmds if group == self.group: self.group_members[uuid] = name self.update_gui() elif msg_type == "EXIT": uuid,name = cmds try: del self.group_members[uuid] except KeyError: pass else: self.update_gui() # elif msg_type == "LEAVE": # uuid,name,group = cmds # elif msg_type == "ENTER": # uuid,name,headers,ip = cmds # logger.warning((uuid,'name',headers,ip)) else: pass logger.debug('thread_loop closing.') self.thread_pipe = None n.stop()
def chat_task(ctx, pipe): print("Game started") print("Name: %s" % NAME) connected_players = 1 network_players = 1 leave_counter = 0 #Set up node for the game n = Pyre("") n.set_header("header_name", NAME) #Join the group n.join(GROUPNAME) #Start broadcasting node n.start() # Set up poller poller = zmq.Poller() poller.register( pipe, zmq.POLLIN ) #Local pipe (contains commands/messages we send through terminal) poller.register(n.socket(), zmq.POLLIN) # A while loop constantly polls for new items = PULL system while True: #Wait for new message to be polled. This function blocks until there is a new message items = dict(poller.poll()) #This are messages from ourselves if pipe in items: message_pipe = pipe.recv() if message_pipe.decode('utf-8') == STOP_COMMAND: break #check if the message is a number elif message_pipe.decode( 'utf-8').isdigit() == True and yourturn == True: #variable to keep the loop going until a correct number is given status = True #check which symbol you got assigned if playerX == True: while status == True: number = int(message_pipe.decode('utf-8')) #check if the spot is free if board[number] != "X" and board[number] != "O": status = False yourturn = False print("New status board:") board[number] = "X" showboard() #check for a winning combination if checkall("X") == True: print("You win!") n.whisper(OPPONENT, str(number).encode('utf-8')) break #when there's no winning combination, it's the other player's turn else: print("Waiting for opponent's move...") #let your opponent know which number you chose n.whisper(OPPONENT, str(number).encode('utf-8')) else: print("Spot taken, try again") message_pipe = pipe.recv() else: while status == True: number = int(message_pipe.decode('utf-8')) if board[number] != "X" and board[number] != "O": status = False yourturn = False print("New status board:") board[number] = "O" showboard() if checkall("O") == True: print("You win!") n.whisper(OPPONENT, str(number).encode('utf-8')) break else: print("Waiting for opponent's move...") n.whisper(OPPONENT, str(number).encode('utf-8')) else: print("Spot taken, try again") message_pipe = pipe.recv() elif message_pipe.decode( 'utf-8').isdigit() == True and yourturn == False: print("It's not your turn, wait for your opponent's move") #if the message isn't a number, it is send as a message to your opponent else: print("Sending message to opponent: %s" % message_pipe.decode('utf-8')) n.whisper(OPPONENT, message_pipe) # Received messages from system or messages from other peers else: cmds = n.recv() #print(">>>>>>>RECEIVED MESSAGE: ", cmds) msg_type = cmds.pop(0) player_uuid = uuid.UUID(bytes=cmds.pop(0)) #OPPONENT = player_uuid #print("player uuid: ", player_uuid) msg_name = cmds.pop(0) if msg_type.decode('utf-8') == "ENTER": headers = json.loads(cmds.pop(0).decode('utf-8')) network_players += 1 if network_players == 2: print( "--------------------------------------------------------------------------------" ) print("New player discovered in network") print("Name:", headers.get("header_name")) print( "--------------------------------------------------------------------------------" ) elif msg_type.decode('utf-8') == "JOIN": connected_players += 1 #check if there's stil room for a player if connected_players > 2: leave = "No free spot left" n.whisper(player_uuid, leave.encode('utf-8')) elif connected_players == 2: print( "--------------------------------------------------------------------------------" ) print("%s joined group" % headers.get("header_name"), cmds.pop(0).decode('utf-8')) print( "--------------------------------------------------------------------------------" ) #if there are 2 players, you know your opponent: OPPONENT = player_uuid showboard() #randomly choose if you want to start assign = random.randint(0, 1) if assign == 1: player_start = True n.whisper(OPPONENT, "$$Istart".encode('utf-8')) else: player_start = False n.whisper(OPPONENT, "$$Ustart".encode('utf-8')) elif msg_type.decode('utf-8') == "WHISPER": message_opponent = cmds.pop(0).decode('utf-8') if message_opponent == "No free spot left": leave_counter += 1 #if you get the message that you must leave from 2 other players, you are the third player if leave_counter == 2: print(message_opponent) break #if the random generators both got a compatible result, the game can start elif message_opponent == "$$Istart" and player_start == False: playerX = False yourturn = False print("You are symbol O") print("You opponent may start, please wait...") elif message_opponent == "$$Ustart" and player_start == True: playerX = True yourturn = True print("You are symbol X") print("You may start") print("Where do you want to place your X?") #when the results are incompatible: try again elif message_opponent == "$$Istart" and player_start == True: assign = random.randint(0, 1) if assign == 1: player_start = True n.whisper(OPPONENT, "$$Istart".encode('utf-8')) else: player_start = False n.whisper(OPPONENT, "$$Ustart".encode('utf-8')) elif message_opponent == "$$Ustart" and player_start == False: assign = random.randint(0, 1) if assign == 1: player_start = True n.whisper(OPPONENT, "$$Istart".encode('utf-8')) else: player_start = False n.whisper(OPPONENT, "$$Ustart".encode('utf-8')) #if you receive a number, this is your opponent's move elif message_opponent.isdigit() == True: yourturn = True print( "--------------------------------------------------------------------------------" ) print("Number opponent: ", message_opponent) print("New status board:") #check for a winning combination based on which player you are if playerX == True: board[int(message_opponent)] = "O" showboard() if checkall('O') == True: print("You loose!") break #if your opponent didn't make a winning combination, it's your turn else: print("Your turn") print("Where do you want to place your X?") else: board[int(message_opponent)] = "X" showboard() if checkall('X') == True: print("You loose!") break else: print("Your turn") print("Where do you want to place your O?") #if you just received a message, print it else: print("Opponent says: ", message_opponent) elif msg_type.decode('utf-8') == "EXIT": if connected_players == 2: print("%s left network" % headers.get("header_name")) connected_players -= 1 print("Total connected players: ", connected_players) leave_counter -= 1 print("Game stopped") n.stop()
def chat_task(ctx, pipe): print("Game started") print("Name: %s" %NAME) connected_players = 1 network_players = 1 leave_counter = 0 #Set up node for the game n = Pyre("") n.set_header("header_name", NAME) #Join the group n.join(GROUPNAME) #Start broadcasting node n.start() # Set up poller poller = zmq.Poller() poller.register(pipe, zmq.POLLIN) #Local pipe (contains commands/messages we send through terminal) poller.register(n.socket(), zmq.POLLIN) # A while loop constantly polls for new items = PULL system while True: #Wait for new message to be polled. This function blocks until there is a new message items = dict(poller.poll()) #This are messages from ourselves if pipe in items: message_pipe = pipe.recv() if message_pipe.decode('utf-8') == STOP_COMMAND: break #check if the message is a number elif message_pipe.decode('utf-8').isdigit() == True and yourturn == True: #variable to keep the loop going until a correct number is given status = True #check which symbol you got assigned if playerX == True: while status == True: number = int(message_pipe.decode('utf-8')) #check if the spot is free if board[number] != "X" and board[number] != "O": status = False yourturn = False print("New status board:") board[number] = "X" showboard() #check for a winning combination if checkall("X") == True: print("You win!") n.whisper(OPPONENT,str(number).encode('utf-8')) break #when there's no winning combination, it's the other player's turn else: print("Waiting for opponent's move...") #let your opponent know which number you chose n.whisper(OPPONENT,str(number).encode('utf-8')) else: print("Spot taken, try again") message_pipe = pipe.recv() else: while status == True: number = int(message_pipe.decode('utf-8')) if board[number] != "X" and board[number] != "O": status = False yourturn = False print("New status board:") board[number] = "O" showboard() if checkall("O") == True: print("You win!") n.whisper(OPPONENT,str(number).encode('utf-8')) break else: print("Waiting for opponent's move...") n.whisper(OPPONENT,str(number).encode('utf-8')) else: print("Spot taken, try again") message_pipe = pipe.recv() elif message_pipe.decode('utf-8').isdigit() == True and yourturn == False: print("It's not your turn, wait for your opponent's move") #if the message isn't a number, it is send as a message to your opponent else: print("Sending message to opponent: %s" %message_pipe.decode('utf-8')) n.whisper(OPPONENT,message_pipe) # Received messages from system or messages from other peers else: cmds = n.recv() #print(">>>>>>>RECEIVED MESSAGE: ", cmds) msg_type = cmds.pop(0) player_uuid = uuid.UUID(bytes=cmds.pop(0)) #OPPONENT = player_uuid #print("player uuid: ", player_uuid) msg_name = cmds.pop(0) if msg_type.decode('utf-8') == "ENTER": headers = json.loads(cmds.pop(0).decode('utf-8')) network_players += 1 if network_players == 2: print("--------------------------------------------------------------------------------") print("New player discovered in network") print("Name:", headers.get("header_name")) print("--------------------------------------------------------------------------------") elif msg_type.decode('utf-8') == "JOIN": connected_players += 1 #check if there's stil room for a player if connected_players > 2: leave = "No free spot left" n.whisper(player_uuid, leave.encode('utf-8')) elif connected_players == 2: print("--------------------------------------------------------------------------------") print("%s joined group" %headers.get("header_name"), cmds.pop(0).decode('utf-8')) print("--------------------------------------------------------------------------------") #if there are 2 players, you know your opponent: OPPONENT = player_uuid showboard() #randomly choose if you want to start assign = random.randint(0,1) if assign == 1: player_start = True n.whisper(OPPONENT, "$$Istart".encode('utf-8')) else: player_start = False n.whisper(OPPONENT, "$$Ustart".encode('utf-8')) elif msg_type.decode('utf-8') == "WHISPER": message_opponent = cmds.pop(0).decode('utf-8') if message_opponent == "No free spot left": leave_counter += 1 #if you get the message that you must leave from 2 other players, you are the third player if leave_counter == 2: print(message_opponent) break #if the random generators both got a compatible result, the game can start elif message_opponent == "$$Istart" and player_start == False: playerX = False yourturn = False print("You are symbol O") print("You opponent may start, please wait...") elif message_opponent == "$$Ustart" and player_start == True: playerX = True yourturn = True print("You are symbol X") print("You may start") print("Where do you want to place your X?") #when the results are incompatible: try again elif message_opponent == "$$Istart" and player_start == True: assign = random.randint(0,1) if assign == 1: player_start = True n.whisper(OPPONENT, "$$Istart".encode('utf-8')) else: player_start = False n.whisper(OPPONENT, "$$Ustart".encode('utf-8')) elif message_opponent == "$$Ustart" and player_start == False: assign = random.randint(0,1) if assign == 1: player_start = True n.whisper(OPPONENT, "$$Istart".encode('utf-8')) else: player_start = False n.whisper(OPPONENT, "$$Ustart".encode('utf-8')) #if you receive a number, this is your opponent's move elif message_opponent.isdigit() == True: yourturn = True print("--------------------------------------------------------------------------------") print("Number opponent: ",message_opponent) print("New status board:") #check for a winning combination based on which player you are if playerX == True: board[int(message_opponent)] = "O" showboard() if checkall('O') == True: print("You loose!") break #if your opponent didn't make a winning combination, it's your turn else: print("Your turn") print("Where do you want to place your X?") else: board[int(message_opponent)] = "X" showboard() if checkall('X') == True: print("You loose!") break else: print("Your turn") print("Where do you want to place your O?") #if you just received a message, print it else: print("Opponent says: ",message_opponent) elif msg_type.decode('utf-8') == "EXIT": if connected_players == 2: print("%s left network" %headers.get("header_name")) connected_players -= 1 print("Total connected players: ", connected_players) leave_counter -= 1 print("Game stopped") n.stop()
if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger("pyre") logger.setLevel(logging.INFO) logger = logging.getLogger() logger.setLevel(logging.DEBUG) ctx = zmq.Context() n = Pyre("Script") n.join('default group') n.start() poller = zmq.Poller() poller.register(n.socket(), zmq.POLLIN) try: while(True): items = dict(poller.poll(timeout=1000)) if n.socket() in items and items[n.socket()] == zmq.POLLIN: cmds = n.recv() msg_type = cmds.pop(0) msg_type = msg_type.decode('utf-8') if msg_type == "SHOUT": uuid,name,group,msg = cmds handle_msg(uuid,name,msg,n) elif msg_type == "WHISPER": uuid,name,msg = cmds handle_msg(uuid,name,msg,n) elif msg_type == "JOIN": uid,name,group = cmds
def thread_loop(self, context, pipe): n = Pyre(self.name) n.join(self.group) n.start() poller = zmq.Poller() poller.register(pipe, zmq.POLLIN) poller.register(n.socket(), zmq.POLLIN) front, back = zhelper.zcreate_pipe(context) poller.register(back, zmq.POLLIN) def wake_up(): #on app close this timer calls a closed socket. We simply catch it here. try: front.send('wake_up') except Exception as e: logger.debug('Orphaned timer thread raised error: %s' % e) t = Timer(self.time_sync_announce_interval, wake_up) t.daemon = True t.start() while (True): try: #this should not fail but it does sometimes. We need to clean this out. # I think we are not treating sockets correclty as they are not thread-save. items = dict(poller.poll()) except zmq.ZMQError: logger.warning('Socket fail.') continue if back in items and items[back] == zmq.POLLIN: back.recv() #timeout events are used for pupil sync. #annouce masterhood every interval time: if isinstance(self.time_sync_node, Clock_Sync_Master): n.shouts( self.group, SYNC_TIME_MASTER_ANNOUNCE + "%s" % self.clock_master_worthiness() + msg_delimeter + '%s' % self.time_sync_node.port) # synced slave: see if we should become master if we dont hear annoncement within time. elif isinstance(self.time_sync_node, Clock_Sync_Follower ) and not self.time_sync_node.offset_remains: if self.get_unadjusted_time( ) - self.last_master_announce > self.time_sync_wait_interval_short: self.time_sync_node.terminate() self.time_sync_node = Clock_Sync_Master( time_fn=self.get_time) n.shouts( self.group, SYNC_TIME_MASTER_ANNOUNCE + "%s" % self.clock_master_worthiness() + msg_delimeter + '%s' % self.time_sync_node.port) # unsynced slave or none should wait longer but eventually take over elif self.get_unadjusted_time( ) - self.last_master_announce > self.time_sync_wait_interval_long: if self.time_sync_node: self.time_sync_node.terminate() self.time_sync_node = Clock_Sync_Master( time_fn=self.get_time) n.shouts( self.group, SYNC_TIME_MASTER_ANNOUNCE + "%s" % self.clock_master_worthiness() + msg_delimeter + '%s' % self.time_sync_node.port) t = Timer(self.time_sync_announce_interval, wake_up) t.daemon = True t.start() if pipe in items and items[pipe] == zmq.POLLIN: message = pipe.recv() # message to quit if message.decode('utf-8') == exit_thread: break else: logger.debug("Shout '%s' to '%s' " % (message, self.group)) n.shouts(self.group, message) if n.socket() in items and items[n.socket()] == zmq.POLLIN: cmds = n.recv() msg_type = cmds.pop(0) msg_type = msg_type.decode('utf-8') if msg_type == "SHOUT": uuid, name, group, msg = cmds logger.debug("'%s' shouts '%s'." % (name, msg)) self._handle_msg(uuid, name, msg, n) elif msg_type == "WHISPER": uuid, name, msg = cmds logger.debug("'%s/' whispers '%s'." % (name, msg)) self._handle_msg_whisper(uuid, name, msg, n) elif msg_type == "JOIN": uuid, name, group = cmds if group == self.group: self.group_members[uuid] = name self.update_gui() elif msg_type == "EXIT": uuid, name = cmds try: del self.group_members[uuid] except KeyError: pass else: self.update_gui() # elif msg_type == "LEAVE": # uuid,name,group = cmds # elif msg_type == "ENTER": # uuid,name,headers,ip = cmds # logger.warning((uuid,'name',headers,ip)) else: pass logger.debug('thread_loop closing.') self.thread_pipe = None n.stop()
def network_manager(self, ctx, write_pipe, node_name, overlay_network_name, read_pipe): # create the poller to wait for messages from pipes and network poller = zmq.Poller() poller.register(write_pipe, zmq.POLLIN) # create the Pyre node object node = Pyre(node_name + str(uuid.uuid4())) # register node to the network, start it and register for events with # the poller. node.join(overlay_network_name) node.start() poller.register(node.socket(), zmq.POLLIN) while (True): # do stuff, aka wait and decode messages items = dict(poller.poll()) if write_pipe in items and items[write_pipe] == zmq.POLLIN: # here is where the thread receives internal data # check if we have to send something outside # or eventually die gracefully message = write_pipe.recv() # here I have a Command + a FardNetworkData object decoded_message = pickle.loads(message) command = decoded_message[0] network_data = decoded_message[1] if command == "$$STOP": # message to quit here break elif command == "$$GET_PEERS": # only synchronous command, retrieves the peers inside the # network of tasks. group = network_data.group peers = node.peers_by_group(group) list_of_peers = [] for peer in peers: list_of_peers.append(str(peer)) write_pipe.send(pickle.dumps(str(";".join(list_of_peers)))) elif command == "$$SEND_MESSAGE" in decoded_message: # send message to a single peer using Pyre # if requested, send back the message to the same node that # sent it. peer = network_data.peer network_data.sender_peer = str(node.uuid()) network_data.message_type = "peer" node.whisper(uuid.UUID(peer), pickle.dumps(network_data)) if network_data.auto_send and peer == network_data.sender_peer: read_pipe.send(pickle.dumps(network_data)) elif command == "$$SEND_TASK_MESSAGE": # send message to a group of identical tasks using Pyre. # Currently implemented with a shout that is ignored by a # receiver if the task name is different from his. # if requested, send back the message to the same node that # sent it. network_data.sender_peer = str(node.uuid()) network_data.message_type = "task" node.shout(group, pickle.dumps(network_data)) if network_data.auto_send: read_pipe.send(pickle.dumps(network_data)) elif command == "$$SEND_GROUP_MESSAGE": # send message to the whole application using Pyre # if requested, send back the message to the same node that # sent it. group = network_data.group network_data.sender_peer = str(node.uuid()) network_data.message_type = "group" node.shout(group, pickle.dumps(network_data)) if network_data.auto_send: read_pipe.send(pickle.dumps(network_data)) else: # here is where the thread receives data from the outside # decode messages and reroute them accordingly cmds = node.recv() msg_type = cmds.pop(0).decode('utf-8') peer_uuid = uuid.UUID(bytes=cmds.pop(0)) sender_node_name = cmds.pop(0).decode('utf-8') if msg_type == "SHOUT": group = cmds.pop(0).decode('utf-8') read_pipe.send(cmds.pop(0)) elif msg_type == "WHISPER": read_pipe.send(cmds.pop(0)) # elif msg_type == "ENTER": # headers = json.loads(cmds.pop(0).decode('utf-8')) # print("NODE_MSG HEADERS: %s" % headers) # for key in headers: # print("key = {0}, value = {1}".format(key, headers[key])) # print("NODE_MSG CONT: %s" % cmds) node.stop()
def thread_loop(self,context,pipe): n = Pyre(self.name) n.join(self.group) n.start() poller = zmq.Poller() poller.register(pipe, zmq.POLLIN) logger.debug(n.socket()) poller.register(n.socket(), zmq.POLLIN) while(True): try: #this should not fail but it does sometimes. We need to clean this out. # I think we are not treating sockets correclty as they are not thread-save. items = dict(poller.poll()) except zmq.ZMQError: logger.warning('Socket fail.') # print(n.socket(), items) if pipe in items and items[pipe] == zmq.POLLIN: message = pipe.recv() # message to quit if message.decode('utf-8') == "EXIT_THREAD": break logger.debug("Emitting to '%s' to '%s' " %(message,self.group)) n.shouts(self.group, message) if n.socket() in items and items[n.socket()] == zmq.POLLIN: cmds = n.recv() msg_type = cmds.pop(0) msg_type = msg_type.decode('utf-8') if msg_type == "SHOUT": uid,name,group,msg = cmds logger.debug("'%s' shouts '%s'."%(name,msg)) self.handle_msg(name,msg) elif msg_type == "WHISPER": pass # uid,name,group,msg = cmds # logger.debug("'%s' whispers '%s'."%(name,msg)) # self.handle_msg(name,msg) elif msg_type == "JOIN": uid,name,group = cmds if group == self.group: self.group_members[uid] = name self.update_gui() elif msg_type == "EXIT": uid,name = cmds try: del self.group_members[uid] except KeyError: pass else: self.update_gui() # elif msg_type == "LEAVE": # uid,name,group = cmds # elif msg_type == "ENTER": # uid,name,headers,ip = cmds logger.debug('thread_loop closing.') self.thread_pipe = None n.stop()
def chat_task(ctx, pipe): print("-----CAR PEER COMMUNICATION STARTED-----") print("Manufacturer: ", MANUFACTURER, " - Model: ", MODEL) connected_cars = 0 #Set up node for the car n = Pyre("") n.set_header("manufacturer", MANUFACTURER) n.set_header("model", MODEL) #Join the group 'chat' n.join(GROUPNAME) #Start broadcasting node n.start() # Set up poller poller = zmq.Poller() poller.register(pipe, zmq.POLLIN) #Local pipe (contains commands/messages we send through terminal) poller.register(n.socket(), zmq.POLLIN) # A while loop constantly polls for new items = PULL system while True: #Wait for new message to be polled. This function blocks until there is a new message items = dict(poller.poll()) #This are messages from ourselves, that we want to shout on the network if pipe in items: message = pipe.recv() # User stopped car if message.decode('utf-8') == STOP_COMMAND: break print(">>>>>> Sending out shout: %s" % message) n.shouts(GROUPNAME, message.decode('utf-8')) # Received messages from system or messages from other peers else: cmds = n.recv() print("--------------------------------------------------------------------------------") #print(">>>>>>>RECEIVED MESSAGE: ", cmds) msg_type = cmds.pop(0) car_uuid = uuid.UUID(bytes=cmds.pop(0)) msg_name = cmds.pop(0) if msg_type.decode('utf-8') == "ENTER": headers = json.loads(cmds.pop(0).decode('utf-8')) print(">>>> NEW CAR DISCOVERED IN NETWORK") print("---Manufacturer:", headers.get("manufacturer"), "--- Model:", headers.get("model")) elif msg_type.decode('utf-8') == "JOIN": print(">>>> NEW CAR JOINED GROUP <<", cmds.pop(0).decode('utf-8'),">>") connected_cars += 1 elif msg_type.decode('utf-8') == "SHOUT": print(">>>> RECEIVED SHOUT IN %s" % cmds.pop(0)) print("---Msg: %s" % cmds.pop(0)) elif msg_type.decode('utf-8') == "EXIT": print(">>>> CAR LEFT NETWORK") connected_cars -= 1 print("---Total connected cars: ", connected_cars) print("---Car_UUID: ", car_uuid) #print("---NODE_MSG REMAINING: %s" % cmds) print("-----CAR COMMUNICATION STOPPED-----") n.stop()
class Authority(): def __init__(self): self.node = Pyre("GAME_AUTH") self.node.set_header("AUTHORITY", "TRUE") self.node.start() self.node.join("world:position") self.node.join("ctf:teams") self.node.join("ctf:dropflag") self.node.join("ctf:gotflag") self.poller = zmq.Poller() self.poller.register(self.node.socket(), zmq.POLLIN) self.players = AuthorityPlayerManager() self.teams = {"blue": [], "red": []} self.level = SaveLevel('./assets/maps/CAPFLAG MAP NAT') red_spawn_pos = self.level.get_place(Place.RED_SPAWN) blue_spawn_pos = self.level.get_place(Place.BLUE_SPAWN) self.flags = { "blue": { "x": blue_spawn_pos[0], "y": blue_spawn_pos[1], "owner": '', "timer": 0 }, "red": { "x": red_spawn_pos[0], "y": red_spawn_pos[1], "owner": '', "timer": 0 } } self.serve() def set_teams(self): blue_players = self.teams["blue"] red_players = self.teams["red"] # Check for removed players in RED for i, playerUUID in enumerate(red_players): if playerUUID not in self.players.players.keys(): red_players.pop(i) # Check for removed players in BLUE for i, playerUUID in enumerate(blue_players): if playerUUID not in self.players.players.keys(): blue_players.pop(i) # Add new players for playerUUID, player in self.players.players.items(): if not (playerUUID in blue_players or playerUUID in red_players): if len(blue_players) > len(red_players): red_players.append(playerUUID) else: blue_players.append(playerUUID) print("Teams: " + "RED: " + ','.join(red_players) + " | BLUE: " + ','.join(blue_players)) self.node.shout("ctf:teams", bson.dumps(self.teams)) def set_flags(self, flag_info): return def update_flags(self): self.node.shout("ctf:flags", bson.dumps(self.flags)) def get_team_from_uuid(self, uuid): place = None if str(uuid) in self.teams['red']: place = Place.RED_SPAWN elif str(uuid) in self.teams['blue']: place = Place.BLUE_SPAWN return place def serve(self): clock = Clock() while True: clock.tick(60) # check network events = self.get_events() if events: try: for event in events: # Update the teams on JOIN and EXIT if event.type == 'JOIN': if event.group == 'ctf:dropflag': for team, flag in self.flags.items(): if flag['owner'] == '': self.node.shout( 'ctf:dropflag', bson.dumps({ 'x': flag['x'], 'y': flag['y'], 'team': team })) elif event.group == 'ctf:gotflag': for team, flag in self.flags.items(): if flag['owner'] != '': self.node.shout( 'ctf:gotflag', bson.dumps({ 'uuid': flag['owner'], 'team': team })) elif event.group == 'ctf:teams': self.players.set(self.node.peers()) self.set_teams() place = self.get_team_from_uuid( event.peer_uuid) pos = self.level.get_place(place) self.node.whisper( event.peer_uuid, bson.dumps({ 'type': 'teleport', 'x': pos[0], 'y': pos[1] })) elif event.type == 'EXIT': for team, flag in self.flags.items(): if flag['owner'] == str(event.peer_uuid): # flag owner is leaving, drop player = self.players.get(event.peer_uuid) flag['x'] = player.x flag['y'] = player.y flag['owner'] = '' self.node.shout( 'ctf:dropflag', bson.dumps({ 'x': flag['x'], 'y': flag['y'], 'team': team })) self.players.set(self.node.peers()) self.set_teams() elif event.type == 'SHOUT': if event.group == "world:position": new_position = bson.loads(event.msg[0]) network_player = self.players.get( event.peer_uuid) network_player.set_position(new_position) # check if flag has been captured for team, flag in self.flags.items(): if flag['owner'] != str(event.peer_uuid): continue tile = self.level.get_tile( new_position['x'], new_position['y']) if tile == TileType.RED_BLOCK and team == 'blue': # TODO: blue's flag has been captured spawn = Place.BLUE_SPAWN elif tile == TileType.BLUE_BLOCK and team == 'red': # TODO: red's flag has been captured spawn = Place.RED_SPAWN else: continue position = self.level.get_place(spawn) flag['x'] = position[0] flag['y'] = position[1] flag['owner'] = '' self.node.shout( 'ctf:dropflag', bson.dumps({ 'x': flag['x'], 'y': flag['y'], 'team': team })) if event.group == 'ctf:gotflags': flag_info = bson.loads(event.msg[0]) self.set_flags(flag_info) elif event.type == 'WHISPER': msg = bson.loads(event.msg[0]) network_player = self.players.get(event.peer_uuid) if msg['type'] == 'death_report': player = self.players.get(event.peer_uuid) previously_owned_flag = None if self.flags['blue']['owner'] == str( event.peer_uuid): previously_owned_flag = 'blue' elif self.flags['red']['owner'] == str( event.peer_uuid): previously_owned_flag = 'red' if previously_owned_flag: flag = self.flags[previously_owned_flag] flag['owner'] = '' self.node.shout( 'ctf:dropflag', bson.dumps({ 'x': player.x, 'y': player.y, 'team': previously_owned_flag })) place = self.get_team_from_uuid( event.peer_uuid) pos = self.level.get_place(place) self.node.whisper( event.peer_uuid, bson.dumps({ 'type': 'teleport', 'x': pos[0], 'y': pos[1] })) player.x = pos[0] player.y = pos[1] except Exception as e: print(e) import traceback print(traceback.format_exc()) pass for team, flag in self.flags.items(): if flag["owner"] != '': continue for uuid, player in self.players.players.items(): if flag['x'] == player.x and flag['y'] == player.y: team_place = self.get_team_from_uuid(uuid) pos = self.level.get_place(team_place) if team == 'red' and team_place == Place.RED_SPAWN or team == 'blue' and team_place == Place.BLUE_SPAWN: if player.x == pos[0] and player.y == pos[1]: continue self.node.shout( 'ctf:dropflag', bson.dumps({ 'x': pos[0], 'y': pos[1], 'team': team })) else: flag["owner"] = uuid self.node.shout( 'ctf:gotflag', bson.dumps({ 'uuid': uuid, 'team': team })) break def poll(self): return dict(self.poller.poll(0)) def peers(self): return self.node.peers() def stop(self): self.node.stop() def get_events(self): changes = self.poll() if self.node.socket() in changes and changes[ self.node.socket()] == zmq.POLLIN: events = self.node.recent_events() return events
class Transport(): '''Message transport mechanisms for LCAS''' def send(self, dest, ntuple): '''Send given ntuple to Transport named dest. If dest isn't listening for messages from this Transport, the message will (currently) be silently ignored.''' if self._prefix is not None: dest = self._prefix + dest self._pyre.shout(dest, json.dumps(ntuple).encode('utf-8')) # send() # Notes on subscribe # # The callback is called in the same thread that listens for pyre # messages, so the callback should start a new thread if it's # going to block or take a long time to run. # # The callback must take one positional argument, the tuple, and # can OPTIONALLY take a keyword argument (e.g. **kw). I use the # inspect module to detect this. May be too clever for my own # good. # # There can be only one callback for a given remote. If you call # subscribe again with the same remote, it raises an error. def subscribe(self, remote, callback): '''When a message is sent from a Transport named remote to this transport, call the passed callback with the ntuple as the first argument. If the callback takes **kw, it will also pass additional metadata such as the Transport name, UUID, and IP of the sender.''' if self._prefix is not None: remote = self._prefix + remote if remote in self._subscribers: raise TransportError( self, 'Transport.subscribe() was called a second time with the same remote (\"%s\"). You must call Transport.unsubscribe() before setting a new callback.' % (remote)) self._subscribers[remote] = callback # subscribe() def unsubscribe(self, remote): '''Stop listening for messages from remote.''' if self._prefix is not None: remote = self._prefix + remote if remote in self._subscribers: del self._subscribers[remote] # unsubscribe() def subscribe_all(self, callback): '''Call callback every time a message is sent from any remote Transport to this Transport.''' if self._subscribe_all is not None: raise TransportError( self, 'Transport.subscribe_all() was called a second time. You must call Transport.unsubscribe_all() before setting a new callback.' ) self._subscribe_all = callback # subscribe_all() def unsubscribe_all(self): self._subscribe_all = None # unsubscribe_all() # Notes on get() # # If you already subscribe to remote, temporarly overrides # the subscribe. The subscribed callback will NOT be called. # The subscription is replaced after get() returns. def get(self, remote): '''Block waiting for a message from a Transport named remote. Returns python namedtuple containing fields object, uuid, name, ip, datetime.''' if self._prefix is not None: remote = self._prefix + remote # The final python namedtuple to be returned needs to be shared # between get_callback() and get(). In python3, you can use # nonlocal, but in python2 you need a trick (storing in a # data structure). The actual value to be returned will # be ret[0]. ret = [None] # The event e will get set when a message is read by the # readthread. e = threading.Event() # This function is a callback used to detect the next message. # It stores the message in a Python namedtuple and sets the # event. def get_callback(tup, **kw): ret[0] = collections.namedtuple( 'TransportEnvelope', ['object', 'uuid', 'name', 'ip', 'datetime'])(tup, kw['uuid'], kw['name'], kw['ip'], kw['datetime']) # Inform get() that ret is ready to be returned. e.set() # get_callback() # Store the old callback, if any oldcb = self._subscribers.get(remote, None) # Set the subscription self._subscribers[remote] = get_callback # Wait for the callback to be called. e.wait() # Restore the old subscription, if any. if oldcb is not None: self._subscribers[remote] = oldcb else: del self._subscribers[remote] # Return the namedtuple. return ret[0] # get() def quit_federation(self): '''Send a quit message to all agents in this federation, and then close down the Transport.''' if self._run: self._pyre.shouts(self._globalchannel, u"QUIT") self._run = False # Wait for the readthread to finish self._readthread.join() # Tell Pyre to shut down self._pyre.stop() def is_running(self): '''Return the status of this Transport. If the Transport isn't running, you should not send it messages and the callbacks will not be called.''' return self._run ###################################################################### # All private methods below here def __init__(self, myname, port=None, prefix=None): # NOTE: Seems to be a bug in Pyre where you can't set the port. if port is not None: raise NotImplementedError( 'There is a bug in Pyre that prevents setting of the discovery port. If you require multiple federations of Pyre components, use prefix instead of port in Transport constructor.' ) # dict of remote name to callback. See subscribe method above. self._subscribers = {} # Callback for all message (or None if none registered) self._subscribe_all = None self._prefix = prefix # Attach the federation name as a prefix to both this channel # and the global channel. The global channel is currently # just used for QUIT messages. if prefix is not None: myname = prefix + myname self._globalchannel = prefix + "GLOBAL" else: self._globalchannel = "GLOBAL" self._pyre = Pyre(myname) if port is not None: self._pyre.set_port(port) self._pyre.join(myname) self._pyre.join(self._globalchannel) self._pyre.start() # Dict of (UUIDs => IP addresses) that have sent a valid ENTER message self._uuid2ip = {} self._run = True self._readthread = threading.Thread(target=self._readworker) self._readthread.start() # __init__() # Handle pyre messages. Run in self._readthread def _readworker(self): '''This method is called in a separate thread to handle messages sent over pyre. It dispataches to methods named for the pyre events (e.g. _ENTER).''' # Set up a poller so recv doesn't block. Possibly not needed # since we'll always get an event when the other agents quit, # but just in case something goes wrong, we want to be sure to # close down. poller = zmq.Poller() sock = self._pyre.socket() poller.register(sock, zmq.POLLIN) while self._run: # Wait until a message is received OR one second timeout. items = dict(poller.poll(1000)) if not (sock in items and items[sock] == zmq.POLLIN): # This should only happen if we time out. continue # There's an event waiting. Read and process it. event = self._pyre.recv() logger.debug('Transport %s-%s received event %s' % (self._pyre.uuid(), self._pyre.name(), event)) eventtype = event[0].decode('utf-8') # Sender's uuid and name sid = uuid.UUID(bytes=event[1]) name = event[2].decode('utf-8') # Make sure we've seen matching ENTER for all events if eventtype != 'ENTER' and sid not in self._uuid2ip: raise TransportProtocolError( self, 'Received event %s with no matching ENTER.' % (event)) continue if eventtype == 'ENTER': # Changed url = event[4].decode('utf-8') self._ENTER(sid, name, url) elif eventtype == 'JOIN': channel = event[3].decode('utf-8') self._JOIN(sid, name, channel) elif eventtype == 'SHOUT': channel = event[3].decode('utf-8') message = event[4].decode('utf-8') if channel == self._globalchannel and message == "QUIT": # Set ourself to stop running, close down pyre, exit # worker thread. self._run = False self._pyre.stop() break else: self._SHOUT(sid, name, channel, message) elif eventtype == 'WHISPER': message = event[3].decode('utf-8') self._WHISPER(sid, name, message) elif eventtype == 'LEAVE': channel = event[3].decode('utf-8') self._LEAVE(sid, name, channel) elif eventtype == 'EXIT': self._EXIT(sid, name) else: raise TransportProtocolError( self, 'Illegal event type in event %s' % (event)) # _readworker() # The following methods are named for the pyre event that this # instance has received. They are called automatically from the # worker thread that's listening for events. def _ENTER(self, sid, name, url): # We expect all connections to be tcp on some port. This regular # expression is used to extract the ip part. urlmatch = re.match('tcp://([0-9.]+):[0-9]+$', url) if urlmatch: ip = urlmatch.group(1) if is_valid_ip(ip): # Everything looks good. Add to list of valid uuids. self._uuid2ip[sid] = ip else: raise TransportSecurityError( self, 'Message from invalid IP address %s in ENTER %s %s %s. Check the function is_valid_ip() in Transport.py.' % (ip, sid, name, url)) else: raise TransportProtocolError( self, 'Malformed URL in ENTER %s %s %s' % (sid, name, url)) # _ENTER() def _JOIN(self, sid, name, channel): pass # _JOIN() def _SHOUT(self, sid, name, channel, message): now = datetime.datetime.now() logger.debug('In _SHOUT with %s %s %s %s' % (sid, name, channel, message)) #??? if name in self._subscribers: logger.debug('got a subscription') cb = self._subscribers[name] self._call_callback(cb, sid, name, channel, message, now) if self._subscribe_all is not None: cb = self._subscribe_all self._call_callback(cb, sid, name, channel, message, now) # _SHOUT() def _call_callback(self, cb, sid, name, channel, message, now): if inspect.getargspec(cb).keywords is None: cb(json.loads(message)) else: cb(message, uuid=sid, name=name, ip=self._uuid2ip[sid], datetime=now) # _call_callback def _WHISPER(self, sid, name, message): raise TransportProtocolError( self, 'Unexpected WHISPER from %s %s' % (sid, name)) # _WHISPER() def _LEAVE(self, sid, name, channel): pass # _LEAVE() def _EXIT(self, sid, name): # Remove sid from list of valid uuids. This should # never be an error since we check in _readworker(). del self._uuid2ip[sid]