From 31ac0da5b103fcd64b30ee25ee22eedda457d4f7 Mon Sep 17 00:00:00 2001 From: Vincent BENOIT Date: Tue, 8 Nov 2022 08:46:33 +0100 Subject: [PATCH] mise a jour des sources et ajout du serveur socket AF_UNIX --- src/intercom.py | 372 ++++++++++++++++++++++++++++-------------------- 1 file changed, 218 insertions(+), 154 deletions(-) diff --git a/src/intercom.py b/src/intercom.py index 44c82be..4539088 100644 --- a/src/intercom.py +++ b/src/intercom.py @@ -36,7 +36,7 @@ def get_conf(logger=None): config = None try: - with open(os.path.join("/home/pi/KineIntercom/src", "KineIntercom.json"), 'r') as f: + with open(os.path.join("/etc/kineintercom", "db.json"), 'r') as f: try: config = json.load(f) except json.decoder.JSONDecodeError as e: @@ -96,12 +96,12 @@ def send_at_cmd(cmd='', timeout=0.0, serObj=None, logger=None): out += c logger.debug("Reponse: {}".format(outlst)) if 'OK' in outlst: - return 0 + return 0, outlst elif 'ERROR' in outlst: logger.error("Error with cmd : {}".format(cmd)) - return 2 + return 2, None else: - return 1 + return 1, None except Exception as e: logger.error("Error: {}".format(e)) return 2 @@ -150,7 +150,34 @@ def init_gsm_com(serObj=None, config=None, logger=None): 'AT+CLIP=1', 'AT+VTD='+str(config['DTMF_DURATION'])] for cmd in cmd_lst: - ret = send_at_cmd(cmd=cmd, timeout=0.0, serObj=serObj, logger=logger) + ret, _ = send_at_cmd(cmd=cmd, timeout=0.0, serObj=serObj, logger=logger) + if ret == 2: + logger.error("Erreur avec la commande AT: {}".format(cmd)) + return False + elif ret == 1: + lgger.warning("Timeout avec la commande AT: {}".format(cmd)) + return True + + +def info_gsm_com(serObj=None, config=None, logger=None): + ''' Retreive GSM Module info + ''' + if not isinstance(serObj, serial.serialposix.Serial): + logger.error("error parameter, expecting serial.serialposix.Serial, get {}".format(type(serObj))) + return False + if not isinstance(logger, log.Logger): + logger.error("error parameter, expecting logging.Logger, get {}".format(type(logger))) + return False + cmd_lst = ['AT+CGMI', + 'AT+CGMM', + 'AT+CGMR', + 'AT+COPS?', + 'AT+CSMINS?', + 'AT+CSPN?', + 'AT+CGSN', + 'AT+CCALR?'] + for cmd in cmd_lst: + ret, rsp = send_at_cmd(cmd=cmd, timeout=0.0, serObj=serObj, logger=logger) if ret == 2: logger.error("Erreur avec la commande AT: {}".format(cmd)) return False @@ -188,7 +215,7 @@ def verify_caller(buf="", num="", logger=None): outlst = buf[7:].split(',') logger.debug("=> {}".format(outlst)) phone_number = outlst[0].replace("\"","") - if phone_number != num: + if not phone_number.endswith(num): logger.warning("phone number not match ! {}/{}".format(phone_number, num)) return False, phone_number return True, phone_number @@ -196,21 +223,37 @@ def verify_caller(buf="", num="", logger=None): def listener(sock, logger): ''' Thread socket listener ''' - logger.debug("Démarrage du serveur de communication avec le service horaire") + + global FLAG_CONF_UPDATE + global FLAG_HORAIRE_UPDATE + flag = True + logger.debug("Démarrage du serveur de communication avec le backend du configurateur") while True: # Wait for a connection try: clientsocket, address = sock.accept() + flag = True + logger.debug("Client connecté {}".format(clientsocket)) except socket.timeout: continue # socket recv() will block for a maximum of 1 sec. #clientsocket.settimeout(1) + data = b'' while clientsocket: - while True: + while flag: try: - data = clientsocket.recv(1) - except Error as e: + data += clientsocket.recv(1) + if data.decode('utf-8').endswith('\n'): + if data.decode('utf-8').startswith('RELOAD_DB'): + logger.debug("==> RELOAD DB") + FLAG_CONF_UPDATE = True + elif data.decode('utf-8').startswith('RELOAD_HOURS'): + FLAG_CONF_UPDATE = True + FLAG_HORAIRE_UPDATE = True + clientsocket = None + flag = False + except: continue if not data: break @@ -234,66 +277,79 @@ def verify_open_hours(conf=None, logger=None): my_date = date.today() day = calendar.day_name[my_date.weekday()] now = datetime.now() - for k,v in conf[day].items(): - time_conf = int(k.split('h')[0])*60 + int(k.split('h')[1]) + for item in conf[day]: + time_conf = int(item['name'].split('h')[0])*60 + int(item['name'].split('h')[1]) current_time = now.hour*60 + now.minute if current_time >= time_conf: - if v == 1: + if item['state'] == 1: flag = True - elif v == 0: + elif item['state'] == 0: flag = False + logger.debug('Jour: {} - Temps courant: {} - Ouverture: {}'.format(day, now.strftime('%Hh%M'), flag)) return flag def init_module(): ''' initialisation of GNSS/GPS/GSM HAT Module ''' - GPIO.setwarnings(False) - GPIO.setmode(GPIO.BOARD) - GPIO.setup(7, GPIO.OUT) - return + try: + GPIO.setmode(GPIO.BOARD) + GPIO.setwarnings(False) + GPIO.setup(7, GPIO.OUT) + except ValueError as e: + return False + return True def setup_module(): ''' Setup module (Set/Reset) ''' while True: - GPIO.output(7, GPIO.LOW) - time.sleep(2) - GPIO.output(7, GPIO.HIGH) + try: + GPIO.output(7, GPIO.LOW) + time.sleep(2) + GPIO.output(7, GPIO.HIGH) + except ValueError as e: + return False break - GPIO.cleanup() + #GPIO.cleanup() + return True -def cron_verify_hours(config=None, logger=None): - ''' cron task +def cron_verify_hours(op=None, config=None, logger=None): + ''' cron job to check opened or closed hours ''' global GSM_MODULE_STATE global GSM_MODULE_INIT_STATE - # Verify hours with conf file - opened = verify_open_hours(conf=config, logger=logger) + if op == 'Horaires': + # Verify hours with conf file + opened = verify_open_hours(conf=config, logger=logger) + elif op == 'Manuel ON': + opened = True + elif op == 'Manuel OFF': + opened = False + else: + logger.error("Wrong 'OPERATION' parameter in database") + if opened: # Si le module GSM doit être allumé et qu'il est éteint, on l'allume if not GSM_MODULE_STATE: logger.info("Allumage du module GSM HAT ...") - init_module() - setup_module() - GSM_MODULE_STATE = True - GSM_MODULE_INIT_STATE = False + if not setup_module(): + logger.error('Erreur d\'allumage du module GSM') + else: + GSM_MODULE_STATE = True + GSM_MODULE_INIT_STATE = False else: # Si le module GSM doit être éteint et qu'il est allumé, on l'eteint if GSM_MODULE_STATE: logger.debug("Fermeture du module GSM HAT ...") - init_module() - setup_module() - GSM_MODULE_STATE = False - GSM_MODULE_INIT_STATE = False - -def sigint_handler(signal, frame): - ''' SIGINT handler function - ''' - sys.exit(0) + if not setup_module(): + logger.error('Erreur de fermeture du module GSM') + else: + GSM_MODULE_STATE = False + GSM_MODULE_INIT_STATE = False def process(buf="", config=None, ser=None, logger=None): - ''' + ''' run process ''' if not isinstance(logger, log.Logger): return False @@ -312,20 +368,20 @@ def process(buf="", config=None, ser=None, logger=None): ret, phone_number = verify_caller(buf=buf, num=config['NUM_AUTORISE'], logger=logger) if not ret: # Disconnect connexion - ret = send_at_cmd(cmd="ATH", timeout=0.0, serObj=ser, logger=logger) + ret, _ = send_at_cmd(cmd="ATH", timeout=0.0, serObj=ser, logger=logger) logger.warning("Phone number not authorized : {}".format(phone_number)) else: logger.debug("Phone number authorized ...") time.sleep(0.3) # connect - ret = send_at_cmd(cmd='ATA', timeout=0.0, serObj=ser, logger=logger) + ret, _ = send_at_cmd(cmd='ATA', timeout=0.0, serObj=ser, logger=logger) if ret == 0: time.sleep(0.2) # send DMTF tone send_at_cmd(cmd='AT+VTS='+config['DTMF_CODE'], timeout=0.0, serObj=ser, logger=logger) - time.sleep(0.5) + time.sleep(config['TONE_DURATION']) # Disconnect - ret = send_at_cmd(cmd='ATH', timeout=0.0, serObj=ser, logger=logger) + ret, _ = send_at_cmd(cmd='ATH', timeout=0.0, serObj=ser, logger=logger) if ret > 0: logger.error('Cannot disconnect ...') else: @@ -337,6 +393,8 @@ def process(buf="", config=None, ser=None, logger=None): GSM_MODULE_STATE = False GSM_MODULE_INIT_STATE = False +FLAG_CONF_UPDATE = False +FLAG_HORAIRE_UPDATE = False def main(): ''' main function @@ -352,19 +410,20 @@ def main(): global GSM_MODULE_STATE global GSM_MODULE_INIT_STATE + global FLAG_CONF_UPDATE + global FLAG_HORAIRE_UPDATE - # Caught Keyboard interrupt - signal(SIGINT, sigint_handler) - # Configuration loader config = get_conf(logger) if not config: logger.error("Impossible de charger la configuration") - exit(1) + sys.exit(1) # init GSM HAT module logger.info("Initialisation du module GSM HAT ...") - init_module() + if not init_module(): + logger.error("Impossible d'initialiser les GPIO de la board") + sys.exit(1) # Serial configuration ser = serial.Serial('/dev/ttyAMA0', @@ -383,7 +442,8 @@ def main(): try: ser.flushInput() #flush input buffer, discarding all its contents ser.flushOutput() #flush output buffer, aborting current output - ret = send_at_cmd(cmd='AT', timeout=0.5, serObj=ser, logger=logger) + # send AT command to test the connection + ret, _ = send_at_cmd(cmd='AT', timeout=0.5, serObj=ser, logger=logger) if ret == 2: logger.error("Erreur d'envoie de la commande AT") elif ret == 1: @@ -398,136 +458,140 @@ def main(): logger.error("Impossible d'ouvrir le port série") sys.exit(1) - # Verify if open or not - opened = verify_open_hours(conf=config['HORAIRES'], logger=logger) + # Verify if the GSM module must be opened or not + if config['OPERATION'] == 'Horaires': + opened = verify_open_hours(conf=config['HORAIRES'], logger=logger) + elif config['OPERATION'] == 'Manuel ON': + opened = True + elif config['OPERATION'] == 'Manuel OFF': + opened = False + else: + logger.error("Wrong 'OPERATION' parameter in database") + ser.close() + sys.exit(1) + if opened: # Si le module GSM doit être allumé et qu'il est éteint, on l'allume if not GSM_MODULE_STATE: logger.info("Allumage du module GSM HAT ...") - setup_module() - GSM_MODULE_STATE = True - # Attente de 10 secondes avant d'initier l'init GSM - time.sleep(10) - # Initialize GSM communication - ret = init_gsm_com(serObj=ser, config=config, logger=logger) - if not ret: - logger.error("Erreur d'initialisation de la com GSM avec le module") + if not setup_module(): + logger.error("Erreur de configuration des GPIOs de la board") ser.close() sys.exit(1) else: - GSM_MODULE_INIT_STATE = True + GSM_MODULE_STATE = True + # Attente de 10 secondes avant d'initier l'init GSM + time.sleep(10) + # Initialize GSM communication + logger.info("Initialisation des commandes AT nécessaires ...") + ret = init_gsm_com(serObj=ser, config=config, logger=logger) + if not ret: + logger.error("Erreur d'initialisation de la com GSM avec le module") + ser.close() + sys.exit(1) + else: + GSM_MODULE_INIT_STATE = True + # Retreive GSM infos + logger.info('Récupération des infos du module ...') + ret = info_gsm_com(serObj=ser, config=config, logger=logger) + if not ret: + logger.error("Erreur d'initialisation de la com GSM avec le module") + ser.close() + sys.exit(1) else: # Si le module GSM doit être éteint et qu'il est allumé, on l'eteint if GSM_MODULE_STATE: logger.debug("Fermeture du module GSM HAT ...") - setup_module() - GSM_MODULE_STATE = False - GSM_MODULE_INIT_STATE = False + if not setup_module(): + logger.error("Erreur de configuration des GPIOs de la board") + ser.close() + sys.exit(1) + else: + GSM_MODULE_STATE = False + GSM_MODULE_INIT_STATE = False -# server_addr = "/tmp/uds_socket" -# # Make sure the socket does not already exist -# try: -# os.unlink(server_addr) -# except OSError: -# if os.path.exists(server_addr): -# raise -# -# # Create UDS socket -# sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) -# # Bind the socket to the port -# sock.bind(server_addr) -# # Listen for incoming connections -# sock.listen(1) -# thread = Thread(target=listener, args=(sock, logger,)) -# thread.start() -# time.sleep(1) + server_addr = "/tmp/uds_socket" + # Make sure the socket does not already exist + try: + os.unlink(server_addr) + except OSError: + if os.path.exists(server_addr): + raise + + # Create UDS socket + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + # Bind the socket to the port + sock.bind(server_addr) + # Listen for incoming connections + sock.listen(1) + thread = Thread(target=listener, args=(sock, logger,)) + thread.start() + time.sleep(1) # Scheduler opened hours sched = BackgroundScheduler(daemon=True) sched.add_job(func=cron_verify_hours, - args=(config['HORAIRES'], logger), + args=(config['OPERATION'], config['HORAIRES'], logger), trigger='cron', minute='*', - id="task_verify_hours") + id="job_id") sched.start() out = '' - while True: - # Si le module GSM est ouvert - if GSM_MODULE_STATE: - if not GSM_MODULE_INIT_STATE: - time.sleep(5) - # Initialize GSM serial communication - ret = init_gsm_com(serObj=ser, config=config, logger=logger) - if not ret: - logger.error("Erreur d'initialisation de la com GSM avec le module") + try: + while True: + if FLAG_CONF_UPDATE: + logger.info("Configuration must be reloaded ...") + # reload conf in memory + config = get_conf(logger) + if FLAG_HORAIRE_UPDATE: + logger.info("Restart scheduler ...") + # Stop job scheduler to restart it + sched.remove_job('job_id') + sched.add_job(func=cron_verify_hours, + args=(config['OPERATION'], config['HORAIRES'], logger), + trigger='cron', + minute='*', + id="job_id") + FLAG_HORAIRE_UPDATE = False + FLAG_CONF_UPDATE = False + + # Si le module GSM est ouvert + if GSM_MODULE_STATE: + # Si le module GSM n'a pas été initialisé + if not GSM_MODULE_INIT_STATE: + # Attente de 10sec pour que le module accroche le signal après allumage + time.sleep(10) + # Initialize GSM serial communication + ret = init_gsm_com(serObj=ser, config=config, logger=logger) + if not ret: + logger.error("Erreur d'initialisation de la com GSM avec le module") + break + else: + GSM_MODULE_INIT_STATE = True + + # While the number of bytes in the input buffer > 0 + if ser.in_waiting > 0: + # remove \r and \n chars from out string + out += ser.read_until().decode('utf-8').replace('\r','').replace('\n','') + if len(out) > 0 : + logger.debug("out: {}".format(out)) + time.sleep(.1) + ret = process(buf=out, config=config, ser=ser, logger=logger) + if not ret: + break + out = '' else: - GSM_MODULE_INIT_STATE = True - - # While the number of bytes in the input buffer > 0 - while ser.in_waiting > 0: - # remove \r and \n chars from out string - out += ser.read_until().decode('utf-8').replace('\r','').replace('\n','') - if len(out) > 0 : - logger.debug("out: {}".format(out)) - time.sleep(.1) - ret = process(buf=out, config=config, ser=ser, logger=logger) - out = '' - else: - time.sleep(10) - -# if ser.isOpen(): -# logger.info("Le port de communication avec le module GSM est ouvert") -# try: -# ser.flushInput() #flush input buffer, discarding all its contents -# ser.flushOutput() #flush output buffer, aborting current output -# if GSM_MODULE_STATE: -# # Initialize GSM communication -# ret = init_gsm_com(serObj=ser, config=config, logger=logger) -# if not ret: -# logger.error("Erreur d'initialisation de la communication avec le module GSM") -# return -# out = '' -# while True: -# # While the number of bytes in the input buffer > 0 -# while ser.in_waiting > 0: -# # remove \r and \n chars from out string -# out += ser.read_until().decode('utf-8').replace('\r','').replace('\n','') -# if len(out) > 0 : -# logger.debug("out: {}".format(out)) -# time.sleep(.1) -# if out.startswith('+CLIP: '): -# # Verify date and time -# ret = verify_open_hours(conf=config['HORAIRES'], log=logger) -# # Verify caller phone number -# ret, phone_number = verify_caller(buf=out, num=config['NUM_AUTORISE'], log=logger) -# if not ret: -# # Disconnect connexion -# send_at_cmd(cmd="ATH", serObj=ser, logger=logger) -# logger.warning("Phone number not authorized : {}".format(phone_number)) -# else: -# logger.debug("Phone number authorized ...") -# time.sleep(0.3) -# # connect -# ret = send_at_cmd(cmd='ATA', serObj=ser, logger=logger) -# if ret: -# time.sleep(0.2) -# # send DMTF tone -# send_at_cmd(cmd='AT+VTS='+config['DTMF_CODE'], serObj=ser, logger=logger) -# time.sleep(0.5) -# # Disconnect -# ret = send_at_cmd(cmd='ATH', serObj=ser, logger=logger) -# if not ret: -# logger.error('Cannot disconnect ...') -# out = '' -# except Exception as e: -# logger.error("error communicating: {}".format(e)) -# else: -# logger.error("cannot open serial port") + time.sleep(1) + else: + time.sleep(1) + except KeyboardInterrupt: + logger.error("Keyboard Interrupt !!") logger.info("fermeture du port de communication avec le GNSS_HAT") ser.close() sched.shutdown(wait=False) + thread.join() if __name__ == '__main__': main()