#!/usr/bin/python # -*- coding: utf-8 -*- # readSeriale v. 2.3 # - single instance timer # - invio multiplo x send eventi accodati #--------------------------------------------------------------- # levare locking # timer semplificata import serial 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 = 3 # numero campioni filtraggio segnale ballerino MAX_COUNTER_BLINK = 10 PROGRAM_NAME ="ReadSer IOB-w v.2.3" # 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 # 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]) B_blinking = array ( 'B',[0,0,0,0,0,0,0,0]) B_previous = array ( 'B',[0,0,0,0,0,0,0,0]) B_input = array ( 'B',[0,0,0,0,0,0,0,0]) B_output = array ( 'B',[0,0,0,0,0,0,0,0]) B_inverting = array ( 'B',[0,0,0,0,0,0,0,0]) B_filter = array ( 'B',[0,0,0,0,0,0,0,0]) B_filter_prev = array ( 'B',[0,0,0,0,0,0,0,0]) B_temp = array ( 'B',[0,0,0,0,0,0,0,0]) i_filter_counters = array ( 'i',[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 buffer seriale e pulizia caratteri non stampabili # ritorna '' se non c'è un messaggio buono o il messaggio pulito ( due bytes hex ) # il messaggio ha il formato xxi00 00xxx def readSeriale(): global to_serial global to_retry global errormsglen ret = '' current = '' i = 0 # ritorna '' se non ci sono abbastanza caratteri try: if ser.inWaiting() < MSGLEN : logPro.error("SERIALE: errore msglen < 9 char - Errore no. " + str(errormsglen)) errormsglen = errormsglen +1 RiavviaSeriale() time.sleep(.2) if errormsglen > 30: if ser.isOpen(): ser.close() sys.exit(1) except: if startstatus == 0: logPro.error ("Porta SERIALE non disponibile - ser.inWaiting error - exit... - Errore no. " + str(errormsglen)) errormsglen = errormsglen +1 RiavviaSeriale() time.sleep(.2) if errormsglen > 30: if ser.isOpen(): ser.close() sys.exit(1) # finchè c'è robba .. leggi e tieni i buoni to_serial = TIMEOUTSERIALE to_retry = MAXRETRY try: while ser.inWaiting() > 0 : try: c = ser.read(1) except: logPro.error("SERIALE: errore su try ser.read") if ser.isOpen(): ser.close() sys.exit(1) # filtra caratteri non stampabili if c > ' ' : current += c #sys.stdout.write(current + '<<<<\n') # ora il messaggio ha il formato xxxxxi00 00xxx : cerco la 'i' iniziale try: while i < len(current) and current[i] != 'i': i = i + 1 except: logPro.error("SERIALE: errore su ricerca i iniziale") if ser.isOpen(): ser.close() sys.exit(1) # se non ho trovato la 'i' restituisco '' if i == len(current)-1: return ret else: current = current[i+1:i+3] # richiesta dati ad IOB requestData() #sys.stdout.write ( current + '\n') except: if startstatus == 0: logPro.error ('Porta SERIALE non disponibile - ser.inWaiting e filtraggio error...exit') if ser.isOpen(): ser.close() sys.exit(1) return current #--------------------------------------------------------------- # richiesta dati ad IOB : scrittura su seriale def requestData (): try : ser.write ("$i" + '\r\n') ser.flush() except : if startstatus == 0: logPro.error ( "SERIAL: Errore di scrittura/flush") #--------------------------------------------------------------- #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: logPro.info("WAIT active send to complete") 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 seriale #--------------------------------------------------------------- def avviaSeriale(): global ser try: ser = serial.Serial( port = comm_port , baudrate = 9600 , parity = serial.PARITY_NONE , stopbits = serial.STOPBITS_ONE , bytesize = serial.EIGHTBITS ) startstatus = 0 except serial.serialutil.SerialException , e : try: if startstatus == 0: logPro.error ( "SERIAL:Errore apertura seriale - " + comm_port) except: pass sys.stdout.write ( '\n' + PROGRAM_NAME + ' Error 1 opening serial\n\n%s\n\n' % e ) #if errormsglen > 30: if ser.isOpen(): ser.close() sys.exit (1) print( "\n\n" + PROGRAM_NAME + " - init ok \n\n") #--------------------------------------------------------------- # Ri avvia porta seriale #--------------------------------------------------------------- def RiavviaSeriale(): global ser try: if ser.isOpen(): ser.close() ser = serial.Serial( port = comm_port , baudrate = 9600 , parity = serial.PARITY_NONE , stopbits = serial.STOPBITS_ONE , bytesize = serial.EIGHTBITS ) startstatus = 0 except serial.serialutil.SerialException , e : try: if startstatus == 0: logPro.error ( "SERIAL:Errore apertura seriale - " + comm_port) except: pass sys.stdout.write ( '\n' + PROGRAM_NAME + ' Error 7 reopening serial\n\n%s\n\n' % e ) #if errormsglen > 30: if ser.isOpen(): ser.close() sys.exit (1) #--------------------------------------------------------------- #--------------------------------------------------------------- #--------------------------------------------------------------- # 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' ) comm_port = config.get ( 'comm' , 'port' ) 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' ) 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' ) # 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' ) MAX_COUNTER_FILTER = config.getint ( 'filter' , 'MAX_COUNTER_FILTER' ) except: print "\n\n" + PROGRAM_NAME + ' - Error 4 - in config file ' 'IOB.cfg' if ser.isOpen(): ser.close() 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 ( ' comm_port = %s' % ( comm_port ) ) 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 seriale avviaSeriale() #-------------------------------------------------------------- # MARCO: qui inserire avvio thread di "svuotaCoda" # avviaSvuotaCoda #print "Avvia svuota coda" do_every ( SENDURLTIME , svuota_coda ); #--------------------------------------------------------------- # ciclo forever and ever old = '' # richiesta dati ad IOB try: requestData() except: logPro.error("SERIALE: errore sul try di requestData") if ser.isOpen(): ser.close() sys.exit(1) #print "Avvio ciclo" logPro.info("Avvio loop principale") while 1: try: time.sleep (SAMPLETIME) except: logPro.error("First_SLEEP: errore attesa sampletime") # lettura dati da IOB try: value = readSeriale() except: if startstatus == 0: logPro.error("errore PRIMA LETTURA SERIALE") if ser.isOpen(): ser.close() sys.exit(1) 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