#!/usr/bin/python # -*- coding: utf-8 -*- # readParallela v. 2.5.1 # - 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 #--------------------------------------------------------------- # levare locking # timer semplificata # GPIO global import time import sys from datetime import datetime import urllib import ConfigParser import os, sys import logging import logging.handlers import threading import Queue from array import * #-------------------------------------------------------------- # COSTANTI MSGLEN = 9 TIMEOUTSERIALE = 10 MAXRETRY = 10 # numero campioni filtraggio segnale ballerino MAX_COUNTER_BLINK = 10 PROGRAM_NAME ="ReadPar IOB-pi v.2.5.1" # DA FILE CONF idxMacchina = "1001" SAMPLETIME = 0.1 TIMEOUTSHORT = (SAMPLETIME*20) TIMEOUTLONG = (SAMPLETIME*600) SENDURLTIME = 0.08 NMAXSEND = 5 # numero massimo di invii per singolo ciclo di svuotamento # VAR to_enable = False to_short = TIMEOUTSHORT to_long = TIMEOUTLONG to_serial = TIMEOUTSERIALE to_retry = MAXRETRY errormsglen = 0 # VAR out_0 = 24 out_1 = 26 in_0 = 11 in_1 = 12 in_2 = 13 in_3 = 15 in_4 = 16 in_5 = 18 in_6 = 22 in_7 = 7 in_8 = 29 in_9 = 31 in_10 = 32 in_11 = 36 # contatore: serve x match tra accoda ed invia x possibile controllo a posteriori... ogni volta che accodo incremento di 1, va da 0 a 9999 cont = '0' # variabile stato online/offline della macchina onLine = '1' # variabile stato seinding/waiting x la parte invio URL sending = '0' # variabile stato timer thread busy timer_busy = False # # array per ingressi filtrati i_counters = array ( 'i',[0,0,0,0,0,0,0,0,0,0,0,0]) B_blinking = array ( 'B',[0,0,0,0,0,0,0,0,0,0,0,0]) B_previous = array ( 'B',[0,0,0,0,0,0,0,0,0,0,0,0]) B_input = array ( 'B',[0,0,0,0,0,0,0,0,0,0,0,0]) B_output = array ( 'B',[0,0,0,0,0,0,0,0,0,0,0,0]) B_inverting = array ( 'B',[0,0,0,0,0,0,0,0,0,0,0,0]) B_filter = array ( 'B',[0,0,0,0,0,0,0,0,0,0,0,0]) B_filter_prev = array ( 'B',[0,0,0,0,0,0,0,0,0,0,0,0]) B_temp = array ( 'B',[0,0,0,0,0,0,0,0,0,0,0,0]) i_filter_counters = array ( 'i',[0,0,0,0,0,0,0,0,0,0,0,0]) #-------------------------------------------------------------- # Gestione coda (condivisa) x registrazione eventi ed invio URL #print "Creazione coda illimitata" Coda = Queue.Queue(0) #queueLock = threading.Lock() #--------------------------------------------------------------- # lettura parallela # ritorna il byte letto pulito ( due char hex ) def readParallelaFiltrata(): global in_0 global in_1 global in_2 global in_3 global in_4 global in_5 global in_6 global in_7 global in_8 global in_9 global in_10 global in_11 global GPIO current = '' try: if GPIO.input(in_0): B_input[0] = 0 else: B_input[0] = 1 if GPIO.input(in_1): B_input[1] = 0 else: B_input[1] = 1 if GPIO.input(in_2): B_input[2] = 0 else: B_input[2] = 1 if GPIO.input(in_3): B_input[3] = 0 else: B_input[3] = 1 if GPIO.input(in_4): B_input[4] = 0 else: B_input[4] = 1 if GPIO.input(in_5): B_input[5] = 0 else: B_input[5] = 1 if GPIO.input(in_6): B_input[6] = 0 else: B_input[6] = 1 if GPIO.input(in_7): B_input[7] = 0 else: B_input[7] = 1 if GPIO.input(in_8): B_input[8] = 0 else: B_input[8] = 1 if GPIO.input(in_9): B_input[9] = 0 else: B_input[9] = 1 if GPIO.input(in_10): B_input[10] = 0 else: B_input[10] = 1 if GPIO.input(in_11): B_input[11] = 0 else: B_input[11] = 1 #ciclo per ogni segnale for i in xrange(12) : # print (i) # v2.1 gestione inversione bit ingresso if ( B_inverting[i] == 1 ) : if ( B_input[i] == 0 ) : B_input[i] = 1 else : B_input[i] = 0 # v2.2 gestione filtro segnali brevi if ( B_filter[i] == 1 ) : # fronte 0 -> 1 if ( B_input[i] == 1 ) and ( B_filter_prev [i] == 0 ) : if ( i_filter_counters[i] == 0 ) : # vero fronte 0 -> 1 i_filter_counters[i] = MAX_COUNTER_FILTER B_temp[i] = 0 # tengo l' ingresso a 0 #logPro.info("START spike 0->1 on bit " + `i` ) else : # fine disturbo breve di uno stato 1 i_filter_counters[i] = 0 B_temp[i] = 1 # tengo l' ingresso a 1 logPro.info("END spike 0->1 on bit " + `i` ) # stabile 1 -> 1 if ( B_input[i] == 1 ) and ( B_filter_prev [i] == 1 ) : if ( i_filter_counters[i] == 0 ) : # segnale stabile a 1 B_temp[i] = 1 # tengo l' ingresso a 1 else : # poco dopo il fronte i_filter_counters[i] = i_filter_counters[i] - 1 B_temp[i] = 0 # tengo l' ingresso a 0 # fronte 1 -> 0 if ( B_input[i] == 0 ) and ( B_filter_prev [i] == 1 ) : if ( i_filter_counters[i] == 0 ) : # vero fronte 1 -> 0 i_filter_counters[i] = MAX_COUNTER_FILTER B_temp[i] = 1 # tengo l' ingresso a 1 #logPro.info("START spike 1->0 on bit " + `i` ) else : # fine disturbo breve di uno stato 0 i_filter_counters[i] = 0 B_temp[i] = 0 # tengo l' ingresso a 0 logPro.info("END spike 1->0 on bit " + `i` ) # stabile 0 -> 0 if ( B_input[i] == 0 ) and ( B_filter_prev [i] == 0 ) : if ( i_filter_counters[i] == 0 ) : # segnale stabile a 0 B_temp[i] = 0 # tengo l' ingresso a 0 else : # poco dopo il fronte i_filter_counters[i] = i_filter_counters[i] - 1 B_temp[i] = 1 # tengo l' ingresso a 1 B_filter_prev [i] = B_input[i] B_input[i] = B_temp[i] # fine gestione filtro segnali brevi # se non blinking, copia ingresso if ( B_blinking[i] == 0 ) : B_output[i] = B_input[i] else: # gestione segnale blinking # se fronte del segnale if ( B_previous[i] != B_input[i] ) : B_previous[i] = B_input[i] # se fronte di salita if ( B_input[i] == 1 ) : # subito uscita = 1 B_output[i] = 1 i_counters[i] = MAX_COUNTER_BLINK #else : # # loggo che ho rilevato un blink... # logPro.info("Blink down on bit " + `i`) else: # no , segnale eguale a prima # se input a 0 if ( B_input[i] == 0 ) : # E CONTEGGIO IN CORSO if ( i_counters[i] > 0 ) : i_counters[i] = i_counters[i] -1 if ( i_counters[i] == 0 ) : B_output[i] = 0 logPro.info("END Blink on bit " + `i` ) #Rimettiamo insieme i bit new_value = 0 if ( B_output[0] == 1 ) : new_value = new_value + 1 if ( B_output[1] == 1 ) : new_value = new_value + 2 if ( B_output[2] == 1 ) : new_value = new_value + 4 if ( B_output[3] == 1 ) : new_value = new_value + 8 if ( B_output[4] == 1 ) : new_value = new_value + 16 if ( B_output[5] == 1 ) : new_value = new_value + 32 if ( B_output[6] == 1 ) : new_value = new_value + 64 if ( B_output[7] == 1 ) : new_value = new_value + 128 if ( B_output[8] == 1 ) : new_value = new_value + 256 if ( B_output[9] == 1 ) : new_value = new_value + 512 if ( B_output[10] == 1 ) : new_value = new_value + 1024 if ( B_output[11] == 1 ) : new_value = new_value + 2048 current = hex( new_value ).replace ( "0x" , "" ).upper() except: pass return current #--------------------------------------------------------------- #Funzione di scrittura su coda con try-except def accoda(): try: dtEve = datetime.utcnow().strftime('%Y%m%d%H%M%S%f')[:-3] Coda.put(dtEve + '#' + value + '#' + cont) except Queue.Full: logPro.error( "Queue full" + `dtEve` + '#' + `value` + '#' + `cont` ) except: logPro.error( "NETWORK:Errore http-no com rete-timeout" + url ) #print "Url aforte" , url #-------------------------------------------------------------- # svuotaCoda x invio dati al server def svuota_coda(): global onLine global sending global timer_busy global NMAXSEND #print "start timer " if ( timer_busy == False ): timer_busy = True #print "start timer ok " try: if not Coda.empty(): #print "coda da svuotare!" response = urllib.urlopen(URLALIVE) answ = response.read() if answ == 'OK': #print "OK alive" response2 = urllib.urlopen(URLENABLED + idxMacchina) answ2 = response2.read() if answ2 == 'OK': # aggiorno stato ad online if onLine == '0': logPro.info("IOB ONLINE!") #print("IOB ONLINE") onLine = '1' # imposto comunque online else: if onLine == '1': logPro.error("IOB offline") #print("IOB offline") onLine = '0' else: if onLine == '1': logPro.error("Server offline") #print("Server offline") onLine = '0' # ora verifico SE si possa inviare (ovvero sia online server e NON ci siano altri send attivi...) if onLine == '1': if sending == '0': #segnalo che sono in sending! sending = '1' # SAM 2016.12.23: modifica x invio FINO A nMaxSend ELEMENTI ad ogni ciclo di svuotamento i = NMAXSEND while i >= 0: if not Coda.empty(): # formatto dataOra corrente dtCurr = datetime.utcnow().strftime('%Y%m%d%H%M%S%f')[:-3] #prendo primo elemento dalla coda resp = Coda.get() # recupero valori da elemento coda! dtEve = resp.split("#")[0] value = resp.split("#")[1] cnt = resp.split("#")[2] url = URLBASE + idxMacchina + URLADV1 + value url = url + '&dtCurr=' + dtCurr + '&dtEve=' + dtEve + '&cnt=' + cnt # CHIAMO URL response3 = urllib.urlopen ( url ) answ3 = response3.read() #print(url) # log valore inviato! logSnd.info( value + ' ['+ cnt +']' + ' R:' + answ3 ) #print "Valore smaltito dalla coda" # tolgo 1 al contatore i -= 1 # completato invio, riporto sending a zero! sending = '0' else: if to_retry > 0: to_retry -= 1 logPro.info("WAIT active send to complete") else: sending = '0' to_retry = MAXRETRY logPro.info("END WAIT, reset to_retry var") else: pass else: pass except: if onLine == '1': logPro.error("Server Non raggiungibile") #print "Non raggiungibile" onLine = '0' # in ogni caso timer_busy = False #print "end timer ok" #print "end timer " #--------------------------------------------------------------- # funzione timer thread #--------------------------------------------------------------- def do_every (interval, worker_func, iterations = 0): if iterations != 1: threading.Timer ( interval, do_every, [interval, worker_func, 0 if iterations == 0 else iterations-1] ).start (); worker_func (); #--------------------------------------------------------------- # gestione contatore #--------------------------------------------------------------- def contatore(): try: global cont ctr = int(cont) ctr +=1 ctr = ctr % 10000 # round robin 10000 eventi x track cont = str(ctr) except: print("errore incremento contatore") #--------------------------------------------------------------- # avvia porta parallela #--------------------------------------------------------------- def avviaParallela(): global in_0 global in_1 global in_2 global in_3 global in_4 global in_5 global in_6 global in_7 global in_8 global in_9 global in_10 global in_11 global GPIO try: GPIO.setmode(GPIO.BOARD) GPIO.setwarnings(False) #GPIO.setup(out_0, GPIO.OUT) # output 0 #GPIO.setup(out_1, GPIO.OUT) # output 1 GPIO.setup(in_0, GPIO.IN) # input 0 GPIO.setup(in_1, GPIO.IN) # input 1 GPIO.setup(in_2, GPIO.IN) # input 2 GPIO.setup(in_3, GPIO.IN) # input 3 GPIO.setup(in_4, GPIO.IN) # input 4 GPIO.setup(in_5, GPIO.IN) # input 5 GPIO.setup(in_6, GPIO.IN) # input 6 GPIO.setup(in_7, GPIO.IN) # input 7 GPIO.setup(in_8, GPIO.IN) # input 8 GPIO.setup(in_9, GPIO.IN) # input 9 GPIO.setup(in_10, GPIO.IN) # input 10 GPIO.setup(in_11, GPIO.IN) # input 11 except: print( "\n\n" + PROGRAM_NAME + " - Error 3 on RPi.GPIO ! \n\n") sys.exit(1) print( "\n\n" + PROGRAM_NAME + " - init ok \n\n") #--------------------------------------------------------------- #--------------------------------------------------------------- # MAIN try: config = ConfigParser.RawConfigParser() config.read ( 'IOB.cfg' ) SAMPLETIME = config.getfloat ( 'time' , 'SAMPLETIME' ) TIMEOUTSHORT = config.getfloat ( 'time' , 'TIMEOUTSHORT' ) TIMEOUTLONG = config.getfloat ( 'time' , 'TIMEOUTLONG' ) SENDURLTIME = config.getfloat ( 'time' , 'SENDURLTIME' ) NMAXSEND = config.getint ( 'time' , 'NMAXSEND' ) idxMacchina = config.get ( 'id' , 'idxMacchina' ) URLBASE = config.get ( 'web' , 'URLBASE' ) URLENABLED = config.get('web' , 'URLENABLED') URLALIVE = config.get ('web' , 'URLALIVE') URLADV1 = config.get ( 'web' , 'URLADV1' ) LOGFILE = config.get ( 'log' , 'LOGFILE' ) LOGLEVEL = config.get ( 'log' , 'LOGLEVEL' ) B_blinking[0] = config.getint ( 'blink' , 'bit0' ) B_blinking[1] = config.getint ( 'blink' , 'bit1' ) B_blinking[2] = config.getint ( 'blink' , 'bit2' ) B_blinking[3] = config.getint ( 'blink' , 'bit3' ) B_blinking[4] = config.getint ( 'blink' , 'bit4' ) B_blinking[5] = config.getint ( 'blink' , 'bit5' ) B_blinking[6] = config.getint ( 'blink' , 'bit6' ) B_blinking[7] = config.getint ( 'blink' , 'bit7' ) B_blinking[8] = config.getint ( 'blink' , 'bit8' ) B_blinking[9] = config.getint ( 'blink' , 'bit9' ) B_blinking[10] = config.getint ( 'blink' , 'bit10' ) B_blinking[11] = config.getint ( 'blink' , 'bit11' ) MAX_COUNTER_BLINK = config.getint ( 'blink' , 'MAX_COUNTER_BLINK' ) # cv 2.1 se bit = 1 allora inverto segnale in ingresso... B_inverting[0] = config.getint ( 'invert' , 'bit0' ) B_inverting[1] = config.getint ( 'invert' , 'bit1' ) B_inverting[2] = config.getint ( 'invert' , 'bit2' ) B_inverting[3] = config.getint ( 'invert' , 'bit3' ) B_inverting[4] = config.getint ( 'invert' , 'bit4' ) B_inverting[5] = config.getint ( 'invert' , 'bit5' ) B_inverting[6] = config.getint ( 'invert' , 'bit6' ) B_inverting[7] = config.getint ( 'invert' , 'bit7' ) B_inverting[8] = config.getint ( 'invert' , 'bit8' ) B_inverting[9] = config.getint ( 'invert' , 'bit9' ) B_inverting[10] = config.getint ( 'invert' , 'bit10' ) B_inverting[11] = config.getint ( 'invert' , 'bit11' ) # cv 2.2 se bit = 1 allora filtro segnali brevi ... B_filter[0] = config.getint ( 'filter' , 'bit0' ) B_filter[1] = config.getint ( 'filter' , 'bit1' ) B_filter[2] = config.getint ( 'filter' , 'bit2' ) B_filter[3] = config.getint ( 'filter' , 'bit3' ) B_filter[4] = config.getint ( 'filter' , 'bit4' ) B_filter[5] = config.getint ( 'filter' , 'bit5' ) B_filter[6] = config.getint ( 'filter' , 'bit6' ) B_filter[7] = config.getint ( 'filter' , 'bit7' ) B_filter[8] = config.getint ( 'filter' , 'bit8' ) B_filter[9] = config.getint ( 'filter' , 'bit9' ) B_filter[10] = config.getint ( 'filter' , 'bit10' ) B_filter[11] = config.getint ( 'filter' , 'bit11' ) MAX_COUNTER_FILTER = config.getint ( 'filter' , 'MAX_COUNTER_FILTER' ) except: print "\n\n" + PROGRAM_NAME + ' - Error 4 - in config file ' 'IOB.cfg' sys.exit(1) #-------------------------------------------- # oggetto Logger #-------------------------------------------- try: logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(name)-8s %(levelname)-8s %(message)s', datefmt='%Y-%m-%d %H:%M:%S', filename=LOGFILE, filemode='a' ) # aggiungo 2 logger specifici x queue e send... logQue = logging.getLogger('queue') logSnd = logging.getLogger('sendUrl') logPro = logging.getLogger('program') except: # manda mail o simili - FARE!!! print "LOG: Impossibile creare file log con nome " print (LOGFILE) #-------------------------------------------- print "\n\n" + PROGRAM_NAME + "\n\n" global startstatus startstatus = 1 if startstatus == 1: logPro.info("Avvio Programma" + PROGRAM_NAME) ## Verifica l'OS e di conseguenza carica il file relativo con metodo di lockfile appropriato + check singola istanza if os.name == 'posix': import unix else: import win logPro.info( "Start " + PROGRAM_NAME ) # lettura file configurazione # [id] # idxMacchina = 2001 # [time] # SAMPLETIME = 0.1 # TIMEOUTSHORT = 200 # TIMEOUTLONG = 6000 print ( ' idxMacchina = %s' % ( idxMacchina ) ) print ( ' SAMPLETIME = %4.2f' % ( SAMPLETIME ) ) print ( ' TIMEOUTSHORT = %4.2f' % ( TIMEOUTSHORT ) ) print ( ' TIMEOUTLONG = %4.2f' % ( TIMEOUTLONG ) ) print ( ' SENDURLTIME = %4.2f' % ( SENDURLTIME ) ) print ( ' URLBASE = %s' % ( URLBASE ) ) print ( ' URLADV1 = %s' % ( URLADV1 ) ) print ( ' LOGFILE = %s' % ( LOGFILE ) ) print ( ' LOGLEVEL = %s' % ( LOGLEVEL ) ) # -sys.stdout.write ( 'idxMacchina ?' + idxMacchina + '\n') to_short = TIMEOUTSHORT to_long = TIMEOUTLONG #-------------------------------------------------------------- # apertura parallela try: import RPi.GPIO as GPIO except RuntimeError: print( "\n\n" + PROGRAM_NAME + " - Error 1 - you need superuser privileges") except: print( "\n\n" + PROGRAM_NAME + " - Error 2 - you need superuser privileges. USE 'sudo' to run your script\n\n") sys.exit(1) avviaParallela() #-------------------------------------------------------------- # MARCO: qui inserire avvio thread di "svuotaCoda" # avviaSvuotaCoda #print "Avvia svuota coda" do_every ( SENDURLTIME , svuota_coda ); #--------------------------------------------------------------- # ciclo forever and ever old = '' #print "Avvio ciclo" logPro.info("Avvio loop principale") while 1: try: time.sleep (SAMPLETIME) except: logPro.info("First_SLEEP: errore attesa sampletime") # lettura dati da IOB value = readParallelaFiltrata() if ( value != '' ) : if value != old : #loggo e invio dati try: logQue.info( value + ' ['+ cont +']') errormsglen = 0 accoda() contatore() except: logPro.error("URLBROWSER: errore registrazione valore e accoda") pass #enable e reset timer to_enable = True to_short = TIMEOUTSHORT to_long = TIMEOUTLONG old = value # gestione timeout breve if ( to_enable ) : to_short = to_short - SAMPLETIME if to_short <= 0: #loggo e invio dati try: logQue.info( '>' + value + ' ['+ cont +']') errormsglen = 0 accoda() contatore() except: logPro.error("URLBROWSER: errore registrazione valore e accoda TO_short") pass to_short = TIMEOUTSHORT to_enable = False # dopo un colpo il timer breve viene disabilitato to_long = TIMEOUTLONG # gestione timeout lungo to_long = to_long - SAMPLETIME if to_long <= 0: #loggo e invio dati try: logQue.info( '>>' + value + ' ['+ cont +']') errormsglen = 0 accoda() contatore() except: logPro.error("URLBROWSER: errore registrazione valore e accoda TO_long") pass to_long = TIMEOUTLONG