diff --git a/IOB-PI/readParallela.py.bak b/IOB-PI/readParallela.py.bak deleted file mode 100644 index 96801a5..0000000 --- a/IOB-PI/readParallela.py.bak +++ /dev/null @@ -1,396 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# readParallela v. 3.1.0 12 Ingressi -# - single instance timer -# - invio multiplo x send eventi accodati -# - gestione segnali BLINKING -# - gestione INVERSIONE segnali cv 10-VII-2018 -# - gestione FILTRAGGIO segnali brevi cv 23-VII-2018 -# - (2.3) gestione 12 bit cv 14-I-2020 -# - (2.4) fix ingressi e conf apertura parallela + gestione vari bit filtraggio x nuovi ingressi + update conf con 12 parametri bit SEL 15-I-2020 -# - (2.4.8) versione adatta a raspberry PI vecchia generazione (GPIO corto, 8bit) -# - (2.5) Fix (hope) ciclo "wait send to complete", gestione timeout (rety infinito se IO riparte in modo anomalo) -# - (2.5.1) Fix numero versione 18.05.2023 -# - (2.5.2) Fix gestione eccezioni con report dettagliato -# - (2.5.3) Fix gestione stringhe e print x python 3.11 in debian 12 / raspberry OS 2025 -# - (2.6.0) Aggiunto gestione Redis x code salvate ogni minuto e ricaricate all'avvio 2025.04.17 -# - (2.6.1) Cleanup generale vecchia queue post test vari -# - (2.6.2) Fix in global di to_retry in send_coda per evitare problemi -# - (3.0.0) Prima versione code-assisted: riformulazione e ottimizzazione globale programma -# - (3.1.0) Ottimizzazione gestione letture GPIO (sempre code-assisted) - - -import time -import sys -import os -import logging -import logging.handlers -import threading -import configparser -from datetime import datetime, timezone -from array import array - -import redis -import requests -import urllib3 - -# Disable urllib3 debug logging to keep application logs clean -urllib3.disable_warnings() -logging.getLogger("urllib3").setLevel(logging.WARNING) - -# Note: RPi.GPIO is imported inside the class or at runtime to prevent errors on non-Pi systems -try: - import RPi.GPIO as GPIO -except ImportError: - GPIO = None - -class ReadParallelaIOB: - def __init__(self, config_path='IOB.cfg'): - self.PROGRAM_NAME = "ReadPar IOB-pi v.3.1.0 (2026)" - self.MAXRETRY = 10 - self.MAX_COUNTER_BLINK = 10 - - # Configuration and State - self.config = configparser - - # Hardware Pins (12 inputs) - self.input_pins = [11, 12, 13, 15, 16, 18, 22, 7, 29, 31, 32, 36] - - # Internal State Arrays - self.i_counters = array('i', [0] * 12) - self.B_blinking = array('B', [0] * 12) - self.B_previous = array('B', [0] * 12) - self.B_input = array('B', [0] * 12) - self.B_output = array('B', [0] * 12) - self.B_inverting = array('B', [0] * 12) - self.B_filter = array('B', [0] * 12) - self.B_filter_prev = array('B', [0] * 12) - self.B_temp = array('B', [0] * 12) - self.i_filter_counters = array('i', [0] * 12) - - # Load configuration after arrays are initialized - self.load_config(config_path) - - # Control Variables - self.cont = 0 - self.onLine = '1' - self.sending = False - self.timer_busy = False - self.to_enable = False - self.to_short = self.TIMEOUTSHORT - self.to_long = self.TIMEOUTLONG - self.to_retry = self.MAXRETRY - - # Redis - self.CodaR = redis.Redis(host='localhost', port=6379, db=0, password='24068Seriate') - self.queue_name = 'IOB' - - # Logging - self.setup_logging() - - def load_config(self, path): - """ - Loads configuration parameters from the specified .cfg file. - Sets up timing, URL, logging, and bit-specific settings (blinking, inversion, filtering). - """ - config = configparser.RawConfigParser() - if not os.path.exists(path): - print(f"Error: Config file {path} not found.") - sys.exit(1) - config.read(path) - - self.idxMacchina = config.get('id', 'idxMacchina') - self.SAMPLETIME = config.getfloat('time', 'SAMPLETIME') - self.TIMEOUTSHORT = config.getfloat('time', 'TIMEOUTSHORT') - self.TIMEOUTLONG = config.getfloat('time', 'TIMEOUTLONG') - self.SENDURLTIME = config.getfloat('time', 'SENDURLTIME') - self.NMAXSEND = config.getint('time', 'NMAXSEND') - - self.URLBASE = config.get('web', 'URLBASE') - self.URLENABLED = config.get('web', 'URLENABLED') - self.URLALIVE = config.get('web', 'URLALIVE') - self.URLADV1 = config.get('web', 'URLADV1') - - self.LOGFILE = config.get('log', 'LOGFILE') - self.LOGLEVEL = config.get('log', 'LOGLEVEL') - - # Load bit settings using loops instead of manual lines - for i in range(12): - self.B_blinking[i] = config.getint('blink', f'bit{i}') - self.MAX_COUNTER_BLINK = config.getint('blink', 'MAX_COUNTER_BLINK') - - for i in range(12): - self.B_inverting[i] = config.getint('invert', f'bit{i}') - self.B_filter[i] = config.getint('filter', f'bit{i}') - self.MAX_COUNTER_FILTER = config.getint('filter', 'MAX_COUNTER_FILTER') - - def setup_logging(self): - """ - Configures the logging system. - """ - logging.basicConfig( - level=logging.DEBUG, - format='%(asctime)s %(name)-8s %(levelname)-8s %(message)s', - datefmt='%Y-%m-%d %H:%M:%S', - filename=self.LOGFILE, - filemode='a' - ) - self.logQue = logging.getLogger('queue') - self.logSnd = logging.getLogger('sendUrl') - self.logPro = logging.getLogger('program') - - def setup_gpio(self): - """ - Initializes the GPIO pins for input. - """ - if GPIO is None: - self.logPro.error("GPIO library not found. Are you on a Raspberry Pi?") - sys.exit(1) - try: - GPIO.setmode(GPIO.BOARD) - GPIO.setwarnings(False) - for pin in self.input_pins: - GPIO.setup(pin, GPIO.IN) - self.logPro.info("GPIO initialized successfully.") - except Exception as e: - self.logPro.error(f"GPIO Setup Error: {e}") - sys.exit(1) - - def rqEnqueue(self, item): - """ - Adds an item to the Redis queue. - """ - self.CodaR.rpush(self.queue_name, item) - - def rqDequeue(self): - """ - Removes and returns an item from the Redis queue. - """ - item = self.CodaR.lpop(self.queue_name) - return item.decode('utf-8') if item else None - - def rqLen(self): - """ - Returns the current length of the Redis queue. - """ - return self.CodaR.llen(self.queue_name) - - def readParallelaFiltrata(self): - """ - Performs the core logic: reads GPIO, applies inversion, filtering, - and blinking, then reconstructs the value as a hex string. - """ - try: - # 1. Efficient GPIO Read - # Using a local reference for speed in loops - gpio_input = GPIO.input - pins = self.input_pins - inverting = self.B_inverting - input_arr = self.B_input - - for i in range(12): - # Read and invert logic immediately if required - raw_val = 0 if gpio_input(pins[i]) else 1 - if inverting[i]: - raw_val = 1 - raw_val - input_arr[i] = raw_val - - # 2. Processing Loop (Filtering & Blinking) - # Pre-caching references to reduce attribute lookups in tight loops - filter_arr = self.B_filter - filter_prev = self.B_filter_prev - filter_counters = self.i_filter_counters - max_filter = self.MAX_COUNTER_FILTER - - blinking = self.B_blinking - previous = self.B_previous - output_arr = self.B_output - blink_counters = self.i_counters - - for i in range(12): - # Debounce / Filter logic - if filter_arr[i]: - curr_in = input_arr[i] - prev_in = filter_prev[i] - - if curr_in != prev_in: - # State change detected - if curr_in == 1: - filter_counters[i] = max_filter if filter_counters[i] == 0 else 0 - self.B_temp[i] = 0 if filter_counters[i] == max_filter else 1 - else: - filter_counters[i] = max_filter if filter_counters[i] == 0 else 0 - self.B_temp[i] = 1 if filter_counters[i] == max_filter else 0 - - filter_prev[i] = curr_in - input_arr[i] = self.B_temp[i] - else: - # No state change, maintain or decrement counter - if filter_counters[i] > 0: - filter_counters[i] -= 1 - self.B_temp[i] = 0 if curr_in == 1 else 1 - else: - self.B_temp[i] = 1 if curr_in == 1 else 0 - - # Update the actual input array with the filtered value - input_arr[i] = self.B_temp[i] - - # Blinking Logic - if blinking[i] == 0: - output_arr[i] = input_arr[i] - else: - if previous[i] != input_arr[i]: - previous[i] = input_arr[i] - if input_arr[i] == 1: - output_arr[i] = 1 - blink_counters[i] = self.MAX_COUNTER_BLINK - else: - if input_arr[i] == 0 and blink_counters[i] > 0: - blink_counters[i] -= 1 - if blink_counters[i] == 0: - output_arr[i] = 0 - - # 3. Optimized Bitwise Reconstruction - new_value = 0 - for i in range(12): - if output_arr[i]: - new_value |= (1 << i) - - return hex(new_value)[2:].upper() - - except Exception as e: - self.logPro.error(f"Error in readParallelaFiltrata: {e}") - return '' - - def accoda(self, value): - try: - dt_eve = datetime.now(timezone.utc).strftime('%Y%m%d%H%M%S%f')[:-3] - self.rqEnqueue(f"{dt_eve}#{value}#{self.cont}") - except Exception as e: - self.logPro.error(f"QUEUE ERROR: {e}") - - def svuotaCoda(self): - if self.timer_busy: - return - - self.timer_busy = True - try: - if self.rqLen() > 0: - # Check connectivity using requests - try: - res_alive = requests.get(self.URLALIVE, timeout=5) - if res_alive.text == 'OK': - res_enabled = requests.get(self.URLENABLED + self.idxMacchina, timeout=5) - if res_enabled.text == 'OK': - if self.onLine == '0': - self.logPro.info("IOB ONLINE!") - self.onLine = '1' - else: - self.onLine = '0' - else: - self.onLine = '0' - except Exception as e: - self.logPro.error(f"Server Connection Error: {e}") - self.onLine = '0' - - if self.onLine == '1' and not self.sending: - self.sending = True - for _ in range(self.NMAXSEND): - if self.rqLen() == 0: - break - - resp = self.rqDequeue() - if not resp: break - - parts = resp.split("#") - dt_eve, val, cnt = parts[0], parts[1], parts[2] - dt_curr = datetime.now(timezone.utc).strftime('%Y%m%d%H%M%S%f')[:-3] - - url = f"{self.URLBASE}{self.idxMacchina}{self.URLADV1}{val}&dtCurr={dt_curr}&dtEve={dt_eve}&cnt={cnt}" - - try: - r = requests.get(url, timeout=5) - self.logSnd.info(f"{val} [{cnt}] R:{r.text}") - except Exception as e: - self.logSnd.error(f"Send Error: {e}") - - self.sending = False - elif self.sending: - if self.to_retry > 0: - self.to_retry -= 1 - self.logPro.info("WAIT active send to complete") - else: - self.sending = False - self.to_retry = self.MAXRETRY - self.logPro.info("END WAIT, reset to_retry") - except Exception as e: - self.logPro.error(f"svuotaCoda Error: {e}") - finally: - self.timer_busy = False - - def run(self): - """ - Starts the main execution loop: - 1. Initializes GPIO. - 2. Spawns a background daemon thread to empty the Redis queue. - 3. Enters a loop to sample GPIO inputs, apply filters/blinking, and queue changes. - """ - self.setup_gpio() - - # Start background thread for queue emptying - def timer_worker(): - while True: - self.svuotaCoda() - time.sleep(self.SENDURLTIME) - - threading.Thread(target=timer_worker, daemon=True).start() - - old_value = '' - - self.logPro.info("-------------------------------") - self.logPro.info(self.PROGRAM_NAME) - self.logPro.info("-------------------------------") - self.logPro.info("Starting main loop") - - while True: - try: - time.sleep(self.SAMPLETIME) - value = self.readParallelaFiltrata() - - if value != '': - if value != old_value: - self.logQue.info(f"{value} [{self.cont}]") - self.accoda(value) - self.cont = (self.cont + 1) % 10000 - - self.to_enable = True - self.to_short = self.TIMEOUTSHORT - self.to_long = self.TIMEOUTLONG - old_value = value - - # Handle Timeouts - if self.to_enable: - self.to_short -= self.SAMPLETIME - if self.to_short <= 0: - self.logQue.info(f">{value} [{self.cont}]") - self.accoda(value) - self.to_short = self.TIMEOUTSHORT - self.to_enable = False - self.to_long = self.TIMEOUTLONG - - self.to_long -= self.SAMPLETIME - if self.to_long <= 0: - self.logQue.info(f">>{value} [{self.cont}]") - self.accoda(value) - self.to_long = self.TIMEOUTLONG - - except KeyboardInterrupt: - self.logPro.info("Keyboard interrupt received. Exiting...") - break - except Exception as e: - self.logPro.error(f"Main loop error: {e}") - -if __name__ == "__main__": - app = ReadParallelaIOB() - app.run()