sabato 7 ottobre 2017

Python: lock file.

Il programmino in Python scritto per dialogare con la macchina PLC usa diversi file aperti in modalità scrittura, ed ero convinto che questo fosse sufficiente per impedire l'esecuzione multipla dello stesso programma.

Invece, anche se un file è aperto un modalità 'w' (write), il file non risulta bloccato, pertanto il programma può essere in esecuzione in sessioni differenti nello stesso PC.

Questo ha provocato alcuni problemi poichè l'operatore, senza accorgersi, aveva lanciato 2 volte l'avvio del programma e ci sono state delle anomalie di comunicazione con il PLC.

Infatti si utilizza una sorta di comunicazione bidirezionale gestendo un byte di stato in modo da operare in modo ordinato (sono pronto a leggere, ho scritto la stringa, ho letto, ho eseguito, etc.); però se, lato PC, ci sono 2 programmi in esecuzione questo scombina inevitabilmente la sequenza di stati.

Ho quindi cercato il modo di aprire un file in modalità "exclusive", ma ho scoperto che non è così semplice. Ho trovato diverse soluzioni che utilizzavano varie librerie (filelock, Portalocker, msvcrt, etc.), ma tutti abbastanza complicati e spesso "platform dependent", per cui alla fine ho scelto di seguire questa strada:
  • all'avvio del programma scrivo un file semaforo dove salvo data-ora e un numero casuale.
  • ad ogni ciclo leggo il file semaforo e verifico che contenga le stesse impostazioni di avvio, altrimenti esco per errore.
Nota bene: in questo caso si ferma il programma che è partito per primo, mentre il secondo prosegue normalmente. Sarebbe stato preferibile il contrario ma, per il nostro scopo, va bene anche così.

questo il listato di un programma di esempio, con un loop di 50 secondi:

#----------------------------------------------------------------------+
# Programma       : SEMAFORO                                           |
# Python versione : 3.6.0                                              |
#----------------------------------------------------------------------+
# Autore     : Fabio Giacobbe - 06/10/2017                             |
# Descrizione: Impedire esecuzione contemporanea dello stesso programma|
#----------------------------------------------------------------------+
# Modifiche  :            by                                           |
# Note       :                                                         |
#----------------------------------------------------------------------+

#-----------------------------------------
#           Import moduli 
#-----------------------------------------
import datetime
import time, msvcrt
from random import randrange

#---- Definizione variabili globali - Costanti fisse ------
NUM_LOOP = 50        # Loop

file_sem = r"C:\Temp\Semaforo.txt"
semaforo_s = ""

#------------------------------------------------------------------------------
# check_semaforo: verifica che la stringa nel file semaforo corrisponda
#                 a quanto scritto inizialmente, altrimenti significa che è
#                 stato avviato nuovamente il programma.
#------------------------------------------------------------------------------
# input : nulla
# return: nulla
#------------------------------------------------------------------------------
def check_semaforo():    

    global fsem
    global semaforo_s

    continua = 1
    conta = 0

    # Apre file semaforo: prova 10 volte con pausa 1 secondo
    while continua == 1:
        try:
            fsem = open(file_sem,"r")
            continua = 0
        except:
            conta = conta + 1
            if conta < 10:
                time.sleep(1)
            else:
                print("Errore apertura file semaforo!!!")
                exit(1)
    
    while 1:
        in_line = fsem.readline()
        if len(in_line) == 0:
            break

        riga = in_line

    fsem.close()

    if semaforo_s != riga:
        print("Errore semaforo esecuzione programma!!!")
        exit(1)
        
#------------------------------------------------------------------------------
# apri_semaforo : Crea file semaforo
#------------------------------------------------------------------------------
# input : nulla
# return: nulla
#------------------------------------------------------------------------------
def apri_semaforo():

    global fsem
    global semaforo_s
    
    # Creo file semaforo, scrivo data e ora + numero casuale e chiudo file
    try:
        fsem = open(file_sem, 'w')

    except:
        print("ERRORE creazione file semaforo!!!")
        exit(1)

    irand = randrange(0, 999999)

    riga = str(datetime.datetime.now()) + str(irand)

    semaforo_s = riga

    fsem.write(riga)
    fsem.flush()
    fsem.close()
    
#------------------------------------------------------------------------------
# INIZIO PROGRAMMA
#------------------------------------------------------------------------------
if __name__=='__main__':

   apri_semaforo()

   somma = 0
   i = 0

   for i in range(1, NUM_LOOP):

       somma = somma + i
       print(("Numero = %3d"% (i)), "Somma = ", somma)

       time.sleep(1)

       check_semaforo()

nella immagine cosa accade se si avvia il programma in 2 differenti finestre:




Nessun commento:

Posta un commento