From 0b94ac2a3bc60980bdd30435c84c3e478cc19998 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sun, 8 Jun 2025 22:59:52 +0200 Subject: [PATCH 1/2] try native ipc --- output.png | Bin 5582 -> 5599 bytes reHDDPrinter.py | 182 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 130 insertions(+), 52 deletions(-) diff --git a/output.png b/output.png index b210d77d8c4708d5906dca1a6f3a3516b8e12172..59eb1bcba7d7bcac418aa256507df2c881470032 100644 GIT binary patch literal 5599 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 literal 5582 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 diff --git a/reHDDPrinter.py b/reHDDPrinter.py index 8bab4bf..d4a610e 100644 --- a/reHDDPrinter.py +++ b/reHDDPrinter.py @@ -3,75 +3,153 @@ """ Author: Hendrik Schutter, localhorst@mosad.xyz Date of creation: 2022/11/23 - Date of last modification: 2022/11/23 + Date of last modification: 2025/06/08 """ -import sysv_ipc -import pycstruct +import ctypes import os import time from brother_ql.brother_ql_create import create_label from brother_ql.raster import BrotherQLRaster import layouter -str_buffer_size = 64 #keep this synchronous to reHDD -msg_queue_key = 0x1B11193C0 #keep this synchronous to reHDD +# Constants +STR_BUFFER_SIZE = 64 # Buffer size for each field in t_driveData +MSG_QUEUE_KEY = 0x1B11193C0 # Message queue key from C code + +# IPC Constants +IPC_CREAT = 0o1000 # Required for msgget file_name = "output.png" printer_path = "/dev/usb/lp0" -def get_struct_format(): - #keep this synchronous to struct in reHDD - driveData = pycstruct.StructDef() - driveData.add('utf-8', 'driveIndex', length=str_buffer_size) - driveData.add('utf-8', 'driveHours', length=str_buffer_size) - driveData.add('utf-8', 'driveCycles', length=str_buffer_size) - driveData.add('utf-8', 'driveErrors', length=str_buffer_size) - driveData.add('utf-8', 'driveShredTimestamp', length=str_buffer_size) - driveData.add('utf-8', 'driveShredDuration', length=str_buffer_size) - driveData.add('utf-8', 'driveCapacity', length=str_buffer_size) - driveData.add('utf-8', 'driveState', length=str_buffer_size) - driveData.add('utf-8', 'driveModelFamiliy', length=str_buffer_size) - driveData.add('utf-8', 'driveModelName', length=str_buffer_size) - driveData.add('utf-8', 'driveSerialnumber', length=str_buffer_size) - driveData.add('utf-8', 'driveReHddVersion', length=str_buffer_size) - return driveData + +# Define ctypes structure for t_driveData +class TDriveData(ctypes.Structure): + _fields_ = [ + ("caDriveIndex", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveHours", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveCycles", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveErrors", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveShredTimestamp", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveShredDuration", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveCapacity", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveState", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveModelFamily", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveModelName", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveSerialnumber", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveReHddVersion", ctypes.c_char * STR_BUFFER_SIZE), + ] + + +# Define ctypes structure for t_msgQueueData +class TMsgQueueData(ctypes.Structure): + _fields_ = [ + ("msg_queue_type", ctypes.c_long), # Message type + ("driveData", TDriveData), # Drive data + ] + + +# Define ctypes bindings for Linux IPC +libc = ctypes.CDLL("libc.so.6") +msgget = libc.msgget +msgrcv = libc.msgrcv + +msgget.argtypes = [ctypes.c_int, ctypes.c_int] +msgget.restype = ctypes.c_int + +msgrcv.argtypes = [ + ctypes.c_int, + ctypes.POINTER(TMsgQueueData), + ctypes.c_size_t, + ctypes.c_long, + ctypes.c_int, +] +msgrcv.restype = ctypes.c_ssize_t + + +def worker(queue_id): + """ + Worker process to read messages from the IPC queue and print them. + """ + try: + while True: + # Prepare to receive a message + msg = TMsgQueueData() + result = msgrcv( + queue_id, + ctypes.byref(msg), + ctypes.sizeof(TMsgQueueData) - ctypes.sizeof(ctypes.c_long), + 0, + 0, + ) + if result == -1: + print("Error reading from message queue.") + break + + # Extract drive data + driveData = msg.driveData + + # Convert drive data fields from bytes to strings + drive_info = { + "driveIndex": driveData.caDriveIndex.decode().strip("\x00"), + "driveHours": int(driveData.caDriveHours.decode().strip("\x00")), + "driveCycles": int(driveData.caDriveCycles.decode().strip("\x00")), + "driveErrors": int(driveData.caDriveErrors.decode().strip("\x00")), + "driveShredTimestamp": int( + driveData.caDriveShredTimestamp.decode().strip("\x00") + ), + "driveShredDuration": int( + driveData.caDriveShredDuration.decode().strip("\x00") + ), + "driveCapacity": int(driveData.caDriveCapacity.decode().strip("\x00")), + "driveState": driveData.caDriveState.decode().strip("\x00"), + "driveModelFamily": driveData.caDriveModelFamily.decode().strip("\x00"), + "driveModelName": driveData.caDriveModelName.decode().strip("\x00"), + "driveSerialnumber": driveData.caDriveSerialnumber.decode().strip( + "\x00" + ), + "driveReHddVersion": driveData.caDriveReHddVersion.decode().strip( + "\x00" + ), + } + + #rehdd_info = layouter.ReHddInfo("https://git.mosad.xyz/localhorst/reHDD", driveData["driveReHddVersion"]) + + # Print the drive information + print(f"Received Drive Data: {drive_info}") + + # while(not os.path.exists(printer_path)): + # print("Printer not found, waiting ...") + # time.sleep(30) #sleep 30 + + # layouter.generate_image(drive_info, rehdd_info, file_name) + # qlr = BrotherQLRaster("QL-570") + # create_label(qlr, file_name, '62') + + # with open(printer_path, 'wb') as file: + # file.write(qlr.data) + # os.remove(file_name) + + except Exception as e: + print(f"Worker encountered an error: {e}") + def main(): + """ + Main function to connect to the existing IPC queue and start the worker process. + """ try: - mq = sysv_ipc.MessageQueue(msg_queue_key, sysv_ipc.IPC_CREAT) + # Connect to the existing message queue + queue_id = msgget(MSG_QUEUE_KEY, 0) + if queue_id == -1: + raise RuntimeError("Failed to connect to the existing message queue.") - while True: - message, mtype = mq.receive() - driveData = get_struct_format().deserialize(message) + # Start the worker process + worker(queue_id) + except Exception as e: + print(f"Main process encountered an error: {e}") - rehdd_info = layouter.ReHddInfo("https://git.mosad.xyz/localhorst/reHDD", driveData['driveReHddVersion']) - drive = layouter.DriveData( - drive_index=int(driveData['driveIndex']),\ - drive_state=str(driveData['driveState']),\ - modelfamiliy=str(driveData['driveModelFamiliy']),\ - modelname=str(driveData['driveModelName']),\ - capacity=int(driveData['driveCapacity']),\ - serialnumber=str(driveData['driveSerialnumber']),\ - power_on_hours=int(driveData['driveHours']),\ - power_cycle=int(driveData['driveCycles']),\ - smart_error_count=int(driveData['driveErrors']),\ - shred_timestamp=int(driveData['driveShredTimestamp']),\ - shred_duration=int(driveData['driveShredDuration'])) - - while(not os.path.exists(printer_path)): - print("Printer not found, waiting ...") - time.sleep(30) #sleep 30 - - layouter.generate_image(drive, rehdd_info, file_name) - qlr = BrotherQLRaster("QL-570") - create_label(qlr, file_name, '62') - - with open(printer_path, 'wb') as file: - file.write(qlr.data) - os.remove(file_name) - except sysv_ipc.ExistentialError: - print("ERROR: message queue creation failed") if __name__ == "__main__": main() From 211bf80b910e5dcf1efd7934ba5f8e2586bbe6ae Mon Sep 17 00:00:00 2001 From: localhorst Date: Sun, 15 Jun 2025 18:03:51 +0200 Subject: [PATCH 2/2] use libc IPC --- README.md | 2 +- reHDDPrinter.py | 203 +++++++++++++++++++++++++++++++----------------- 2 files changed, 133 insertions(+), 72 deletions(-) diff --git a/README.md b/README.md index a31523c..7e6c02d 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ ## Install ## -`pip install qrcode sysv-ipc pycstruct brother-ql` +`pip install qrcode brother-ql` ``` cd /root/ diff --git a/reHDDPrinter.py b/reHDDPrinter.py index d4a610e..899c0e6 100644 --- a/reHDDPrinter.py +++ b/reHDDPrinter.py @@ -3,28 +3,36 @@ """ Author: Hendrik Schutter, localhorst@mosad.xyz Date of creation: 2022/11/23 - Date of last modification: 2025/06/08 + Date of last modification: 2025/06/15 """ import ctypes import os import time +import signal +import argparse +import warnings +import logging +from PIL import Image from brother_ql.brother_ql_create import create_label from brother_ql.raster import BrotherQLRaster import layouter -# Constants -STR_BUFFER_SIZE = 64 # Buffer size for each field in t_driveData -MSG_QUEUE_KEY = 0x1B11193C0 # Message queue key from C code +# Suppress deprecation and printer warnings +warnings.filterwarnings("ignore", category=DeprecationWarning) +logging.getLogger("brother_ql").setLevel(logging.ERROR) -# IPC Constants -IPC_CREAT = 0o1000 # Required for msgget +# Constants +STR_BUFFER_SIZE = 64 +MSG_QUEUE_KEY = 0x1B11193C0 +IPC_CREAT = 0o1000 file_name = "output.png" printer_path = "/dev/usb/lp0" +terminate = False + -# Define ctypes structure for t_driveData class TDriveData(ctypes.Structure): _fields_ = [ ("caDriveIndex", ctypes.c_char * STR_BUFFER_SIZE), @@ -42,15 +50,14 @@ class TDriveData(ctypes.Structure): ] -# Define ctypes structure for t_msgQueueData class TMsgQueueData(ctypes.Structure): _fields_ = [ - ("msg_queue_type", ctypes.c_long), # Message type - ("driveData", TDriveData), # Drive data + ("msg_queue_type", ctypes.c_long), + ("driveData", TDriveData), ] -# Define ctypes bindings for Linux IPC +# IPC bindings libc = ctypes.CDLL("libc.so.6") msgget = libc.msgget msgrcv = libc.msgrcv @@ -68,84 +75,138 @@ msgrcv.argtypes = [ msgrcv.restype = ctypes.c_ssize_t -def worker(queue_id): - """ - Worker process to read messages from the IPC queue and print them. - """ +def signal_handler(signum, frame): + global terminate + print(f"Signal {signum} received, terminating...") + terminate = True + + +signal.signal(signal.SIGINT, signal_handler) +signal.signal(signal.SIGTERM, signal_handler) + + +def wait_for_printer(): + while not os.path.exists(printer_path): + print("Printer not found, waiting ...") + time.sleep(30) + return True + + +def create_drive_objects(drive_info): + """Convert dictionary to layouter-compatible DriveData and ReHddInfo objects""" + drive = layouter.DriveData( + drive_index=int(drive_info["driveIndex"]), + drive_state=drive_info["driveState"], + modelfamily=drive_info["driveModelFamily"], + modelname=drive_info["driveModelName"], + capacity=int(drive_info["driveCapacity"]), + serialnumber=drive_info["driveSerialnumber"], + power_on_hours=int(drive_info["driveHours"]), + power_cycle=int(drive_info["driveCycles"]), + smart_error_count=int(drive_info["driveErrors"]), + shred_timestamp=int(drive_info["driveShredTimestamp"]), + shred_duration=int(drive_info["driveShredDuration"]), + ) + + rehdd_info = layouter.ReHddInfo( + link="https://git.mosad.xyz/localhorst/reHDD", + version=drive_info["driveReHddVersion"], + ) + + return drive, rehdd_info + + +def worker(queue_id, test_mode=False): try: - while True: - # Prepare to receive a message - msg = TMsgQueueData() - result = msgrcv( - queue_id, - ctypes.byref(msg), - ctypes.sizeof(TMsgQueueData) - ctypes.sizeof(ctypes.c_long), - 0, - 0, - ) - if result == -1: - print("Error reading from message queue.") - break + while not terminate: + if test_mode: + drive_info = { + "driveIndex": "42", + "driveHours": 44, + "driveCycles": 45, + "driveErrors": 43, + "driveShredTimestamp": int(time.time()), + "driveShredDuration": 0, + "driveCapacity": 42, + "driveState": "shredded", + "driveModelFamily": "modelFamily", + "driveModelName": "modelName", + "driveSerialnumber": "serial", + "driveReHddVersion": "V1.1.2", + } + else: + msg = TMsgQueueData() + result = msgrcv( + queue_id, + ctypes.byref(msg), + ctypes.sizeof(TMsgQueueData) - ctypes.sizeof(ctypes.c_long), + 0, + 0, + ) + if result == -1: + err = ctypes.get_errno() + print(f"Error reading from message queue: {os.strerror(err)}") + break - # Extract drive data - driveData = msg.driveData + d = msg.driveData + drive_info = { + "driveIndex": d.caDriveIndex.decode().strip("\x00"), + "driveHours": int(d.caDriveHours.decode().strip("\x00")), + "driveCycles": int(d.caDriveCycles.decode().strip("\x00")), + "driveErrors": int(d.caDriveErrors.decode().strip("\x00")), + "driveShredTimestamp": int( + d.caDriveShredTimestamp.decode().strip("\x00") + ), + "driveShredDuration": int( + d.caDriveShredDuration.decode().strip("\x00") + ), + "driveCapacity": int(d.caDriveCapacity.decode().strip("\x00")), + "driveState": d.caDriveState.decode().strip("\x00"), + "driveModelFamily": d.caDriveModelFamily.decode().strip("\x00"), + "driveModelName": d.caDriveModelName.decode().strip("\x00"), + "driveSerialnumber": d.caDriveSerialnumber.decode().strip("\x00"), + "driveReHddVersion": d.caDriveReHddVersion.decode().strip("\x00"), + } - # Convert drive data fields from bytes to strings - drive_info = { - "driveIndex": driveData.caDriveIndex.decode().strip("\x00"), - "driveHours": int(driveData.caDriveHours.decode().strip("\x00")), - "driveCycles": int(driveData.caDriveCycles.decode().strip("\x00")), - "driveErrors": int(driveData.caDriveErrors.decode().strip("\x00")), - "driveShredTimestamp": int( - driveData.caDriveShredTimestamp.decode().strip("\x00") - ), - "driveShredDuration": int( - driveData.caDriveShredDuration.decode().strip("\x00") - ), - "driveCapacity": int(driveData.caDriveCapacity.decode().strip("\x00")), - "driveState": driveData.caDriveState.decode().strip("\x00"), - "driveModelFamily": driveData.caDriveModelFamily.decode().strip("\x00"), - "driveModelName": driveData.caDriveModelName.decode().strip("\x00"), - "driveSerialnumber": driveData.caDriveSerialnumber.decode().strip( - "\x00" - ), - "driveReHddVersion": driveData.caDriveReHddVersion.decode().strip( - "\x00" - ), - } - - #rehdd_info = layouter.ReHddInfo("https://git.mosad.xyz/localhorst/reHDD", driveData["driveReHddVersion"]) - - # Print the drive information print(f"Received Drive Data: {drive_info}") - # while(not os.path.exists(printer_path)): - # print("Printer not found, waiting ...") - # time.sleep(30) #sleep 30 + drive_obj, rehdd_info = create_drive_objects(drive_info) + layouter.generate_image(drive_obj, rehdd_info, file_name) - # layouter.generate_image(drive_info, rehdd_info, file_name) - # qlr = BrotherQLRaster("QL-570") - # create_label(qlr, file_name, '62') + if wait_for_printer(): + qlr = BrotherQLRaster("QL-570") + image = Image.open(file_name) + create_label(qlr, image, "62") + with open(printer_path, "wb") as file: + file.write(qlr.data) + os.remove(file_name) + else: + print("Skipping printing due to printer unavailability.") - # with open(printer_path, 'wb') as file: - # file.write(qlr.data) - # os.remove(file_name) + if test_mode: + break except Exception as e: print(f"Worker encountered an error: {e}") def main(): - """ - Main function to connect to the existing IPC queue and start the worker process. - """ + parser = argparse.ArgumentParser() + parser.add_argument( + "--test", action="store_true", help="Run in test mode with fake data" + ) + args = parser.parse_args() + + if args.test: + print("Running in test mode.") + worker(None, test_mode=True) + return + try: - # Connect to the existing message queue queue_id = msgget(MSG_QUEUE_KEY, 0) if queue_id == -1: raise RuntimeError("Failed to connect to the existing message queue.") - # Start the worker process worker(queue_id) except Exception as e: print(f"Main process encountered an error: {e}")