From e3794619b4216e9eaecc676efe80d8f7281bad48 Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Sun, 24 Oct 2021 19:13:32 -0700 Subject: [PATCH] OLED screen: Clean exit on error; proper handling of removable devices; signal handling; splash screen (#364) * Clean exit on errors * Avoid unnecessary scrolling * Fix bug where removable media wasn't updated * Add signal handler, solution from https://stackoverflow.com/questions/1112343/how-do-i-capture-sigint-in-python * Display a splash screen on startup * Introduce stop splash * Add parameters to enable the SIGTERM handling to take effect * Change to a less busy stop splash * Less busy start splash * Iterate on splash bitmaps * Iterate on splash bitmap * Add check for libopenjp2-7-dev in the start script * Iterate on splash bitmap * Iterate on splash bitmap --- src/oled_monitor/monitor_rascsi.service | 3 + src/oled_monitor/rascsi_oled_monitor.py | 106 ++++++++++++++++++------ src/oled_monitor/splash_start.bmp | Bin 0 -> 574 bytes src/oled_monitor/splash_stop.bmp | Bin 0 -> 658 bytes src/oled_monitor/start.sh | 11 ++- 5 files changed, 92 insertions(+), 28 deletions(-) create mode 100644 src/oled_monitor/splash_start.bmp create mode 100644 src/oled_monitor/splash_stop.bmp diff --git a/src/oled_monitor/monitor_rascsi.service b/src/oled_monitor/monitor_rascsi.service index 91a7d9ca..14f05466 100644 --- a/src/oled_monitor/monitor_rascsi.service +++ b/src/oled_monitor/monitor_rascsi.service @@ -6,6 +6,9 @@ After=network.target rascsi.service Type=simple Restart=always ExecStart=/home/pi/RASCSI/src/oled_monitor/start.sh +ExecStop=/bin/echo "Shutting down the OLED Monitor gracefully..." +ExecStop=/bin/pkill --signal 2 -f "python3 rascsi_oled_monitor.py" +ExecStop=/bin/sleep 2 StandardOutput=syslog StandardError=syslog SyslogIdentifier=RASCSIMON diff --git a/src/oled_monitor/rascsi_oled_monitor.py b/src/oled_monitor/rascsi_oled_monitor.py index 4300f816..bb9fe3a7 100755 --- a/src/oled_monitor/rascsi_oled_monitor.py +++ b/src/oled_monitor/rascsi_oled_monitor.py @@ -30,16 +30,49 @@ # THE SOFTWARE. from time import sleep from sys import argv, exit -import logging from board import I2C from adafruit_ssd1306 import SSD1306_I2C from PIL import Image, ImageDraw, ImageFont -from os import path +from os import path, getcwd from collections import deque from struct import pack, unpack +import signal import socket import rascsi_interface_pb2 as proto +class GracefulInterruptHandler(object): + def __init__(self, signals=(signal.SIGINT, signal.SIGTERM)): + self.signals = signals + self.original_handlers = {} + + def __enter__(self): + self.interrupted = False + self.released = False + + for sig in self.signals: + self.original_handlers[sig] = signal.getsignal(sig) + signal.signal(sig, self.handler) + + return self + + def handler(self, signum, frame): + self.release() + self.interrupted = True + + def __exit__(self, type, value, tb): + self.release() + + def release(self): + if self.released: + return False + + for sig in self.signals: + signal.signal(sig, self.original_handlers[sig]) + + self.released = True + return True + + WIDTH = 128 HEIGHT = 32 # Change to 64 if needed BORDER = 5 @@ -186,11 +219,11 @@ def send_pb_command(payload): return send_over_socket(s, payload) except socket.error as error: counter += 1 - logging.warning("The RaSCSI service is not responding - attempt " + \ + print("The RaSCSI service is not responding - attempt " + \ str(counter) + "/" + str(tries)) error_msg = str(error) - logging.error(error_msg) + exit(error_msg) def send_over_socket(s, payload): @@ -216,7 +249,7 @@ def send_over_socket(s, payload): while bytes_recvd < response_length: chunk = s.recv(min(response_length - bytes_recvd, 2048)) if chunk == b'': - logging.error("Read an empty chunk from the socket. \ + exit("Read an empty chunk from the socket. \ Socket connection has dropped unexpectedly. \ RaSCSI may have has crashed.") chunks.append(chunk) @@ -224,7 +257,7 @@ def send_over_socket(s, payload): response_message = b''.join(chunks) return response_message else: - logging.error("The response from RaSCSI did not contain a protobuf header. \ + exit("The response from RaSCSI did not contain a protobuf header. \ RaSCSI may have crashed.") @@ -255,29 +288,52 @@ def formatted_output(): output.append(f"~~RaSCSI v{version}~~") return output +def start_splash(): + splash = Image.open(f"{cwd}/splash_start.bmp").convert("1") + draw.bitmap((0, 0), splash) + oled.image(splash) + oled.show() + sleep(6) + +def stop_splash(): + draw.rectangle((0,0,WIDTH,HEIGHT), outline=0, fill=0) + splash = Image.open(f"{cwd}/splash_stop.bmp").convert("1") + draw.bitmap((0, 0), splash) + oled.image(splash) + oled.show() + +cwd = getcwd() + +start_splash() + version = rascsi_version() -while True: +with GracefulInterruptHandler() as h: + while True: - snapshot = deque(formatted_output()) - output = snapshot + ref_snapshot = formatted_output() + snapshot = ref_snapshot + output = deque(snapshot) - while len(snapshot) == len(output): - # Draw a black filled box to clear the image. - draw.rectangle((0,0,WIDTH,HEIGHT), outline=0, fill=0) - y_pos = top - for line in output: - draw.text((x, y_pos), line, font=font, fill=255) - y_pos += 8 + while snapshot == ref_snapshot: + # Draw a black filled box to clear the image. + draw.rectangle((0,0,WIDTH,HEIGHT), outline=0, fill=0) + y_pos = top + for line in output: + draw.text((x, y_pos), line, font=font, fill=255) + y_pos += 8 - #if len(snapshot) > 4: - output.rotate(-1) + # Shift the index of the array by one to get a scrolling effect + if len(output) > 5: + output.rotate(-1) - # Display image. - oled.image(image) - oled.show() - sleep(1000/delay_time_ms) + # Display image. + oled.image(image) + oled.show() + sleep(1000/delay_time_ms) - snapshot = deque(formatted_output()) - if len(snapshot) < 5: - break + snapshot = formatted_output() + + if h.interrupted: + stop_splash() + exit("Shutting down the OLED display...") diff --git a/src/oled_monitor/splash_start.bmp b/src/oled_monitor/splash_start.bmp new file mode 100644 index 0000000000000000000000000000000000000000..bd7493748530b66373663776ba4e0ef37db9ff8a GIT binary patch literal 574 zcmZ?rwPRuc13Mt80mKbJtN_G}KnMdu+zbpL3{nbJ{vQNF(ZDJ+fF>P^CcVl5W&xCL zK;xrx>%q3di67MFJJ`uQK0I@d{d4|j1%(eEpYXZ!=IqRwGw+{Ef0mYW?&b-fGk?wl z`Ts%un)5$_d>$ZwhCGO0asDHae+DE!|6KZm4~6G}`p=vJ=?C(kE|eH1mFue?0wEXx<%PH*7^D=U{67N&1c8+}xd#V{B}+Dm2*GSR&cjm>I}1HZ~1i)ePqw8#4g)yCd_#`eEuJ@-b-gGwh6QKHJGN z8$LF+1L==6V30^)U|==?xd5ae$WI3HAAtC9_klP-0Czuxi%lLa0BIh+Xchnfgg?Hb literal 0 HcmV?d00001 diff --git a/src/oled_monitor/start.sh b/src/oled_monitor/start.sh index 89c88eee..a51553a5 100755 --- a/src/oled_monitor/start.sh +++ b/src/oled_monitor/start.sh @@ -15,6 +15,11 @@ if ! command -v python3 &> /dev/null ; then echo "Run 'sudo apt install python3' to fix." ERROR=1 fi +if ! python3 -m venv --help &> /dev/null ; then + echo "venv could not be found" + echo "Run 'sudo apt install python3-venv' to fix." + ERROR=1 +fi # Dep to build Pillow if ! dpkg -l python3-dev &> /dev/null; then echo "python3-dev could not be found" @@ -31,9 +36,9 @@ if ! dpkg -l libpng-dev &> /dev/null; then echo "Run 'sudo apt install libpng-dev' to fix." ERROR=1 fi -if ! python3 -m venv --help &> /dev/null ; then - echo "venv could not be found" - echo "Run 'sudo apt install python3-venv' to fix." +if ! dpkg -l libopenjp2-7-dev &> /dev/null; then + echo "libopenjp2-7-dev could not be found" + echo "Run 'sudo apt install libopenjp2-7-dev' to fix." ERROR=1 fi if [ $ERROR = 1 ] ; then