def launcher(): """Starts eye processes. Hosts the IPC Backbone and Logging functions. Reacts to notifications: ``launcher_process.should_stop``: Stops the launcher process ``eye_process.should_start``: Starts the eye process """ #Reliable msg dispatch to the IPC via push bridge. def pull_pub(ipc_pub_url, pull): ctx = zmq.Context.instance() pub = ctx.socket(zmq.PUB) pub.connect(ipc_pub_url) while True: m = pull.recv_multipart() pub.send_multipart(m) #The delay proxy handles delayed notififications. def delay_proxy(ipc_pub_url, ipc_sub_url): ctx = zmq.Context.instance() sub = zmq_tools.Msg_Receiver(ctx, ipc_sub_url, ('delayed_notify', )) pub = zmq_tools.Msg_Dispatcher(ctx, ipc_pub_url) poller = zmq.Poller() poller.register(sub.socket, zmq.POLLIN) waiting_notifications = {} while True: if poller.poll(timeout=250): #Recv new delayed notification and store it. topic, n = sub.recv() n['_notify_time_'] = time() + n['delay'] waiting_notifications[n['subject']] = n #When a notifications time has come, pop from dict and send it as notification for s, n in list(waiting_notifications.items()): if n['_notify_time_'] < time(): del n['_notify_time_'] del n['delay'] del waiting_notifications[s] pub.notify(n) #Recv log records from other processes. def log_loop(ipc_sub_url, log_level_debug): import logging #Get the root logger logger = logging.getLogger() #set log level if log_level_debug: logger.setLevel(logging.DEBUG) else: logger.setLevel(logging.INFO) #Stream to file fh = logging.FileHandler(os.path.join(user_dir, '{}.log'.format(app)), mode='w') fh.setFormatter( logging.Formatter( '%(asctime)s - %(processName)s - [%(levelname)s] %(name)s: %(message)s' )) logger.addHandler(fh) #Stream to console. ch = logging.StreamHandler() ch.setFormatter( logging.Formatter( '%(processName)s - [%(levelname)s] %(name)s: %(message)s')) logger.addHandler(ch) # IPC setup to receive log messages. Use zmq_tools.ZMQ_handler to send messages to here. sub = zmq_tools.Msg_Receiver(zmq_ctx, ipc_sub_url, topics=("logging", )) while True: topic, msg = sub.recv() record = logging.makeLogRecord(msg) logger.handle(record) ## IPC timebase = Value(c_double, 0) eyes_are_alive = Value(c_bool, 0), Value(c_bool, 0) zmq_ctx = zmq.Context() #Let the OS choose the IP and PORT ipc_pub_url = 'tcp://*:*' ipc_sub_url = 'tcp://*:*' ipc_push_url = 'tcp://*:*' # Binding IPC Backbone Sockets to URLs. # They are used in the threads started below. # Using them in the main thread is not allowed. xsub_socket = zmq_ctx.socket(zmq.XSUB) xsub_socket.bind(ipc_pub_url) ipc_pub_url = xsub_socket.last_endpoint.decode('utf8').replace( "0.0.0.0", "127.0.0.1") xpub_socket = zmq_ctx.socket(zmq.XPUB) xpub_socket.bind(ipc_sub_url) ipc_sub_url = xpub_socket.last_endpoint.decode('utf8').replace( "0.0.0.0", "127.0.0.1") pull_socket = zmq_ctx.socket(zmq.PULL) pull_socket.bind(ipc_push_url) ipc_push_url = pull_socket.last_endpoint.decode('utf8').replace( "0.0.0.0", "127.0.0.1") # Starting communication threads: # A ZMQ Proxy Device serves as our IPC Backbone ipc_backbone_thread = Thread(target=zmq.proxy, args=(xsub_socket, xpub_socket)) ipc_backbone_thread.setDaemon(True) ipc_backbone_thread.start() pull_pub = Thread(target=pull_pub, args=(ipc_pub_url, pull_socket)) pull_pub.setDaemon(True) pull_pub.start() log_thread = Thread(target=log_loop, args=(ipc_sub_url, 'debug' in sys.argv)) log_thread.setDaemon(True) log_thread.start() delay_thread = Thread(target=delay_proxy, args=(ipc_push_url, ipc_sub_url)) delay_thread.setDaemon(True) delay_thread.start() del xsub_socket, xpub_socket, pull_socket topics = ('notify.eye_process.', 'notify.player_process.', 'notify.world_process.', 'notify.player_drop_process.', 'notify.launcher_process.', 'notify.meta.should_doc', 'notify.circle_detector_process.should_start', 'notify.ipc_startup') cmd_sub = zmq_tools.Msg_Receiver(zmq_ctx, ipc_sub_url, topics=topics) cmd_push = zmq_tools.Msg_Dispatcher(zmq_ctx, ipc_push_url) while True: # Wait until subscriptions were successfull cmd_push.notify({'subject': 'ipc_startup'}) if cmd_sub.socket.poll(timeout=50): cmd_sub.recv() break if app == 'service': Process(target=service, name='service', args=(timebase, eyes_are_alive, ipc_pub_url, ipc_sub_url, ipc_push_url, user_dir, app_version)).start() elif app == 'capture': Process(target=world, name='world', args=( timebase, eyes_are_alive, ipc_pub_url, ipc_sub_url, ipc_push_url, user_dir, app_version, )).start() elif app == 'player': if len(sys.argv) > 2: rec_dir = os.path.expanduser(sys.argv[-1]) else: rec_dir = None Process(target=player_drop, name='player_drop', args=( rec_dir, ipc_pub_url, ipc_sub_url, ipc_push_url, user_dir, app_version, )).start() with Prevent_Idle_Sleep(): while True: # listen for relevant messages. if cmd_sub.socket.poll(timeout=1000): topic, n = cmd_sub.recv() if "notify.eye_process.should_start" in topic: eye_id = n['eye_id'] Process(target=eye, name='eye{}'.format(eye_id), args=(timebase, eyes_are_alive[eye_id], ipc_pub_url, ipc_sub_url, ipc_push_url, user_dir, app_version, eye_id, n.get('overwrite_cap_settings'))).start() elif "notify.player_process.should_start" in topic: Process(target=player, name='player', args=( n['rec_dir'], ipc_pub_url, ipc_sub_url, ipc_push_url, user_dir, app_version, )).start() elif "notify.player_drop_process.should_start" in topic: Process(target=player_drop, name='player', args=( n['rec_dir'], ipc_pub_url, ipc_sub_url, ipc_push_url, user_dir, app_version, )).start() elif "notify.circle_detector_process.should_start" in topic: Process(target=circle_detector, name='circle_detector', args=(ipc_push_url, n['pair_url'], n['source_path'], n['timestamps_path'])).start() elif "notify.meta.should_doc" in topic: cmd_push.notify({ 'subject': 'meta.doc', 'actor': 'launcher', 'doc': launcher.__doc__ }) else: if not active_children(): break for p in active_children(): p.join()
def launcher(): """Starts eye processes. Hosts the IPC Backbone and Logging functions. Reacts to notifications: ``launcher_process.should_stop``: Stops the launcher process ``eye_process.should_start``: Starts the eye process """ # Reliable msg dispatch to the IPC via push bridge. def pull_pub(ipc_pub_url, pull): ctx = zmq.Context.instance() pub = ctx.socket(zmq.PUB) pub.connect(ipc_pub_url) while True: m = pull.recv_multipart() pub.send_multipart(m) # The delay proxy handles delayed notififications. def delay_proxy(ipc_pub_url, ipc_sub_url): ctx = zmq.Context.instance() sub = zmq_tools.Msg_Receiver(ctx, ipc_sub_url, ("delayed_notify", )) pub = zmq_tools.Msg_Dispatcher(ctx, ipc_pub_url) poller = zmq.Poller() poller.register(sub.socket, zmq.POLLIN) waiting_notifications = {} TOPIC_CUTOFF = len("delayed_") while True: if poller.poll(timeout=250): # Recv new delayed notification and store it. topic, n = sub.recv() n["__notify_time__"] = time() + n["delay"] waiting_notifications[n["subject"]] = n # When a notifications time has come, pop from dict and send it as notification for s, n in list(waiting_notifications.items()): if n["__notify_time__"] < time(): n["topic"] = n["topic"][TOPIC_CUTOFF:] del n["__notify_time__"] del n["delay"] del waiting_notifications[s] pub.notify(n) # Recv log records from other processes. def log_loop(ipc_sub_url, log_level_debug): import logging # Get the root logger logger = logging.getLogger() # set log level logger.setLevel(logging.NOTSET) # Stream to file fh = logging.FileHandler( os.path.join(user_dir, "{}.log".format(parsed_args.app)), mode="w", encoding="utf-8", ) fh.setFormatter( logging.Formatter( "%(asctime)s - %(processName)s - [%(levelname)s] %(name)s: %(message)s" )) logger.addHandler(fh) # Stream to console. ch = logging.StreamHandler() ch.setFormatter( logging.Formatter( "%(processName)s - [%(levelname)s] %(name)s: %(message)s")) if log_level_debug: ch.setLevel(logging.DEBUG) else: ch.setLevel(logging.INFO) logger.addHandler(ch) # IPC setup to receive log messages. Use zmq_tools.ZMQ_handler to send messages to here. sub = zmq_tools.Msg_Receiver(zmq_ctx, ipc_sub_url, topics=("logging", )) while True: topic, msg = sub.recv() record = logging.makeLogRecord(msg) logger.handle(record) ## IPC timebase = Value(c_double, 0) eye_procs_alive = Value(c_bool, 0), Value(c_bool, 0) zmq_ctx = zmq.Context() # Let the OS choose the IP and PORT ipc_pub_url = "tcp://*:*" ipc_sub_url = "tcp://*:*" ipc_push_url = "tcp://*:*" # Binding IPC Backbone Sockets to URLs. # They are used in the threads started below. # Using them in the main thread is not allowed. xsub_socket = zmq_ctx.socket(zmq.XSUB) xsub_socket.bind(ipc_pub_url) ipc_pub_url = xsub_socket.last_endpoint.decode("utf8").replace( "0.0.0.0", "127.0.0.1") xpub_socket = zmq_ctx.socket(zmq.XPUB) xpub_socket.bind(ipc_sub_url) ipc_sub_url = xpub_socket.last_endpoint.decode("utf8").replace( "0.0.0.0", "127.0.0.1") pull_socket = zmq_ctx.socket(zmq.PULL) pull_socket.bind(ipc_push_url) ipc_push_url = pull_socket.last_endpoint.decode("utf8").replace( "0.0.0.0", "127.0.0.1") # Starting communication threads: # A ZMQ Proxy Device serves as our IPC Backbone ipc_backbone_thread = Thread(target=zmq.proxy, args=(xsub_socket, xpub_socket)) ipc_backbone_thread.setDaemon(True) ipc_backbone_thread.start() pull_pub = Thread(target=pull_pub, args=(ipc_pub_url, pull_socket)) pull_pub.setDaemon(True) pull_pub.start() log_thread = Thread(target=log_loop, args=(ipc_sub_url, parsed_args.debug)) log_thread.setDaemon(True) log_thread.start() delay_thread = Thread(target=delay_proxy, args=(ipc_push_url, ipc_sub_url)) delay_thread.setDaemon(True) delay_thread.start() del xsub_socket, xpub_socket, pull_socket topics = ( "notify.eye_process.", "notify.player_process.", "notify.world_process.", "notify.service_process", "notify.clear_settings_process.", "notify.player_drop_process.", "notify.launcher_process.", "notify.meta.should_doc", "notify.circle_detector_process.should_start", "notify.ipc_startup", ) cmd_sub = zmq_tools.Msg_Receiver(zmq_ctx, ipc_sub_url, topics=topics) cmd_push = zmq_tools.Msg_Dispatcher(zmq_ctx, ipc_push_url) while True: # Wait until subscriptions were successfull cmd_push.notify({"subject": "ipc_startup"}) if cmd_sub.socket.poll(timeout=50): cmd_sub.recv() break import logging if unknown_args: logging.warning(f"Unknown command-line arguments: {unknown_args}") if parsed_args.app == "service": cmd_push.notify({"subject": "service_process.should_start"}) elif parsed_args.app == "capture": cmd_push.notify({"subject": "world_process.should_start"}) elif parsed_args.app == "player": rec_dir = os.path.expanduser(parsed_args.recording) cmd_push.notify({ "subject": "player_drop_process.should_start", "rec_dir": rec_dir }) with Prevent_Idle_Sleep(): while True: # listen for relevant messages. if cmd_sub.socket.poll(timeout=1000): topic, n = cmd_sub.recv() if "notify.eye_process.should_start" in topic: eye_id = n["eye_id"] Process( target=eye, name="eye{}".format(eye_id), args=( timebase, eye_procs_alive[eye_id], ipc_pub_url, ipc_sub_url, ipc_push_url, user_dir, app_version, eye_id, n.get("overwrite_cap_settings"), parsed_args.hide_ui, ), ).start() elif "notify.player_process.should_start" in topic: Process( target=player, name="player", args=( n["rec_dir"], ipc_pub_url, ipc_sub_url, ipc_push_url, user_dir, app_version, ), ).start() elif "notify.world_process.should_start" in topic: Process( target=world, name="world", args=( timebase, eye_procs_alive, ipc_pub_url, ipc_sub_url, ipc_push_url, user_dir, app_version, parsed_args.port, parsed_args.hide_ui, ), ).start() elif "notify.clear_settings_process.should_start" in topic: Process(target=clear_settings, name="clear_settings", args=(user_dir, )).start() elif "notify.service_process.should_start" in topic: Process( target=service, name="service", args=( timebase, eye_procs_alive, ipc_pub_url, ipc_sub_url, ipc_push_url, user_dir, app_version, parsed_args.port, parsed_args.hide_ui, ), ).start() elif "notify.player_drop_process.should_start" in topic: Process( target=player_drop, name="player", args=( n["rec_dir"], ipc_pub_url, ipc_sub_url, ipc_push_url, user_dir, app_version, ), ).start() elif "notify.circle_detector_process.should_start" in topic: Process( target=circle_detector, name="circle_detector", args=(ipc_push_url, n["pair_url"], n["source_path"]), ).start() elif "notify.meta.should_doc" in topic: cmd_push.notify({ "subject": "meta.doc", "actor": "launcher", "doc": launcher.__doc__, }) else: if not active_children(): break for p in active_children(): p.join()