def _handle_incoming_zmq(self): """Executes in separate thread: _zmq_messaging_thread.""" # NOTE - We shouldn't need the lock here because access to the # zmq socket should ONLY ever happen in one thread (this one!) # and logging is already threadsafe - we delegate operations # that might need the lock to _handle_zmq_msg(). # Initialise sockets zmq_poller = zmq.Poller() zmq_termination_reply_socket = self.zmq_context.socket(zmq.REP) zmq_reply_socket = self.zmq_context.socket(zmq.REP) zmq_reply_socket.bind( ZMQ_ADDRESS_LOCALHOST.format(port=self.zmq_req_port) ) zmq_termination_reply_socket.bind( ZMQ_ADDRESS_INPROC.format( identifier=self.zmq_manager_term_identifier ) ) zmq_poller.register(zmq_reply_socket, zmq.POLLIN) zmq_poller.register(zmq_termination_reply_socket, zmq.POLLIN) # Provide a method to loop over sockets that have data def _loop_over_sockets(): term = False for sock in socks_with_data: if sock is zmq_termination_reply_socket: term = True elif not term: # If we're not in the process of terminating... msg = sock.recv().decode() reply = self._handle_zmq_msg(msg) if reply is None: log.warning(WARN_NO_REPLY) reply = self._encapsulate_reply(self._generate_error()) sock.send(ElementTree.tostring(reply)) return term # Poll for messages while True: socks_with_data = dict(zmq_poller.poll()) if socks_with_data: term = _loop_over_sockets() if term: break # Cleanup ZMQ zmq_poller.unregister(zmq_reply_socket) zmq_poller.unregister(zmq_termination_reply_socket) zmq_reply_socket.close() zmq_termination_reply_socket.close()
def __init__(self): """Default constructor - Creates a new SensorManager.""" description = "Manage Yarely sensors" # The parent constructor provides config and logging and gets a # starting set of handlers using this classes _init_handlers() method. super().__init__(ZMQ_SENSORMANAGER_REP_PORT, description) self.registered = False # Setup for ZMQ Scheduler Messaging sensor_term_id = "sensormanager_term_{id}" self.zmq_sensormanager_term_identifier = sensor_term_id.format( id=id(self)) self.zmq_scheduler_req_addr = ZMQ_ADDRESS_LOCALHOST.format( port=ZMQ_SENSORMANAGER_REQ_PORT) self.zmq_scheduler_request_queue = queue.Queue() # Q of infinite size
def start_handler(self, handler_stub): """Start a new subprocess (Handler) using the specified command line arguments. Once it is started and has registered over ZMQ it will be sent the specified params via ZMQ. :param handler_stub: the uri string to be matched. :type handler_stub: a :class:`~manager.HandlerStub` instance that describes the Handler to be started. :return: the subprocess ID for the newly started Handler. :rtype: int """ handler = copy.deepcopy(handler_stub) params_over_zmq = handler.params_over_zmq if hasattr( handler, 'params_over_zmq' ) else dict() command_line_args = handler.command_line_args if hasattr( handler, 'command_line_args' ) else list() command_line_args.append( ZMQ_ADDRESS_LOCALHOST.format(port=self.zmq_req_port) ) subproc = SubprocessExecutionWithErrorCapturing( command_line_args, params_over_zmq ) with self._lock: if ( self._stop_request.is_set() or not self._handler_status_check_thread.is_alive() ): msg = ('Cannot start handler - handler status checker stopped ' 'or stopping') raise ManagerNotExecutingError(msg) subprocess_id = subproc.start() self._executing_handlers[subprocess_id] = subproc return subprocess_id
def _make_client(self): self.client = self.context.socket(zmq.REQ) self.client.connect( ZMQ_ADDRESS_LOCALHOST.format(port=ZMQ_DISPLAYCONTROLLER_REP_PORT)) self.poll.register(self.client, zmq.POLLIN)
def _handle_incoming_zmq(self): """Handles incoming requests. Uses _handle_zmq_messages to map requests on to methods. """ # Create reply socket to subscription manager zmq_subsmanager_reply_socket = self.zmq_context.socket(zmq.REP) zmq_subsmanager_reply_socket.setsockopt( zmq.LINGER, ZMQ_SOCKET_LINGER_MSEC ) zmq_subsmanager_reply_socket.bind( ZMQ_ADDRESS_LOCALHOST.format(port=ZMQ_SUBSMANAGER_REQ_PORT) ) # Create a reply socket to the sensor manager zmq_sensormanager_reply_socket = self.zmq_context.socket(zmq.REP) zmq_sensormanager_reply_socket.setsockopt( zmq.LINGER, ZMQ_SOCKET_LINGER_MSEC ) zmq_sensormanager_reply_socket.bind( ZMQ_ADDRESS_LOCALHOST.format(port=ZMQ_SENSORMANAGER_REQ_PORT) ) # Create termination socket zmq_termination_reply_socket = self.zmq_context.socket(zmq.REP) zmq_termination_reply_socket.bind( ZMQ_ADDRESS_INPROC.format( identifier=self.zmq_scheduler_term_identifier ) ) # Register all sockets zmq_poller = zmq.Poller() zmq_poller.register(zmq_subsmanager_reply_socket, zmq.POLLIN) zmq_poller.register(zmq_sensormanager_reply_socket, zmq.POLLIN) zmq_poller.register(zmq_termination_reply_socket, zmq.POLLIN) # Provide a method to loop over sockets that have data. It tries to # find matching methods for incoming requests/replies with # _handle_zmq_msg(). def _loop_over_sockets(): term = False for sock in socks_with_data: if sock is zmq_termination_reply_socket: return True msg = sock.recv().decode() reply = self._handle_zmq_msg(msg) # Check if we got a valid reply from the method called. if reply is None: log.warning( "No reply generated, replying with error!" ) reply = self._encapsulate_reply(self._generate_error()) sock.send(ElementTree.tostring(reply)) return term # Look at all incoming messages while True: socks_with_data = dict(zmq_poller.poll()) if socks_with_data: term = _loop_over_sockets() if term: break # Cleanup ZMQ zmq_poller.unregister(zmq_subsmanager_reply_socket) zmq_poller.unregister(zmq_sensormanager_reply_socket) zmq_poller.unregister(zmq_termination_reply_socket) zmq_subsmanager_reply_socket.close() zmq_sensormanager_reply_socket.close() zmq_termination_reply_socket.close()
_TERMINATION_MARKER = object() QUEUE_TIMEOUT = 1 # Seconds WARN_NO_REPLY = 'Expected reply from Scheduler not received.' DISPLAY_STATE_POLLING_FREQ = 3 # Seconds # FIXME path DISPLAY_DEVICE_DIRECTORY = os.path.abspath( __file__)[:-len('display_controller.py')] DISPLAY_DEVICE_DRIVER_SUFFIX = '_display_device.py' DISPLAY_IS_ON = "IS_ON" DISPLAY_IS_OFF = "IS_OFF" DISPLAY_UNKNOWN_STATE = "UNKNOWN_STATE" LINESPEED_DISPLAY = termios.B9600 # FIXME ZMQ_SCHEDULER_ADDR = ZMQ_ADDRESS_LOCALHOST.format( port=ZMQ_DISPLAYCONTROLLER_REP_PORT) class DisplayControllerError(Exception): """Base class for display controller errors""" pass class DisplayController(threading.Thread): """ Starts a thread that can send power commands to the display asynchronously. """ def __init__(self, display_serial, analytics_tracking_id,
def _start_renderer(self, item, position, layout=None): """ Find the appropriate renderer for the content item, initialise it with an optional layout, find cached path to the content item and start the renderer subprocess. :param item: Content Item instance to be shown on the screen. :param layout: Optional layout attributes (see display_item). :param position: Optional position attribute (see display_item). :return: subprocess ID. """ # Get initial args and find the right renderer. # We also give the renderer a unique ID that it will use to communicate # back to Display Manager. args = get_initial_args(item.get_content_type()) module = ARG_RENDER_NAMESPACE + args['module'] renderer_uuid = str(uuid.uuid4()) cmd_args = [ self._get_yarely_module_starter_path(), '-m', module, '--uuid', renderer_uuid, ZMQ_ADDRESS_LOCALHOST.format(port=ZMQ_RENDERER_REQ_PORT) ] # We take the first URI from the content item to display. item_uri = str(item) # Try to get the item from the cache and overwrite its URI. if 'precache' in args and args['precache']: item_uri = self.cache.file_cached(item, strict=False) # Validate item_uri to make sure it can be scheduled at all if item_uri is None: raise RendererError() # Check if item_uri really is a URI. Some renderer don't like local # paths and prefer URIs (file://) instead. if ( 'param_type' in args and args['param_type'] == 'uri' and True not in (item_uri.startswith('http'), item_uri.startswith('udp'), item_uri.startswith('file'), item_uri.startswith('rtmp')) ): item_uri = platform.get_uri_from_local_path(item_uri) # Prepare the new renderer and start it log.info('Starting new renderer: {args!s}'.format(args=cmd_args)) params_over_zmq = {args['param_type']: item_uri} # Add the layout if layout is not None: params_over_zmq.update(layout) logging.debug("Send params over ZMQ: {}".format(params_over_zmq)) subp = SubprocessExecutionWithErrorCapturing( cmd_args, params_over_zmq ) # Start the renderer and store the reference in _executing_renderers. subprocess_id = subp.start() # Save the reference to the new renderer. renderer = ExecutingRenderer(subp, position, renderer_uuid, item) with self._renderers_lock: self._renderers[renderer_uuid] = renderer return subprocess_id