diff --git a/.gitignore b/.gitignore index 3e78e42d..a9d4c876 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,6 @@ __pycache__ src/web/current src/web/rascsi_interface_pb2.py src/oled_monitor/current +src/oled_monitor/rascsi_interface_pb2.py src/raspberrypi/hfdisk/ *~ diff --git a/src/oled_monitor/rascsi_oled_monitor.py b/src/oled_monitor/rascsi_oled_monitor.py index 38048815..882baf09 100755 --- a/src/oled_monitor/rascsi_oled_monitor.py +++ b/src/oled_monitor/rascsi_oled_monitor.py @@ -4,7 +4,8 @@ # Updates to output rascsi status to an OLED display # Copyright (C) 2020 Tony Kuker # Author: Tony Kuker -# Developed for: https://www.amazon.com/MakerFocus-Display-SSD1306-3-3V-5V-Arduino/dp/B079BN2J8V +# Developed for: +# https://www.makerfocus.com/collections/oled/products/2pcs-i2c-oled-display-module-0-91-inch-i2c-ssd1306-oled-display-module-1 # # All other code: # Copyright (c) 2017 Adafruit Industries @@ -36,6 +37,7 @@ import busio import adafruit_ssd1306 from PIL import Image, ImageDraw, ImageFont import subprocess +import rascsi_interface_pb2 as proto WIDTH = 128 HEIGHT = 32 # Change to 64 if needed @@ -87,39 +89,148 @@ font = ImageFont.load_default() # Some other nice fonts to try: http://www.dafont.com/bitmap.php # font = ImageFont.truetype('Minecraftia.ttf', 8) +def device_list(): + """ + Sends a DEVICES_INFO command to the server. + Returns a list of dicts with info on all attached devices. + """ + command = proto.PbCommand() + command.operation = proto.PbOperation.DEVICES_INFO + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) + + device_list = [] + n = 0 + + while n < len(result.devices_info.devices): + did = result.devices_info.devices[n].id + dun = result.devices_info.devices[n].unit + dtype = proto.PbDeviceType.Name(result.devices_info.devices[n].type) + dstat = result.devices_info.devices[n].status + dprop = result.devices_info.devices[n].properties + + # Building the status string + dstat_msg = [] + if dprop.read_only == True: + dstat_msg.append("Read-Only") + if dstat.protected == True and dprop.protectable == True: + dstat_msg.append("Write-Protected") + if dstat.removed == True and dprop.removable == True: + dstat_msg.append("No Media") + if dstat.locked == True and dprop.lockable == True: + dstat_msg.append("Locked") + + from os import path + dpath = result.devices_info.devices[n].file.name + dfile = path.basename(dpath) + dparam = result.devices_info.devices[n].params + dven = result.devices_info.devices[n].vendor + dprod = result.devices_info.devices[n].product + drev = result.devices_info.devices[n].revision + dblock = result.devices_info.devices[n].block_size + dsize = int(result.devices_info.devices[n].block_count) * int(dblock) + + device_list.append( + { + "id": did, + "un": dun, + "device_type": dtype, + "status": ", ".join(dstat_msg), + "path": dpath, + "file": dfile, + "params": dparam, + "vendor": dven, + "product": dprod, + "revision": drev, + "block_size": dblock, + "size": dsize, + } + ) + n += 1 + + return device_list + +def send_pb_command(payload): + """ + Takes a str containing a serialized protobuf as argument. + Establishes a socket connection with RaSCSI. + """ + # Host and port number where rascsi is listening for socket connections + HOST = 'localhost' + PORT = 6868 + + counter = 0 + tries = 100 + error_msg = "" + + import socket + while counter < tries: + try: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.connect((HOST, PORT)) + return send_over_socket(s, payload) + except socket.error as error: + counter += 1 + logging.warning("The RaSCSI service is not responding - attempt " + \ + str(counter) + "/" + str(tries)) + error_msg = str(error) + + logging.error(error_msg) + + +def send_over_socket(s, payload): + """ + Takes a socket object and str payload with serialized protobuf. + Sends payload to RaSCSI over socket and captures the response. + Tries to extract and interpret the protobuf header to get response size. + Reads data from socket in 2048 bytes chunks until all data is received. + """ + from struct import pack, unpack + + # Prepending a little endian 32bit header with the message size + s.send(pack("= 4: + # Extracting the response header to get the length of the response message + response_length = unpack("