Files
2018-11-28 19:19:12 +01:00

636 lines
19 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
# readSeriale v. 2.3e
# - single instance timer
# - invio multiplo x send eventi accodati
# - attenzione .. non c'e' la possibilita di definire bit blinking, inversione ecc.
# - gestione wait se troppi errori in msglen...
#---------------------------------------------------------------
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 = 5
TIMEOUTSERIALE = 10
MAXRETRY = 3
# numero campioni filtraggio segnale ballerino
MAX_COUNTER_BLINK = 10
PROGRAM_NAME ="ReadSer IOB-w v.2.3e"
# DA FILE CONF
idxMacchina = "L003"
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
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()
#---------------------------------------------------------------
#---------------------------------------------------------------
def ChiudiSerialeEsci():
global ser
if ser.isOpen():
ser.close()
sys.exit(1)
#---------------------------------------------------------------
#---------------------------------------------------------------
# lettura buffer seriale e pulizia caratteri non stampabili
# ritorna '00' se non c'è un messaggio buono o il messaggio pulito ( due bytes hex )
# il messaggio ha il formato xx<ACK>i00 00<CR><LF>xxx CON IL COMANDO $i
# il messaggio ha il formato xx<ACK>I00<CR><LF>xxx CON IL COMANDO $I
def readSeriale():
global to_serial
global to_retry
global errormsglen
global ser
ret = ''
current = '80'
i = 0
n_trials = 0
errormsglen = 0
b_ok = False
try:
while ( not b_ok ):
if ser.inWaiting() < MSGLEN : # se non ci sono abbastanza caratteri
time.sleep (.2) # attesa breve
n_trials = n_trials + 1
if ( n_trials > 5 ) :
logPro.error("err. 15 - msglen < 5 char - Trial # " + str(errormsglen) + " | " + str(ser.inWaiting()))
RiavviaSeriale()
time.sleep(.5)
requestData()
n_trials = 0
b_ok = True # esco dal loop
#endif n_trials
else : # finchè c'è robba .. leggi e tieni i buoni
while ser.inWaiting() > 0 :
try:
c = ser.read(1)
except Exception, e:
logPro.error("err. 13 - error on try ser.read -- EXIT -- " + str(e) )
ChiudiSerialeEsci()
#end try
# filtra caratteri non stampabili
if c > ' ' :
current += c
# sys.stdout.write(current + '<<<<\n')
#end while
# ora il messaggio ha il formato xxxxxi00 00xxx : cerco la 'i' iniziale
# ora il messaggio ha il formato xxxxxI00xxx : cerco la 'I' iniziale
try:
while i < len(current) and current[i] != 'I':
i = i + 1
#end while
except:
logPro.error("err. 14 - error on find I >>" + current +"<<")
#end try
# se non ho trovato la 'i' restituisco '00'
if i == len(current)-1:
return ret
else:
current = current[i+1:i+3]
# levo i due bit piu alti .....
current = "{0:0>2X}".format(int ( current, 16 ) & 0X3F)
#end if
# richiesta dati ad IOB
requestData()
n_trials = 0
errormsglen = 0
b_ok = True # esco dal loop
# sys.stdout.write ( current + '\n')
#endif
#end while not b_ok
except Exception, e:
logPro.error (' err. 18 - Serial Port error... --EXIT -- ---> ' + str(e) )
ChiudiSerialeEsci()
return current
#---------------------------------------------------------------
# richiesta dati ad IOB : scrittura su seriale
def requestData ():
global ser
try :
#$I per vers 1, $i per vers 2 scheda, prima con 6 seconda con 9 char risposta
ser.write ("$I" + '\r\n')
ser.flush()
except :
logPro.error ( "SERIAL: err. 19 - Write / 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,
timeout = 5
)
except serial.serialutil.SerialException , e :
sys.stdout.write ( '\n' + PROGRAM_NAME + ' err 11 - opening serial\n\n%s\n\n' % e )
ChiudiSerialeEsci()
#end try
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,
timeout = 5
)
except serial.serialutil.SerialException , e :
sys.stdout.write ( '\n' + PROGRAM_NAME + ' err 12 - reopening serial\n\n%s\n\n' % e )
ChiudiSerialeEsci()
#end try
#---------------------------------------------------------------
#---------------------------------------------------------------
#---------------------------------------------------------------
# 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' + " -- EXIT --"
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"
## 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("err 20 - try requestData -- EXIT --")
ChiudiSerialeEsci()
#print "Avvio ciclo"
logPro.info("Avvio loop principale")
while True :
try:
time.sleep (SAMPLETIME)
except:
logPro.error("err 21 - First_SLEEP: errore attesa sampletime")
# lettura dati da IOB
try:
value = readSeriale()
except:
logPro.error("err 22 - main loop read serial -- EXIT --")
ChiudiSerialeEsci()
if ( value != '' ) :
if value != old :
#loggo e invio dati
try:
logQue.info( value + ' ['+ cont +']')
accoda()
contatore()
except:
logPro.error("err 23 - 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 +']')
accoda()
contatore()
except:
logPro.error("err 24 - 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 +']')
accoda()
contatore()
except:
logPro.error("err 25 - URLBROWSER: errore registrazione valore e accoda TO_long")
pass
to_long = TIMEOUTLONG