def test_pacman(self): config = Config() image_size = config.get('DISPLAY_SIZE') prev_frame = Frame(bytearray(b'0' * image_size), 1) next_frame = Frame(bytearray(b'1' * image_size), 1) seq = IntermezzoPacman(prev_frame, next_frame) verify_length(seq, image_size)
def test_fram_length(self): config = Config() image_size = config.get('DISPLAY_SIZE') prev_frame = Frame(bytearray([0x66]*image_size), 100) next_frame = Frame(bytearray([0x66]*image_size), 100) seq = IntermezzoInvaders(prev_frame, next_frame) verify_length(seq, image_size)
def __init__(self, endpoint, factory, reactor=None): super().__init__(endpoint, factory) if reactor is None: from twisted.internet import reactor self.reactor = reactor self.config = Config() self.protocol = None self._system_name = None
def __init__(self, endpoint, factory, reactor=None): super().__init__(endpoint, factory, retryPolicy=backoffPolicy(), clock=reactor) self.reactor = _maybeGlobalReactor(reactor) self.config = Config() self.protocol = None self._system_name = None
def test_AnimateStill(self, sched): seq = FrameSequence() img_data = bytearray(Config().get('DISPLAY_SIZE')) seq.add_frame(Frame(img_data, 2000)) animated_seq = AnimateStill(seq[0]) assert Config().get('DISPLAY_HEIGHT') == len(animated_seq) assert sum([frame.duration for frame in animated_seq.frames]) == 2000 seq.add_frame(Frame(img_data, None)) animated_seq = AnimateStill(seq[1]) assert Config()['DISPLAY_DEFAULT_DELAY'] == sum( [frame.duration for frame in animated_seq.frames])
def CreateContent(contentCls): config = Config() startLogging() log_level = 'info' if config['DEBUG']: log_level = 'debug' setLogLevel(namespace='mqtt', levelStr=log_level) setLogLevel(namespace=contentCls.__name__, levelStr=log_level) factory = MQTTFactory(profile=MQTTFactory.PUBLISHER) myEndpoint = clientFromString(reactor, Config().get('MQTT_BROKER_CONN_STRING')) serv = contentCls(myEndpoint, factory) serv.startService(contentCls.__name__) return serv
def test_invaders(self): config = Config() width = config['DISPLAY_WIDTH'] height = config['DISPLAY_HEIGHT'] for s in range(height*2+2): s = _invaders(0) assert len(s) % width == 0, "Should be a multiple of %d, but %d bytes left." % (width, len(s) % width)
def IntermezzoInvaders(previous_frame: Frame, next_frame: Frame): """ Show Invaders from the top to the bottom switching programs. """ config = Config() prv = previous_frame.raw() nxt = next_frame.raw() seq = FrameSequence() frame_delay = config['INVADERS_FRAME_DELAY'] height = config['DISPLAY_HEIGHT'] width = config['DISPLAY_WIDTH'] size = height * width invader_height = int(len(_invaders(0)) / width) for step in range(height + invader_height + 4): # lets go from top to bottom img = bytearray().join([ nxt, bytearray(width), # Empty. bytearray(width), # Empty. _invaders(step), bytearray(width), # Empty. bytearray(width), # Empty. prv ]) if step == 0: frame_data = img[-1 * size - (step * width):] else: frame_data = img[-1 * size - (step * width):-(step * width)] seq.add_frame(Frame(frame_data, frame_delay)) return seq
def AnimateVerticalScroll(image: bytearray, line_duration: int) -> list: """ I let the content of a longer image scroll vertically up. :param image: The image that there is to scroll. :type image: bytearray :param line_duration: The duration in ms that each line should be shown. :type line_duration: int :return: List of frames that make up the scrolling motion. :rtype: list """ config = Config() display_width = config['DISPLAY_WIDTH'] animate_duration = config['TYPESETTER_ANIMATE_VERTICAL_SCROLL_DELAY'] nr_of_lines = len(image) / display_width # nr of lines does the whole image has. nr_of_scroll = int(nr_of_lines - config['DISPLAY_HEIGHT']) # number of lines there are to scroll f_start = 0 f_end = config['DISPLAY_SIZE'] frames = [] for nr in range(nr_of_scroll): if nr % 8 == 0: # On a full line, show for longer. duration = line_duration else: duration = animate_duration frames.append(Frame(image[f_start:f_end], duration=duration)) f_start += display_width f_end += display_width frames.append(Frame(image[-config['DISPLAY_SIZE']:], duration=line_duration)) return frames
def CreateService(ServiceCls): startLogging() setLogLevel(namespace='mqtt', levelStr='info') setLogLevel(namespace='__main__', levelStr='debug') factory = MQTTFactory(profile=MQTTFactory.PUBLISHER | MQTTFactory.SUBSCRIBER) myEndpoint = clientFromString(reactor, Config().get('MQTT_BROKER_CONN_STRING')) serv = ServiceCls(myEndpoint, factory) serv.startService(ServiceCls.__name__) return serv
def AnimateStill(still: Frame): """ I take a frame and create a sequence where the duration of the frame is visible. :param still: The image to animate. :type still: Frame :return: The sequence of frames with the time animation. :rtype: FrameSequence """ seq = FrameSequence() width, height = Config().get('DISPLAY_WIDTH'), Config().get('DISPLAY_HEIGHT') seq_duration = still.duration if not seq_duration: seq_duration = Config()['DISPLAY_DEFAULT_DELAY'] steps_ms = int(seq_duration / height) still_img = still.raw() still.duration = steps_ms for nr in range(height): frame = bytearray(still_img) frame[width*nr-1] = 0xff seq.add_frame(Frame(frame, steps_ms)) seq[-1].duration += seq_duration - seq.duration # Add the steps_ms missing because of the division. return seq
def _create_graph_line(self, values): string, fraction = values line = bytearray() MarkupLine(line, string, font8x8) display_width = Config()['DISPLAY_WIDTH'] line_location = int(display_width * fraction) for x in range(8): begin = x * display_width end = x * display_width + line_location c = begin while c < end: line[c] = (~(line[c]) & 0xff) c += 1 return line
def MarkupLine(image: bytearray, line: str, font: GenericFont): display_width = Config()['DISPLAY_WIDTH'] char_display_width = int(display_width / font.width) # maximum number of characters on a line line_image = bytearray(display_width * 8) # Bytes of the line. for j, c in enumerate(line[:char_display_width]): # Look at each character of a line try: glyph = font[ord(c)] except KeyError: glyph = font[ord("?")] xpos = j * font.width # Horizontal Position in the line. for n, glyph_line in enumerate(glyph): # Look at each row of the glyph (is just a byte) for x in range(8): # Look at the bits if testBit(glyph_line, x) != 0: line_image[xpos + n * display_width + x] = 0xff image.extend(line_image)
def test_program_retire(self): catalog = Catalog() catalog.now = lambda: 10 self._create_and_add_sequence(catalog, "First", ["Foo"]) f_iter = catalog.frames_iter() assert bytearray(b"Foo") == next(f_iter).raw()[0: 3] # Only foo is shown assert bytearray(b"Foo") == next(f_iter).raw()[0:3] catalog.now = lambda: 20 # Time passes self._create_and_add_sequence(catalog, "Second", ["Bar"]) assert bytearray(b"Bar") == next(f_iter).raw()[0:3] assert bytearray(b"Foo") == next(f_iter).raw()[0:3] assert bytearray(b"Bar") == next(f_iter).raw()[0:3] catalog.now = lambda: 20 + Config()["PROGRAM_RETIREMENT_AGE"] assert bytearray(b"Foo") == next(f_iter).raw()[ 0:3] # Foo now gets retired. assert bytearray(b"Bar") == next(f_iter).raw()[0:3] assert bytearray(b"Bar") == next(f_iter).raw()[0:3] self._create_and_add_sequence(catalog, "Second", ["Bar2"]) # "Second" got updated assert bytearray(b"Bar2") == next(f_iter).raw()[0:4] catalog.now = lambda: 30 + Config()["PROGRAM_RETIREMENT_AGE"] assert bytearray(b"Bar2") == next(f_iter).raw()[ 0:4] # Still exists, because "Second" was updated.
def _create_and_add_sequence(self, catalog, program_name, sequence_content, valid_time=None): seq = FrameSequence() # Frame(bytearray(b'Bar') * int(3456/3) seq.program = program_name if isinstance(sequence_content[0], Frame): seq.frames = sequence_content else: seq.frames = [ Frame(bytearray(f.encode() * int(3456 / len(f))), 10) for f in sequence_content ] seq.valid_time = valid_time if valid_time is not None else Config( )['PROGRAM_RETIREMENT_AGE'] catalog.add_program(program_name, seq) return seq
def IntermezzoWipe(previous_frame: Frame, next_frame: Frame): config = Config() wipe_frame_delay = config['INTERMEZZO_WIPE_FRAME_DELAY'] wipe_frame_step_size = config['INTERMEZZO_WIPE_FRAME_STEP_SIZE'] prv = previous_frame.raw() nxt = next_frame.raw() seq = FrameSequence() height = config['DISPLAY_HEIGHT'] width = config['DISPLAY_WIDTH'] sep = bytearray([0x00, 0x00, 0x40, 0x60, 0x80, 0x80, 0xff, 0x00]) sep_len = len(sep) for step in range(wipe_frame_step_size, width - wipe_frame_step_size - sep_len, wipe_frame_step_size): img_data = bytearray() for row in range(0, height): start = width * row img_data.extend(nxt[start:start + step] + sep + prv[start + step + sep_len:start + width]) seq.add_frame(Frame(img_data, wipe_frame_delay)) return seq
def IntermezzoPacman(previous_frame: Frame, next_frame: Frame): config = Config() frame_move = config['PACMAN_MOVE'] frame_delay = config['PACMAN_DELAY'] prv = previous_frame.raw() nxt = next_frame.raw() seq = FrameSequence() height = config['DISPLAY_HEIGHT'] width = config['DISPLAY_WIDTH'] spacer = bytearray([0x00, 0x00, 0x00]) pacmans = [Pacman1, Pacman2] i = 0 for step in range(0, width + len(spacer) + len(Pacman1[0]), frame_move): i += 1 img_data = bytearray() for row_nr in range(height): prv_row = prv[row_nr * width:(row_nr + 1) * width] nxt_row = nxt[row_nr * width:(row_nr + 1) * width] row = prv_row + pacmans[i % 2][row_nr] + spacer + nxt_row img_data.extend(row[step:step + width]) seq.add_frame(Frame(img_data, frame_delay)) return seq
class GenericContent(ClientService): def __init__(self, endpoint, factory, reactor=None): super().__init__(endpoint, factory) if reactor is None: from twisted.internet import reactor self.reactor = reactor self.config = Config() self.protocol = None self._system_name = None def startService(self, name): log.info("starting MQTT Content Publisher Service") # invoke whenConnected() inherited method self._system_name = name self.whenConnected().addCallback(self.connectToBroker) ClientService.startService(self) @inlineCallbacks def connectToBroker(self, protocol): ''' Connect to MQTT broker ''' self.protocol = protocol self.protocol.onDisconnection = self.onDisconnection self.protocol.setWindowSize(3) try: yield self.protocol.connect(self._system_name, keepalive=60) except Exception as e: self.log.error("Connecting to {broker} raised {excp!s}", broker=self.config.get('MQTT_BROKER_CONN_STRING'), excp=e) else: self.log.info("Connected to {broker}", broker=self.config.get('MQTT_BROKER_CONN_STRING')) self.reactor.callLater(0, self.onBrokerConnected) self_name = self.__class__.__name__ self.publish(topic=LEDSLIE_TOPIC_STATS_BASE+self_name, message="%s now (re-)connected" % self_name) def onBrokerConnected(self): log.info("onBrokerConnected called") def onDisconnection(self, reason): ''' get notfied of disconnections and get a deferred for a new protocol object (next retry) ''' log.debug("<Connection was lost !> <reason={r}>", r=reason) self.whenConnected().addCallback(self.connectToBroker) def publish(self, topic, message, qos=0, retain=False): if hasattr(message, 'serialize'): message = message.serialize() self.log.debug("To '{topic}', Published: '{data}'", topic=topic, data=message) return self.protocol.publish(topic, message, qos, retain) def remove_display(self, program_name): """ Remove the program from the display. :param program_name: The name of the program to remove :type program_name: str :return: The deferred that's called when the command has been send. :rtype: deferred """ return self.publish(LEDSLIE_TOPIC_SEQUENCES_PROGRAMS[:-1] + program_name, "")
return shifted_data class FakeSerialPort(object): def __init__(self, protocol): log.warn("Starting the FakeSerialPort") self.protocol = protocol self.protocol.transport = self def write(self, data): log.info("FAKE WRITING #%d bytes" % len(data)) if __name__ == '__main__': log = Logger(__file__) config = Config(envvar_silent=False) scheduler = CreateService(Scheduler) scheduler.add_intermezzo(IntermezzoWipe) scheduler.add_intermezzo(IntermezzoInvaders) scheduler.add_intermezzo(IntermezzoPacman) led_screen = LEDScreen() serial_port = config.get('SERIAL_PORT') if serial_port == 'fake': log.warn("FAKE SERIAL SELECTED.") FakeSerialPort(led_screen) else: baudrate = config.get('SERIAL_BAUDRATE') log.info("REAL Serialport %s @ %s" % (serial_port, baudrate)) RealSerialPort(led_screen, serial_port, reactor, baudrate=baudrate) scheduler.led_screen = led_screen reactor.run()
and destination_code not in DestinationCode_ignore): self.log.warn("Missing DestinationCode: %s = %s" % (destination_code, trans['DestinationName50'])) # 'LinePublicNumber' -- '2' # 'JourneyNumber' -- 8, # 'DestinationCode' -- 'NSN' # 'DestinationName50' -- 'Nieuw Sloten' # 'ExpectedArrivalTime' -- '2017-12-17T00:35:15' def publish_ov_display(self, info_lines: list) -> Deferred: def _logAll(*args): self.log.debug("all publishing complete args={args!r}", args=args) if not info_lines: return msg = TextTripleLinesLayout() msg.lines = info_lines msg.line_duration = self.config["OVINFO_LINE_DELAY"] msg.valid_time = 60 # Information is only valid for a minute. msg.program = 'ovinfo' msg.size = '6x7' msg.lines = info_lines d = self.publish(topic=LEDSLIE_TOPIC_TYPESETTER_3LINES, message=msg, qos=1) d.addCallbacks(_logAll, self._logFailure) return d if __name__ == '__main__': ns = __file__.split(os.sep)[-1] Config(envvar_silent=False) CreateContent(OVInfoContent) reactor.run()
class GenericProcessor(ClientService): subscriptions = () def __init__(self, endpoint, factory, reactor=None): super().__init__(endpoint, factory, retryPolicy=backoffPolicy(), clock=reactor) self.reactor = _maybeGlobalReactor(reactor) self.config = Config() self.protocol = None self._system_name = None def startService(self, name): log.info("starting MQTT Client Subscriber&Publisher Service") # invoke whenConnected() inherited method self.whenConnected().addCallback(self.connectToBroker) ClientService.startService(self) self._system_name = name @inlineCallbacks def connectToBroker(self, protocol): ''' Connect to MQTT broker ''' self.protocol = protocol self.protocol.onPublish = self.onPublish self.protocol.onDisconnection = self.onDisconnection self.protocol.setWindowSize(3) self.stats_task = task.LoopingCall(self.publish_vital_stats) self.stats_task.start(5.0, now=False) try: yield self.protocol.connect(self._system_name, keepalive=60) yield self.subscribe() except Exception as e: log.error("Connecting to {broker} raised {excp!s}", broker=self.config.get('MQTT_BROKER_CONN_STRING'), excp=e) else: log.info("Connected and subscribed to {broker}", broker=self.config.get('MQTT_BROKER_CONN_STRING')) self.reactor.callLater(0, self.onBrokerConnected) self_name = self.__class__.__name__ self.publish(topic=LEDSLIE_TOPIC_STATS_BASE + self_name, message="%s (re-)connected" % self_name) def onBrokerConnected(self): log.info("onBrokerConnected called") def subscribe(self): def _logFailure(failure): log.debug("subscriber reported {message}", message=failure.getErrorMessage()) return failure def _logGrantedQoS(value): log.debug("subscriber response {value!r}", value=value) return True def _subscribe_topic(response, topic, qos): log.info("subscriber response {value!r}", value=response) return self.protocol.subscribe(topic, qos) d = Deferred() for topic, qos in self.subscriptions: d.addCallback(_subscribe_topic, topic, qos) d.addErrback(_logFailure) d.callback("Start") return d def onPublish(self, topic, payload, qos, dup, retain, msgId): raise NotImplemented() def publish(self, topic, message, qos=0, retain=False): if isinstance(message, bytes): message = bytearray(message) elif isinstance(message, GenericMessage): message = message.serialize() return self.protocol.publish(topic, message, qos, retain=retain) def _logPublishFailure(self, failure): log.debug("publisher reported {message}", message=failure.getErrorMessage()) return failure def publish_vital_stats(self): pass def onDisconnection(self, reason): ''' get notfied of disconnections and get a deferred for a new protocol object (next retry) ''' log.info("<Connection was lost !> <reason={r}>", r=reason) self.whenConnected().addCallback(self.connectToBroker)
def __init__(self): self._config = Config()
def test_publishProgress(self, progress): progress.publishProgress() assert 1 == len(progress.protocol._published_messages) assert progress.protocol._published_messages[-1][0].endswith("/progress") frame = base64.b64decode(json.loads(progress.protocol._published_messages[-1][1].decode())[0][0][0]) assert Config()["DISPLAY_SIZE"] == len(frame)