def onMessage(self, payload, isBinary): if isBinary: logger.warning( "Binary message received ({} bytes): ignoring".format( len(payload))) return unicode_message = payload.decode('utf8', 'replace') try: message = json.loads(unicode_message) except json.JSONDecodeError: logger.exception( "Cannot load {}: ignoring".format(unicode_message)) return # message could be validated here with something like pydantic logger.debug("{} >>> {}".format(self, message)) try: if message["action"] == "publish": self.factory.publish(message["event"], message["channel"]) elif message["action"] == "subscribe": self.subscribe(message["channel"]) except Exception: logger.error( "Error while trying to process message={}: skipping".format( message), exc_info=True)
def status(self, value): if value not in self.STATUSES: logger.error( "Unknown status {} (must be one of {}): skipping".format( value, ", ".join(self.STATUSES))) return if self.status == value: logger.info("Status is already {}: skipping".format(value)) return self._status = value if self.status == 'playing': category = ArduinoProtocol.PLAYING self.on_playing() elif self.status == 'success': category = ArduinoProtocol.SUCCESS self.on_success() else: # status is disabled category = ArduinoProtocol.DISABLED for port in self.authenticators.keys(): self.send_serial({ArduinoProtocol.CATEGORY: category}, port)
def difficulty(self, value): if value not in self.available_difficulties: logger.error( "Unknown difficulty '{}' (must be one of {}): skipping".format( value, ", ".join(self.available_difficulties))) self._difficulty = value
def process_event(self, event, channel): callbacks = self._get_callbacks(event, channel) for callback in callbacks: try: args, kwargs = self._get_args_for_callback(event, channel, callback) except ValueError: logger.error( "Error while executing callback {}: ignoring".format(callback['callable'].__name__), exc_info=True) continue callback['callable'](*args, **kwargs)
def status(self, value): if value not in self.STATUSES: logger.error( "Unknown status {} (must be one of {}): skipping".format( value, ", ".join(self.STATUSES))) return if self.status == value: logger.info("Status is already {}: skipping".format(value)) return self._status = value category = ArduinoProtocol.PLAYING if self.status == 'playing' else ArduinoProtocol.DISABLED self.send_serial({ArduinoProtocol.CATEGORY: category})
def event_reactivate_permanently(self, index: int): logger.info("Reactivating laser index={}".format(index)) self.deactivated_lasers = [ x for x in self.deactivated_lasers if x != index ] self.publish_deactivated_lasers() try: with open(self.config['deactivation_file'], 'w+') as fh: json.dump(self.deactivated_lasers, fh) except Exception: logger.error("Unable to make reactivation persistent", exc_info=True) else: logger.info( "Laser index={} has been permanently reactivated (new deactivated list={})" .format(index, self.deactivated_lasers))
def send_serial(self, event, port=None): if port is None: if not self._serials: logger.error("No serial instance available: skipping") return serial = list(self._serials.values())[0] else: if port not in self._serials: logger.error("Port {} has no serial instance available: skipping".format(port)) return serial = self._serials[port] serial.send_event(event)
def printer_serial_event(self, event: str): if event != 'Ok': logger.error("Unknown serial event '{}': skipping".format(event)) return logger.info("Received Ok from printer: processing next instruction") self.printer_gcode_instructions.pop( 0) # This instruction is the one being acknowledged by the 'Ok' if not self.printer_gcode_instructions: logger.info("No more gcode instructions: going to idle state") return if self.printer_is_halted: logger.info("Printer is halted: waiting to resume") return next_instruction = self.printer_gcode_instructions[0] self.process_gcode_instruction(next_instruction)
def __init__(self, *args, **kwargs): self._difficulty = None super(LaserMaze, self).__init__(*args, **kwargs) self.laser_prefix = self.config['laser_prefix'] self.default_difficulty = self.config['default_difficulty'] self.difficulty_settings = self.config['difficulty_settings'] try: with open(self.config['deactivation_file'], "r") as fh: self.deactivated_lasers = json.loads(fh.read()) except Exception: logger.error("Unable to load deactivation file", exc_info=True) self.deactivated_lasers = [] self.laser_alarm_counters = {} reactor.callLater(3, self.init_arduino)
def event_deactivate_permanently(self, index: int): if index in self.deactivated_lasers: logger.info( "Laser index={} is already deactivated: skipping".format( index)) else: logger.info("Deactivating laser index={}".format(index)) self.deactivated_lasers = sorted(self.deactivated_lasers + [index]) self.publish_deactivated_lasers() try: with open(self.config['deactivation_file'], 'w+') as fh: json.dump(self.deactivated_lasers, fh) except Exception: logger.error("Unable to make deactivation persistent", exc_info=True) else: logger.info( "Laser index={} has been permanently deactivated (new deactivated list={})" .format(index, self.deactivated_lasers))
def onMessage(self, payload, isBinary): if isBinary: logger.warning("Binary message received ({} bytes): ignoring".format(len(payload))) return unicode_message = payload.decode('utf8', 'replace') try: message = json.loads(unicode_message) except json.JSONDecodeError: logger.warning("Cannot load {}: ignoring".format(unicode_message)) return # message could be validated here with something like pydantic try: logger.info("{} <<< {}".format(message['channel'], message['event'])) self.factory.process_event(message['event'], message['channel']) except Exception: logger.error("Error while trying to process message={}".format(message), exc_info=True)
def _get_callback_inspection_data(self, callable): callback_parameters = inspect.signature(callable).parameters inspection_data = { 'callable': callable, 'channel': callable.channel, 'filter': callable.filter, 'pass_channel': False, 'variable_kwargs': False, 'kwargs': {}, } # This inspection is not 100% proof. Positional only arguments or cases that I would not think about can mess # with this logic, but at least it covers a lot of cases. for param in callback_parameters.values(): if param.kind == param.POSITIONAL_ONLY: if not inspection_data['pass_channel']: inspection_data['pass_channel'] = True else: logger.error( "Multiple positional only parameters detected in {}. Only one for the channel is " "supported.".format(callable.__name__)) elif param.kind == param.VAR_KEYWORD: # If there is one variable keyword argument in the signature, we don't need to check if some event # fields need to be popped, we already know we can pass all kwargs inspection_data['variable_kwargs'] = True elif param.kind == param.POSITIONAL_OR_KEYWORD: inspection_data['kwargs'][param.name] = { 'required': param.default is param.empty, 'annotation': ... if param.annotation is param.empty else param.annotation } else: # Only inspect._ParameterKind.VAR_POSITIONAL left, but variable positional arguments are not used pass return inspection_data
def clientConnectionFailed(self, connector, reason): logger.error('Connection failed. Reason: {}'.format(reason)) ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)
def clientConnectionLost(self, connector, reason): logger.error('Connection lost (reason={})'.format(reason)) ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
def log_published_error(self, message): logger.error(message) self.publish({'hostname': socket.gethostname(), 'log': message}, 'error')