diff --git a/app_v3_simplified.py b/app.py similarity index 74% rename from app_v3_simplified.py rename to app.py index 6ba2cfd..f066e87 100644 --- a/app_v3_simplified.py +++ b/app.py @@ -195,6 +195,32 @@ def post_backup_data(): except Exception as e: logging.error(f"Error posting backup data: {e}") +def load_config(): + """Launch configuration UI when config card is inserted (card ID: 12886709)""" + try: + config_path = "./config.py" + + if os.path.exists(config_path): + logging.info("🔧 Launching configuration interface...") + print("🔧 Launching configuration interface...") + + # Launch config.py as a subprocess + subprocess.Popen([sys.executable, config_path], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) + + logging.info("✓ Configuration UI launched successfully") + print("✓ Configuration UI launched successfully") + return True + else: + logging.error("Config file not found at ./config.py") + print("✗ Config file not found at ./config.py") + return False + except Exception as e: + logging.error(f"Error launching configuration: {e}") + print(f"✗ Error launching configuration: {e}") + return False + # ============================================================================ # RFID READER - Background Thread # ============================================================================ @@ -256,10 +282,12 @@ class RFIDReader(rdm6300.BaseReader): """Detect card insertion - just set flag""" card_id = card.value - # Special card: device config card (ignore) + # Special card: device config card if card_id == 12886709: - logging.info(f"🔴 CONFIG CARD {card_id} detected (ignored)") - print(f"🔴 CONFIG CARD {card_id} detected (ignored)") + logging.info(f"🔴 CONFIG CARD {card_id} detected - Loading configuration") + print(f"🔴 CONFIG CARD {card_id} detected - Loading configuration") + # Call config loading function + load_config() return # IMMEDIATE LED feedback (BEFORE flag, for instant response) @@ -305,19 +333,23 @@ def process_card_events(hostname, device_ip): if card_id is not None: logging.info(f"[Main] Processing CARD INSERTED: {card_id}") - # Build API URL (1 = ON/inserted) - url = f"{HARTING_API_BASE}/{name}/{card_id}/1/{timestamp}" - - # Try to post - if post_to_harting(url): - logging.info(f"✓ Card event posted to API: {card_id}") + # Skip posting to Harting API if device is not configured + if name != "noconfig": + # Build API URL (1 = ON/inserted) + url = f"{HARTING_API_BASE}/{name}/{card_id}/1/{timestamp}" + + # Try to post + if post_to_harting(url): + logging.info(f"✓ Card event posted to API: {card_id}") + else: + logging.warning(f"✗ Offline: Saving card {card_id} to backup") + try: + with open(TAG_FILE, "a") as f: + f.write(url + "\n") + except Exception as e: + logging.error(f"Failed to save backup: {e}") else: - logging.warning(f"✗ Offline: Saving card {card_id} to backup") - try: - with open(TAG_FILE, "a") as f: - f.write(url + "\n") - except Exception as e: - logging.error(f"Failed to save backup: {e}") + logging.debug(f"Device not configured (noconfig). Skipping API post for card {card_id}") # ALWAYS send log to monitoring server (regardless of API post result) send_log_to_server(f"Card {card_id} inserted", hostname, device_ip, name) @@ -327,19 +359,23 @@ def process_card_events(hostname, device_ip): if card_id is not None: logging.info(f"[Main] Processing CARD REMOVED: {card_id}") - # Build API URL (0 = OFF/removed) - url = f"{HARTING_API_BASE}/{name}/{card_id}/0/{timestamp}" - - # Try to post - if post_to_harting(url): - logging.info(f"✓ Card event posted to API: {card_id}") + # Skip posting to Harting API if device is not configured + if name != "noconfig": + # Build API URL (0 = OFF/removed) + url = f"{HARTING_API_BASE}/{name}/{card_id}/0/{timestamp}" + + # Try to post + if post_to_harting(url): + logging.info(f"✓ Card event posted to API: {card_id}") + else: + logging.warning(f"✗ Offline: Saving card {card_id} to backup") + try: + with open(TAG_FILE, "a") as f: + f.write(url + "\n") + except Exception as e: + logging.error(f"Failed to save backup: {e}") else: - logging.warning(f"✗ Offline: Saving card {card_id} to backup") - try: - with open(TAG_FILE, "a") as f: - f.write(url + "\n") - except Exception as e: - logging.error(f"Failed to save backup: {e}") + logging.debug(f"Device not configured (noconfig). Skipping API post for card {card_id}") # ALWAYS send log to monitoring server (regardless of API post result) send_log_to_server(f"Card {card_id} removed", hostname, device_ip, name) @@ -495,6 +531,73 @@ def cleanup_old_logs(hostname, device_ip, name): except Exception as e: logging.error(f"Error cleaning up logs: {e}") +# ============================================================================ +# CHROMIUM LAUNCHER +# ============================================================================ + +def launch_chromium(name): + """ + Launch Chromium with either production URL or local fallback page + """ + try: + # Debug logging + print(f"DEBUG: launch_chromium called with name='{name}'") + print(f"DEBUG: name type={type(name)}, len={len(name)}") + print(f"DEBUG: name != 'noconfig' = {name != 'noconfig'}") + logging.debug(f"DEBUG: launch_chromium called with name='{name}' (type={type(name).__name__}, len={len(name)})") + + # Determine which URL to use based on device configuration + if name != "noconfig": + # Device is configured - use production URL + url = "http://10.76.140.17/iweb_v2/index.php/traceability/production" + print(f"Device configured as '{name}'. Launching production URL: {url}") + logging.info(f"Launching Chromium with production URL for device: {name}") + + # Launch Chromium with production URL + print("Starting Chromium with production URL...") + subprocess.Popen( + ["chromium", "--test-type", "--noerrors", "--kiosk", "--start-fullscreen", + "--unsafely-treat-insecure-origin-as-secure=http://10.76.140.17", url], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL, + start_new_session=True + ) + else: + # Device not configured - use local HTML file + print(f"Device NOT configured (name='noconfig'). Using local HTML.") + local_html_dir = os.path.join(DATA_DIR, "html") + local_html_file = os.path.join(local_html_dir, "Screen.html") + + # Create directory if it doesn't exist + os.makedirs(local_html_dir, exist_ok=True) + + # Check if local HTML file exists + if not os.path.exists(local_html_file): + print(f"Warning: Local HTML file not found at {local_html_file}") + print(f"Please create the file or copy it from the data/html directory") + logging.warning(f"Local HTML file not found: {local_html_file}") + + # Convert to file:// URL for local file + abs_path = os.path.abspath(local_html_file) + url = f"file://{abs_path}" + print(f"Device not configured. Launching local fallback page: {url}") + logging.info(f"Loading Screen.html from: {abs_path}") + + # Launch Chromium with local file - different flags for file:// URLs + print("Starting Chromium with local HTML file...") + subprocess.Popen( + ["chromium", "--test-type", "--noerrors", "--kiosk", "--start-fullscreen", + "--no-first-run", "--no-default-browser-check", url], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL, + start_new_session=True + ) + + print("✓ Chromium launched successfully") + logging.info("Chromium launched successfully") + + except Exception as e: + print(f"✗ Failed to launch Chromium: {e}") + logging.error(f"Failed to launch Chromium: {e}") + # ============================================================================ # MAIN APPLICATION # ============================================================================ @@ -550,7 +653,11 @@ def main(): wifi_thread.start() print("✓ WiFi monitor started") - # Application ready + # Launch Chromium browser + print("\nInitializing Chromium launcher...") + time.sleep(2) # Give system time to stabilize + launch_chromium(read_idmasa()) + print() logging.info("RFID Client operational") send_log_to_server("RFID Client operational", hostname, device_ip, read_idmasa()) print("✓ RFID Client operational - waiting for cards...") diff --git a/config.py b/config.py new file mode 100644 index 0000000..9f0c4a2 --- /dev/null +++ b/config.py @@ -0,0 +1,205 @@ +# config py este gandit pentru a verifica configurarile raspberiului +# verifica hostname-ul si il afiseaza +# verifica ce nume are masa si o afiseaza +# verifica daca masa este configurata cu sistem de prezenta operator +# verifica daca imprimanta este instalata corespunzator + +import os, time, socket, cups, subprocess +import FreeSimpleGUI as psg +import FreeSimpleGUI as sg +from multiprocessing import Process + + + +# import socket +host = socket.gethostname() # luam numele de host +def set_printer(): # importam codul python pentru printer + print("Cheking printer") + # acest segment acceseaza fisierul local idmas.txt si verifica ce informatii sunt acolo + # in mod normal acolo salvam numele mesei care trebuie sa corespunda cu numele imprimantei + f = open("./data/idmasa.txt","r") # deschid fisierul in care am salvat numele printerului + test = f.readlines() # citesc toate liniile + f.close() + # in cazul in care avem fisier corupt il resetam la original + try: + idmasa = test[0] + + except IndexError: + idmasa = "0" + print(idmasa) + # citim lista de printere disponibila + conn = cups.Connection () + printers = conn.getPrinters () + printer_stat=0 + for printer in printers: + print (printer, printers[printer]["device-uri"]) + if printer == idmasa: + printer_stat = 1 + + + if printer_stat == 1: + print("Printer is Ok") + else: + print("Printer is not ok") + p_Name = idmasa + print("Installing the new printer") + time.sleep(2) + cmd = "sudo /usr/sbin/lpadmin -p "+idmasa+" -E -v usb://CITIZEN/CT-S310II?serial=00000000 -m CTS310II.ppd" + os.popen(cmd) + time.sleep(2) + print("Printer Was Installed") + conn = cups.Connection() + printers = conn.getPrinters() + for printer in printers: + print(printer, printers[printer]["device-uri"]) + time.sleep(1) + print("Printerwas seted to "+idmasa+"") + print("Test printer App Closed") +# partea acesata se ocupa de verificarea existentei celor doua fisiere pentru a nu avea erori +# verificam daca exista fisierul care identifica masa +path = './data/idmasa.txt' +isFile = os.path.isfile(path) #verifica existenta fisierului prin bolean Tru/false + +if not isFile: +# print(path)# nu se face nimic pentru ca exista fisierul + + fp = open("./data/idmasa.txt", 'w') # cream fisier + fp.write('noconfig') # scriem in fisier prima line pentru a avea un punct de pornire + fp.close() # inchidem fisierul + +# verificam fisierul de prezenta pe baza acestuia stim daca locul de munca este configurat cu card de prezenta sau nu +path1 = './data/idmasa.txt' #verificare existenta al doilea fisier +isFile = os.path.isfile(path1)# verifica existenta fisierului + +#urmeaza sa citim fisierele pentru a crea cateva variabile +# prima variabila este idmasa +f = open("./data/idmasa.txt","r") # deschid fisierul +name = f.readlines() # citesc toate liniile +f.close() # inchid fisierul +try: + idmasa = name[0]# se verifica daca exista informatie in text + +except IndexError: + idmasa = "noconfig"# daca nu exista informatie in text setam variabila + + +n_config = 0 +#incepem sa definim primele functii din configurare +def notokfunction(): # este functie pentru butonul cancel din formular + global n_config + msg1 = "Id masa a fost actualizat la: "+ idmasa +"" ## pregatim mesajul pentru fereastra pop up + n_config = 1 + msg2 = "Slotul Pentru cartela este configurat by default" # pregatim mesajul pentru fereastra pop up + layout = [[sg.Text(msg1)], [sg.Text(msg2)], [sg.Button("Ok")]] + window = sg.Window("Configurari", layout) + while True: + event, values = window.read() + + if event == "Ok" or event == sg.WIN_CLOSED: + + break + + window.close() +#am inchis functia notok + +#functia pentru butonul ok din formular +def okfunction(): + global n_config + if idmasa == config1: # variabila config 1 este preluata din formular + msg1 = "Masa este setat corect: "+ idmasa +"" # se printeaza mesaj ca nu se actualizeaza id de masa + # print(msg1) + else: + f = open("./data/idmasa.txt","w") # deschidem fisierul config in mod scriere + L = config1 + f.write(L) # actualizam linia cu noua valuare din config + f.close() # inchidem fisierul + msg1 = "Id masa a fost actualizat la: "+ config1 +"" # pregatim mesajul pentru fereastra pop up + n_config = 0 +# + + # definim fereastra pentru ok asemena cu functia notok + layout = [[sg.Text(msg1)], [sg.Output(size=(40, 15))], [sg.Button("Ok")]] + + window = sg.Window("Configurari", layout) + while True: + event, values = window.read() + # End program if user closes window or + # presses the OK button + if event == "Ok" or event == sg.WIN_CLOSED: + + break + if nook == 1: + notokfunction() + n_config = 1 + time.sleep(2) + #asteptam 10 secunde si pornim functia de setare printer + set_printer() + #verificam daca hostul corespunde cu ce este in formular + time.sleep(2) + if host == host_conf: + print("Host name ok") + else: + print("Host name not ok") + time.sleep(2) + print("Update Hostname" ) + cmd = "sudo hostnamectl set-hostname "+host_conf+"" # comanda sa schimbam hostnameul + os.popen(cmd) + print("Os hostname updated") + time.sleep(2) + print("System will reboot in 5 seconds") + time.sleep(1) + print("System will reboot in 4 seconds") + time.sleep(1) + print("System will reboot in 3 seconds") + time.sleep(1) + print("System will reboot in 2 seconds") + time.sleep(1) + print("System will reboot in 1 seconds") + time.sleep(3) + + + + window.close() +# incepem initializarea feresteri ptrincipale gui + +sg.theme('dark grey 9') #alegem tema dark grey +psg.set_options(font=('Arial Bold', 16)) # setam fontul +#setarile de layout +layout = [ + [sg.Text('Host_Name', size=(20,1)),sg.InputText(default_text= ""+host+"" , enable_events=False)], + [sg.Text('Loc De Munca', size=(20,1)),sg.InputText(default_text=""+idmasa+"", enable_events=False)], + + [sg.Button('Ok'), sg.Button('Cancel')] +] +# setam window +window = psg.Window('Form', layout, size=(800,190),finalize=True) +# citim si configuram widgetul pentru butoanele 1 si 0 din randul + +while True: + + nook = 0 # cream o variabila care sa ne spuna daca a fost setao butonul ok sau nu + event, values = window.read() # citim valorile intr-o lista numita values + host_conf= values[0] # atribuim primul item din lista variabilei Host config + # aceasta variabila o vom folosi pentru a scrie sa nu noul hostname + print(host_conf) + if event == sg.WIN_CLOSED or event == 'Cancel': + nook = 1 # daca se da cancel setam variabila nook la 1 + n_config = 1 # setam variabila n_config la 1 + break + config1 = values[1] # atribuim lui config 1 valuarea din campul Loc de munca care a fost scris cu Id masa + + + # pornim functi care scrie valorile config in fisiere ok function + okfunction() +# si inchidem formularul + + # semnalam ca s-a terminat afisarea formularului + break + + +#inchidem formularul +window.close() +# daca variabila nook este 1 +time.sleep(2) +if n_config == 0: + os.system("sudo reboot now") diff --git a/data/device_info.txt b/data/device_info.txt index 41c707a..ccef6e2 100644 --- a/data/device_info.txt +++ b/data/device_info.txt @@ -1,2 +1,2 @@ -RPI-Device +RPI-testDevice 192.168.1.104 diff --git a/data/html/Screen.html b/data/html/Screen.html new file mode 100755 index 0000000..9e30af7 --- /dev/null +++ b/data/html/Screen.html @@ -0,0 +1,69 @@ + + +
+ + +
+ Please scan the configuration RFID card
+
+