""" RFID reader initialization and handling using RDM6300 library Extends rdm6300.BaseReader to handle card events (insert/remove) and integrate with batch logging system """ import logging import time from config_settings import SERIAL_DEVICES, CONFIG_CARD_ID, DEVICE_INFO_FILE from logger_module import log_with_server from logger_batch_module import queue_log_message from led_module import led_blink_pattern, led_on, led_off class RFIDReaderHandler: """Custom RFID Reader extending rdm6300.BaseReader for event handling""" def __init__(self, device_hostname, device_ip): """Initialize the reader handler""" self.device_hostname = device_hostname self.device_ip = device_ip self.reader = None def card_inserted(self, card): """Handle RFID card insertion event""" try: logging.info(f"🔴 CARD INSERTED EVENT TRIGGERED - Card ID: {card.value}") print(f"🔴 CARD INSERTED - ID: {card.value}") # Special handling for config card if card.value == CONFIG_CARD_ID: logging.info(f"Config card detected: {card.value}") queue_log_message("CONFIG_CARD_DETECTED", self.device_hostname, self.device_ip) return # Log card insertion timestamp = time.strftime("%Y-%m-%d %H:%M:%S") msg = f"Card inserted - ID: {card.value}" logging.info(msg) print(f"✓ Logging card insertion: {msg}") queue_log_message(msg, self.device_hostname, self.device_ip) # LED feedback: turn LED on when card is detected try: logging.info("🟢 Turning LED ON") led_on() print("✓ LED turned ON") except Exception as e: logging.debug(f"Could not control LED: {e}") except Exception as e: logging.error(f"Error handling card insertion: {e}") print(f"✗ Error in card_inserted: {e}") def card_removed(self, card): """Handle RFID card removal event""" try: logging.info(f"⚪ CARD REMOVED EVENT TRIGGERED - Card ID: {card.value}") print(f"⚪ CARD REMOVED - ID: {card.value}") # Special handling for config card if card.value == CONFIG_CARD_ID: logging.info(f"Config card removed: {card.value}") queue_log_message("CONFIG_CARD_REMOVED", self.device_hostname, self.device_ip) return # Log card removal timestamp = time.strftime("%Y-%m-%d %H:%M:%S") msg = f"Card removed - ID: {card.value}" logging.info(msg) print(f"✓ Logging card removal: {msg}") queue_log_message(msg, self.device_hostname, self.device_ip) # LED feedback: turn LED off when card is removed try: logging.info("⚫ Turning LED OFF") led_off() print("✓ LED turned OFF") except Exception as e: logging.debug(f"Could not control LED: {e}") except Exception as e: logging.error(f"Error handling card removal: {e}") print(f"✗ Error in card_removed: {e}") def initialize_rfid_reader(device_hostname=None, device_ip=None): """ Initialize RFID reader with RDM6300 library and multiple device attempts Args: device_hostname: Device hostname for logging device_ip: Device IP for logging Returns: Reader object or None if initialization fails """ try: from rdm6300 import BaseReader except ImportError: logging.error("✗ rdm6300 module not installed") print("✗ rdm6300 module not installed") log_with_server("ERROR: rdm6300 not installed", device_hostname, device_ip) return None logging.info("Initializing RFID reader with RDM6300...") print("Initializing RFID reader...") # Create handler for card events handler = RFIDReaderHandler(device_hostname or "unknown", device_ip or "0.0.0.0") for device in SERIAL_DEVICES: try: logging.info(f"Attempting to initialize RFID reader on {device}...") print(f"Attempting to initialize RFID reader on {device}...") # Create custom reader class that extends BaseReader class CustomReader(BaseReader): def card_inserted(self, card): logging.debug(f"[CustomReader] card_inserted called with card ID: {card.value}") handler.card_inserted(card) def card_removed(self, card): logging.debug(f"[CustomReader] card_removed called with card ID: {card.value}") handler.card_removed(card) # Initialize reader logging.debug(f"Creating reader object for {device}...") reader = CustomReader(device) logging.debug(f"Reader object created, attempting to start listening on {device}...") print(f"Reader created, starting to listen on {device}...") # Start reader in non-blocking way with timeout detection import threading reader_started = threading.Event() reader_error = [None] def start_reader(): try: logging.info(f"[Reader Thread] Starting reader on {device}") # This will block, listening for RFID cards reader.start() reader_started.set() except Exception as e: reader_error[0] = e logging.error(f"[Reader Thread] Error: {e}") reader_started.set() # Start reader in a NON-DAEMON thread so it keeps running # The reader runs indefinitely listening for cards reader_thread = threading.Thread(target=start_reader, daemon=False, name="RFIDReaderThread") reader_thread.start() logging.info(f"RFID reader thread started (thread name: {reader_thread.name})") # Wait up to 2 seconds to see if reader starts without error if not reader_started.wait(timeout=2): # Still trying, this is normal - reader is listening logging.info(f"Reader listening on {device} (waiting for cards...)") print(f"Reader listening on {device}") elif reader_error[0]: # Error occurred raise reader_error[0] # If we get here, reader is listening successfully logging.info(f"✓ RFID reader successfully initialized on {device}") print(f"✓ RFID reader successfully initialized on {device}") log_with_server(f"RFID reader started on {device}", device_hostname, device_ip) # LED feedback: 3 x 0.5 second blinks to indicate successful initialization try: logging.info("LED feedback: 3 blinks for successful RFID initialization") led_blink_pattern(3, 0.5) except Exception as e: logging.debug(f"Could not execute LED blink: {e}") return reader except FileNotFoundError as e: logging.warning(f"✗ Device {device} not found: {e}") print(f"✗ Device {device} not found") continue except PermissionError as e: logging.warning(f"✗ Permission denied for {device}: {e}") print(f"✗ Permission denied for {device}") print(f" Hint: Try adding user to dialout group: sudo usermod -a -G dialout $USER") continue except OSError as e: logging.warning(f"✗ OS Error on {device}: {e}") print(f"✗ OS Error on {device}: {e}") if "could not open port" in str(e).lower(): print(f" Hint: Serial port already in use or not accessible") elif "permission denied" in str(e).lower(): print(f" Hint: Permission denied - check user groups") continue except Exception as e: logging.warning(f"✗ Failed to initialize on {device}: {type(e).__name__}: {e}") print(f"✗ Failed to initialize on {device}: {type(e).__name__}: {e}") continue # If we get here, all devices failed logging.error("✗ Could not initialize RFID reader on any device") print("✗ Could not initialize RFID reader on any device") print("Available solutions:") print(" 1. Check hardware connections") print(" 2. Enable UART: sudo raspi-config -> Interface Options -> Serial") print(" 3. Add user to dialout group: sudo usermod -a -G dialout $USER") print(" 4. Reboot the system after making changes") log_with_server("ERROR: RFID reader initialization failed on all devices", device_hostname, device_ip) return None