First commit
This commit is contained in:
+139
@@ -0,0 +1,139 @@
|
||||
"""Treiber fuer die Grossanzeige: Telegramme bauen, senden, Antwort pruefen."""
|
||||
|
||||
import time
|
||||
|
||||
import migra
|
||||
import config
|
||||
|
||||
|
||||
class Display:
|
||||
"""Sendet Telegramme an die Anzeige und wertet das Antworttelegramm aus.
|
||||
|
||||
Das Antworttelegramm ist zugleich die Flusskontrolle: Nach seinem Erhalt
|
||||
darf sofort das naechste Telegramm folgen (Handbuch 3.5). Ohne Antwort
|
||||
muesste zwischen den Telegrammen "Empfangstimeout + 150 ms" gewartet werden.
|
||||
"""
|
||||
|
||||
def __init__(self, transport,
|
||||
dest=config.DISPLAY_ADDR,
|
||||
src=config.HOST_ADDR,
|
||||
checksum=config.USE_CHECKSUM,
|
||||
response=config.WANT_RESPONSE):
|
||||
self._t = transport
|
||||
self._dest = dest
|
||||
self._src = src
|
||||
self._checksum = checksum
|
||||
self._response = response
|
||||
|
||||
def send(self, data_unit):
|
||||
"""Data-Unit rahmen, senden und ggf. auf das Antworttelegramm warten."""
|
||||
frame = migra.build_telegram(
|
||||
data_unit,
|
||||
dest=self._dest,
|
||||
src=self._src,
|
||||
checksum=self._checksum,
|
||||
response=self._response,
|
||||
)
|
||||
self._t.write(frame)
|
||||
if self._response:
|
||||
migra.parse_response(self._t.read_response(), src=self._src)
|
||||
else:
|
||||
# Ohne Antwort ist die Anzeige erst nach Abarbeitung des Telegramms
|
||||
# wieder empfangsbereit: Empfangstimeout + 150 ms (Handbuch 3.5).
|
||||
time.sleep(config.QUIET_TIME)
|
||||
|
||||
# -- Hoehere Funktionen ------------------------------------------------
|
||||
def clear(self):
|
||||
self.send(migra.DataUnit().fill(config.BACKGROUND))
|
||||
|
||||
def set_brightness(self, percent):
|
||||
self.send(migra.DataUnit().brightness(percent, color=config.COLOR))
|
||||
|
||||
def show_lines(self, *lines, clear=False):
|
||||
"""Zeilen ab Zeile 1 auf die Anzeige schreiben.
|
||||
|
||||
Baut ein einziges Telegramm: pro Zeile Cursor setzen und den Text
|
||||
schreiben. Das Gradzeichen (config.DEGREE_MARKER) wird ueber einen kurzen
|
||||
Zeichensatzwechsel eingefuegt.
|
||||
|
||||
Jede Zeile wird mit Leerzeichen auf volle Breite (CHARS_PER_LINE)
|
||||
aufgefuellt und damit *an Ort und Stelle* ueberschrieben -- ohne die
|
||||
Anzeige vorher komplett zu loeschen. Das vermeidet das sichtbare Blitzen
|
||||
bei zyklischen Updates und haelt das Telegramm kurz (sonst kommt das
|
||||
naechste zu frueh und das letzte Zeichen kann verloren gehen).
|
||||
clear=True erzwingt zusaetzlich ein Vollbild-Loeschen (einmal beim Start).
|
||||
|
||||
Reihenfolge: Die Zeilen werden von unten nach oben gezeichnet, damit die
|
||||
oberste Zeile zuletzt kommt. Grund: Ein Zeichensatzwechsel mitten im
|
||||
Telegramm beschaedigt die Home-Zelle (0,0) -- die oberste Zeile muss also
|
||||
nach allen Wechseln geschrieben werden, damit sie diese Zelle ueberschreibt.
|
||||
(Am Geraet verifiziert 2026-07-15.)
|
||||
"""
|
||||
if len(lines) > len(config.LINE_Y):
|
||||
raise ValueError("Anzeige hat nur %d Zeilen" % len(config.LINE_Y))
|
||||
|
||||
du = migra.DataUnit()
|
||||
if clear:
|
||||
du.fill(config.BACKGROUND)
|
||||
du.attributes(fg=config.COLOR, bg=config.BACKGROUND, blink=migra.STATIC)
|
||||
for i in reversed(range(len(lines))):
|
||||
self._emit_line(du, config.LINE_Y[i], lines[i])
|
||||
self.send(du)
|
||||
|
||||
def show_line(self, index, text, clear=False):
|
||||
"""Nur eine einzelne Zeile aktualisieren (eigenes, kurzes Telegramm).
|
||||
|
||||
Fuer zyklische Updates: die Zeile ohne Gradzeichen (also ohne
|
||||
Zeichensatzwechsel) laesst sich beliebig oft neu schreiben, ohne dass die
|
||||
Anzeige Bytes verschluckt. Zeilen *mit* Gradzeichen sollten dagegen selten
|
||||
und moeglichst per show_lines (Vollbild) gezeichnet werden.
|
||||
"""
|
||||
if not 0 <= index < len(config.LINE_Y):
|
||||
raise ValueError("ungueltiger Zeilenindex %d" % index)
|
||||
du = migra.DataUnit()
|
||||
if clear:
|
||||
du.fill(config.BACKGROUND)
|
||||
du.attributes(fg=config.COLOR, bg=config.BACKGROUND, blink=migra.STATIC)
|
||||
self._emit_line(du, config.LINE_Y[index], text)
|
||||
self.send(du)
|
||||
|
||||
def _emit_line(self, du, y, text):
|
||||
"""Eine Zeile bei Pixelzeile y ausgeben, auf volle Breite aufgefuellt."""
|
||||
text = text[:config.CHARS_PER_LINE].ljust(config.CHARS_PER_LINE)
|
||||
du.charset(config.CHARSET, spaced=config.CHARSET_SPACED)
|
||||
du.cursor(0, y)
|
||||
for i, part in enumerate(text.split(config.DEGREE_MARKER)):
|
||||
if i > 0:
|
||||
self._emit_degree(du)
|
||||
if part:
|
||||
du.text(part)
|
||||
|
||||
def _emit_degree(self, du):
|
||||
"""Das Gradzeichen einfuegen -- ueber Zeichensatzwechsel oder als Fallback."""
|
||||
if config.DEGREE_CHARSET is None:
|
||||
du.text(config.DEGREE_FALLBACK)
|
||||
return
|
||||
du.charset(config.DEGREE_CHARSET, spaced=config.CHARSET_SPACED)
|
||||
du.text(config.DEGREE_CHAR)
|
||||
du.charset(config.CHARSET, spaced=config.CHARSET_SPACED)
|
||||
|
||||
def close(self):
|
||||
self._t.close()
|
||||
|
||||
|
||||
def open_display(port=config.PORT, fake=False, response=config.WANT_RESPONSE):
|
||||
"""Transport passend zur Plattform oeffnen und Display-Objekt liefern."""
|
||||
if fake:
|
||||
from transport import FakeTransport
|
||||
t = FakeTransport(src=config.HOST_ADDR, display_addr=config.DISPLAY_ADDR)
|
||||
else:
|
||||
from transport import SerialTransport
|
||||
t = SerialTransport(
|
||||
port=port,
|
||||
baudrate=config.BAUDRATE,
|
||||
parity=config.PARITY,
|
||||
bytesize=config.BYTESIZE,
|
||||
stopbits=config.STOPBITS,
|
||||
timeout=config.RESPONSE_TIMEOUT,
|
||||
)
|
||||
return Display(t, response=response)
|
||||
Reference in New Issue
Block a user