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 00000000..bd749374 Binary files /dev/null and b/src/oled_monitor/splash_start.bmp differ diff --git a/src/oled_monitor/splash_stop.bmp b/src/oled_monitor/splash_stop.bmp new file mode 100644 index 00000000..df15ff05 Binary files /dev/null and b/src/oled_monitor/splash_stop.bmp differ 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