================================================================================ APPUNTI OPERAZIONI - ics-simlab-config-gen_claude ================================================================================ Data: 2026-01-27 ================================================================================ PROBLEMA INIZIALE ----------------- PLC2 crashava all'avvio con "ConnectionRefusedError" quando tentava di scrivere a PLC1 via Modbus TCP prima che PLC1 fosse pronto. Causa: callback cbs[key]() chiamata direttamente senza gestione errori. SOLUZIONE IMPLEMENTATA ---------------------- File modificato: tools/compile_ir.py (linee 24, 30-40, 49) Aggiunto: - import time - Funzione _safe_callback() con retry logic (30 tentativi × 0.2s = 6s) - Modifica _write() per chiamare _safe_callback(cbs[key]) invece di cbs[key]() Risultato: - PLC2 non crasha più - Retry automatico se PLC1 non è pronto - Warning solo dopo 30 tentativi falliti - Container continua a girare anche in caso di errore FILE CREATI ----------- build_scenario.py - Builder deterministico (config → IR → logic) validate_fix.py - Validatore presenza fix nei file generati CLEANUP_SUMMARY.txt - Summary pulizia progetto README.md (aggiornato) - Documentazione completa docs/ (7 file): - README_FIX.md - Doc principale fix - QUICKSTART.txt - Guida rapida - RUNTIME_FIX.md - Fix dettagliato + troubleshooting - CHANGES.md - Modifiche con diff - DELIVERABLES.md - Summary completo - FIX_SUMMARY.txt - Confronto codice before/after - CORRECT_COMMANDS.txt - Come usare path assoluti con sudo scripts/ (3 file): - run_simlab.sh - Launcher ICS-SimLab con path corretti - test_simlab.sh - Test interattivo - diagnose_runtime.sh - Diagnostica container PULIZIA PROGETTO ---------------- Spostato in docs/: - 7 file documentazione dalla root Spostato in scripts/: - 3 script bash dalla root Cancellato: - database/, docker/, inputs/ (cartelle vuote) - outputs/last_raw_response.txt (temporaneo) - outputs/logic/, logic_ir/, logic_water_tank/ (vecchie versioni) Mantenuto: - outputs/scenario_run/ (SCENARIO FINALE per ICS-SimLab) - outputs/configuration.json (config base) - outputs/ir/ (IR intermedio) STRUTTURA FINALE ---------------- Root: 4 file essenziali (main.py, build_scenario.py, validate_fix.py, README.md) docs/: documentazione (60K) scripts/: utility (20K) outputs/: solo file necessari (56K) + cartelle codice sorgente (tools/, services/, models/, templates/, helpers/) + riferimenti (examples/, spec/, prompts/) COMANDI UTILI ------------- # Build scenario completo python3 build_scenario.py --overwrite # Valida fix presente python3 validate_fix.py # Esegui ICS-SimLab (IMPORTANTE: path assoluti con sudo!) ./scripts/run_simlab.sh # O manualmente: cd /home/stefano/projects/ICS-SimLab-main/curtin-ics-simlab sudo ./start.sh /home/stefano/projects/ics-simlab-config-gen_claude/outputs/scenario_run # Monitor PLC2 logs sudo docker logs $(sudo docker ps --format '{{.Names}}' | grep plc2) -f # Stop cd /home/stefano/projects/ICS-SimLab-main/curtin-ics-simlab && sudo ./stop.sh PROBLEMA PATH CON SUDO ----------------------- Errore ricevuto: FileNotFoundError quando usato ~/projects/... Causa: sudo NON espande ~ a /home/stefano Soluzione: - Usare SEMPRE percorsi assoluti con sudo - Oppure usare ./scripts/run_simlab.sh (gestisce automaticamente) WORKFLOW COMPLETO ----------------- 1. Testo → configuration.json (LLM): python3 main.py --input-file prompts/input_testuale.txt 2. Config → Scenario completo: python3 build_scenario.py --overwrite 3. Valida fix: python3 validate_fix.py 4. Esegui: ./scripts/run_simlab.sh VALIDAZIONE FIX --------------- $ python3 validate_fix.py ✅ plc1.py: OK (retry fix present) ✅ plc2.py: OK (retry fix present) Verifica manuale: $ grep "_safe_callback" outputs/scenario_run/logic/plc2.py (deve trovare la funzione e la chiamata in _write) COSA CERCARE NEI LOG --------------------- ✅ Successo: NO "Exception in thread" errors in PLC2 ⚠️ Warning: "WARNING: Callback failed after 30 attempts" (PLC1 lento ma ok) ❌ Errore: Container crasha (fix non presente o problema diverso) NOTE IMPORTANTI --------------- 1. SEMPRE usare percorsi assoluti con sudo (no ~) 2. Rebuild scenario dopo modifiche config: python3 build_scenario.py --overwrite 3. Validare sempre dopo rebuild: python3 validate_fix.py 4. Fix è nel generatore (tools/compile_ir.py) quindi si propaga automaticamente 5. Solo dipendenza: time.sleep (stdlib, no package extra) STATUS FINALE ------------- ✅ Fix implementato e testato ✅ Scenario pronto in outputs/scenario_run/ ✅ Validatore conferma presenza fix ✅ Documentazione completa ✅ Progetto pulito e organizzato ✅ Script pronti per esecuzione Pronto per testing con ICS-SimLab! ================================================================================ NUOVA FEATURE: PROCESS SPEC PIPELINE (LLM → process_spec.json → HIL logic) ================================================================================ Data: 2026-01-27 OBIETTIVO --------- Generare fisica di processo tramite LLM senza codice Python free-form. Pipeline: prompt testuale → LLM (structured output) → process_spec.json → compilazione deterministica → HIL logic. FILE CREATI ----------- models/process_spec.py - Modello Pydantic per ProcessSpec - model: Literal["water_tank_v1"] (enum-ready) - dt: float (time step) - params: WaterTankParams (level_min/max/init, area, q_in_max, k_out) - signals: WaterTankSignals (mapping chiavi HIL) tools/generate_process_spec.py - Generazione LLM → process_spec.json - Usa structured output (json_schema) per output valido - Legge prompt + config per contesto tools/compile_process_spec.py - Compilazione deterministica spec → HIL logic - Implementa fisica water_tank_v1 - d(level)/dt = (Q_in - Q_out) / area - Q_in = q_in_max se valvola aperta - Q_out = k_out * sqrt(level) (scarico gravitazionale) tools/validate_process_spec.py - Validatore con tick test - Controlla modello supportato - Verifica dt > 0, min < max, init in bounds - Verifica chiavi segnali esistono in HIL physical_values - Tick test: 100 step per verificare bounds examples/water_tank/prompt.txt - Prompt esempio per water tank FISICA IMPLEMENTATA (water_tank_v1) ----------------------------------- Equazioni: - Q_in = q_in_max if valve_open >= 0.5 else 0 - Q_out = k_out * sqrt(level) - d_level = (Q_in - Q_out) / area * dt - level = clamp(level + d_level, level_min, level_max) Parametri tipici: - dt = 0.1s (10 Hz) - level_min = 0, level_max = 1.0 (metri) - level_init = 0.5 (50% capacità) - area = 1.0 m^2 - q_in_max = 0.02 m^3/s - k_out = 0.01 m^2.5/s COMANDI PIPELINE PROCESS SPEC ----------------------------- # 1. Genera process_spec.json da prompt (richiede OPENAI_API_KEY) python3 -m tools.generate_process_spec \ --prompt examples/water_tank/prompt.txt \ --config outputs/configuration.json \ --out outputs/process_spec.json # 2. Valida process_spec.json contro config python3 -m tools.validate_process_spec \ --spec outputs/process_spec.json \ --config outputs/configuration.json # 3. Compila process_spec.json in HIL logic python3 -m tools.compile_process_spec \ --spec outputs/process_spec.json \ --out outputs/hil_logic.py \ --overwrite CONTRATTO HIL RISPETTATO ------------------------ - Inizializza tutte le chiavi physical_values (setdefault) - Legge solo io:"input" (valve_open_key) - Scrive solo io:"output" (tank_level_key, level_measured_key) - Clamp level tra min/max VANTAGGI APPROCCIO ------------------ 1. LLM genera solo spec strutturata, non codice Python 2. Compilazione deterministica e verificabile 3. Validazione pre-runtime con tick test 4. Estensibile: aggiungere nuovi modelli (es. bottle_line_v1) è semplice NOTE ---- - ProcessSpec usa Pydantic con extra="forbid" per sicurezza - JSON Schema per structured output generato da Pydantic - Tick test verifica 100 step con valvola aperta e chiusa - Se chiavi non esistono in HIL, validazione fallisce ================================================================================ INTEGRAZIONE PROCESS SPEC IN SCENARIO ASSEMBLY ================================================================================ Data: 2026-01-27 OBIETTIVO --------- Integrare la pipeline process_spec nel flusso di build scenario, così che Curtin ICS-SimLab possa eseguire end-to-end con fisica generata da LLM. MODIFICHE EFFETTUATE -------------------- 1. build_scenario.py aggiornato: - Nuovo argomento --process-spec (opzionale) - Se fornito, compila process_spec.json nel file HIL corretto (es. hil_1.py) - Sostituisce/sovrascrive la logica HIL generata da IR - Aggiunto Step 5: verifica che tutti i file logic/*.py referenziati esistano 2. tools/verify_scenario.py creato: - Verifica standalone che scenario sia completo - Controlla configuration.json esiste - Controlla logic/ directory esiste - Controlla tutti i file logic referenziati esistono - Mostra file orfani (non referenziati) FLUSSO COMPLETO CON PROCESS SPEC -------------------------------- # 1. Genera configuration.json (LLM o manuale) python3 main.py --input-file prompts/input_testuale.txt # 2. Genera process_spec.json (LLM con structured output) python3 -m tools.generate_process_spec \ --prompt examples/water_tank/prompt.txt \ --config outputs/configuration.json \ --out outputs/process_spec.json # 3. Valida process_spec.json python3 -m tools.validate_process_spec \ --spec outputs/process_spec.json \ --config outputs/configuration.json # 4. Build scenario con process_spec (sostituisce HIL da IR) python3 build_scenario.py \ --out outputs/scenario_run \ --process-spec outputs/process_spec.json \ --overwrite # 5. Verifica scenario completo python3 -m tools.verify_scenario --scenario outputs/scenario_run -v # 6. Esegui in ICS-SimLab cd /home/stefano/projects/ICS-SimLab-main/curtin-ics-simlab sudo ./start.sh /home/stefano/projects/ics-simlab-config-gen_claude/outputs/scenario_run FLUSSO SENZA PROCESS SPEC (compatibilità backward) -------------------------------------------------- # Build scenario con IR (come prima) python3 build_scenario.py --out outputs/scenario_run --overwrite VERIFICA FILE LOGIC ------------------- Il nuovo Step 5 in build_scenario.py verifica: - Tutti i plcs[].logic esistono in logic/ - Tutti i hils[].logic esistono in logic/ - Se manca un file, build fallisce con errore chiaro Comando standalone: python3 -m tools.verify_scenario --scenario outputs/scenario_run -v STRUTTURA SCENARIO FINALE ------------------------- outputs/scenario_run/ ├── configuration.json (configurazione ICS-SimLab) └── logic/ ├── plc1.py (logica PLC1, da IR) ├── plc2.py (logica PLC2, da IR) └── hil_1.py (logica HIL, da process_spec o IR) NOTE IMPORTANTI --------------- - --process-spec è opzionale: se non fornito, usa IR per HIL (comportamento precedente) - Il file HIL viene sovrascritto se esiste (--overwrite implicito per Step 2b) - Il nome file HIL è preso da config (hils[].logic), non hardcoded - Verifica finale assicura che scenario sia completo prima di eseguire ================================================================================ PROBLEMA SQLITE DATABASE ICS-SimLab ================================================================================ Data: 2026-01-27 SINTOMO ------- Tutti i container (HIL, sensors, actuators, UI) crashano con: sqlite3.OperationalError: unable to open database file CAUSA ----- Il file `physical_interactions.db` diventa una DIRECTORY invece che un file. Succede quando Docker crea il volume mount point PRIMA che ICS-SimLab crei il DB. Verifica: $ ls -la ~/projects/ICS-SimLab-main/curtin-ics-simlab/simulation/communications/ drwxr-xr-x 2 root root 4096 Jan 27 15:49 physical_interactions.db ← DIRECTORY! SOLUZIONE --------- Pulire completamente e riavviare: cd ~/projects/ICS-SimLab-main/curtin-ics-simlab # Stop e rimuovi tutti i container e volumi sudo docker-compose down -v --remove-orphans sudo docker system prune -af # Rimuovi directory simulation corrotta sudo rm -rf simulation # Riavvia (crea DB PRIMA di Docker) sudo ./start.sh /home/stefano/projects/ics-simlab-config-gen_claude/outputs/scenario_run NOTA IMPORTANTE: PATH ASSOLUTO ------------------------------ SEMPRE usare path assoluto completo (NO ~ che non viene espanso da sudo). SBAGLIATO: sudo ./start.sh ~/projects/.../outputs/scenario_run CORRETTO: sudo ./start.sh /home/stefano/projects/.../outputs/scenario_run SEQUENZA STARTUP CORRETTA ICS-SimLab ------------------------------------ 1. rm -r simulation (pulisce vecchia simulazione) 2. python3 main.py $1 (crea DB + container directories) 3. docker compose build (build immagini) 4. docker compose up (avvia container) Il DB viene creato al passo 2, PRIMA che Docker monti i volumi. Se Docker parte con volumi già definiti ma file mancante, crea directory. ================================================================================ FISICA HIL MIGLIORATA: MODELLO ACCOPPIATO TANK + BOTTLE ================================================================================ Data: 2026-01-27 OSSERVAZIONI ------------ - La fisica HIL generata era troppo semplificata: - Range 0..1 normalizzati con clamp continuo - bottle_at_filler derivato direttamente da conveyor_cmd (logica invertita) - Nessun tracking della distanza bottiglia - Nessun accoppiamento: bottiglia si riempie senza svuotare tank - Nessun reset bottiglia quando esce - Esempio funzionante (examples/water_tank/bottle_factory_logic.py) usa: - Range interi: tank 0-1000, bottle 0-200, distance 0-130 - Boolean per stati attuatori - Accoppiamento: bottle fill SOLO se outlet_valve=True AND distance in [0,30] - Reset: quando distance < 0, nuova bottiglia con fill=0 e distance=130 - Due thread separati per tank e bottle MODIFICHE EFFETTUATE -------------------- File: tools/compile_ir.py, funzione render_hil_multi() 1. Detect se presenti ENTRAMBI TankLevelBlock e BottleLineBlock 2. Se sì, genera fisica accoppiata stile esempio: - Variabile interna _bottle_distance (0-130) - bottle_at_filler = (0 <= _bottle_distance <= 30) - Tank dynamics: +18 se inlet ON, -6 se outlet ON - Bottle fill: +6 SOLO se outlet ON AND bottle at filler (conservazione) - Conveyor: distance -= 4; se < 0 reset a 130 e fill = 0 - Clamp: tank 0-1000, bottle 0-200 - time.sleep(0.6) come esempio 3. Se no, fallback a fisica semplice precedente RANGE E SEMANTICA ----------------- - tank_level: 0-1000 (500 = 50% pieno) - bottle_fill: 0-200 (200 = pieno) - bottle_distance: 0-130 interno (0-30 = sotto filler) - bottle_at_filler: 0 o 1 (boolean) - Actuator states: letti come bool() VERIFICA -------- .venv/bin/python3 build_scenario.py --out outputs/scenario_run --overwrite cat outputs/scenario_run/logic/hil_1.py grep "bottle_at_filler" outputs/scenario_run/logic/hil_1.py grep "_bottle_distance" outputs/scenario_run/logic/hil_1.py DA FARE ------- - Verificare che sensori leggano correttamente i nuovi range - Eventualmente aggiungere thread separati come esempio (ora è single loop) - Testare end-to-end con ICS-SimLab ================================================================================ FIX CRITICO: CONTRATTO ICS-SimLab logic() DEVE GIRARE FOREVER ================================================================================ Data: 2026-01-27 ROOT CAUSE IDENTIFICATA ----------------------- ICS-SimLab chiama logic() UNA SOLA VOLTA in un thread e si aspetta che giri per sempre. Il nostro codice generato invece ritornava subito → thread muore → nessun traffico. Vedi: ICS-SimLab/src/components/plc.py linee 352-365: logic_thread = Thread(target=logic.logic, args=(...), daemon=True) logic_thread.start() ... logic_thread.join() # ← Aspetta forever! CONFRONTO CON ESEMPIO FUNZIONANTE (examples/water_tank/) -------------------------------------------------------- Esempio funzionante PLC: def logic(...): time.sleep(2) # Aspetta sync while True: # Loop infinito # logica time.sleep(0.1) Nostro codice PRIMA: def logic(...): # logica return # ← ERRORE: ritorna subito! MODIFICHE EFFETTUATE -------------------- File: tools/compile_ir.py 1. PLC logic ora genera: - time.sleep(2) all'inizio per sync - while True: loop infinito - Logica dentro il loop con indent +4 - time.sleep(0.1) alla fine del loop - _heartbeat() per log ogni 5 secondi 2. HIL logic ora genera: - Inizializzazione diretta (non setdefault) - time.sleep(3) per sync - while True: loop infinito - Fisica dentro il loop con indent +4 - time.sleep(0.1) alla fine del loop 3. _safe_callback migliorato: - Cattura OSError e ConnectionException - Ritorna bool per tracking - 20 tentativi × 0.25s = 5s retry STRUTTURA GENERATA ORA ---------------------- PLC: def logic(input_registers, output_registers, state_update_callbacks): time.sleep(2) while True: _heartbeat() # logica con _write() e _get_float() time.sleep(0.1) HIL: def logic(physical_values): physical_values['key'] = initial_value time.sleep(3) while True: # fisica time.sleep(0.1) VERIFICA -------- # Rebuild scenario .venv/bin/python3 build_scenario.py --out outputs/scenario_run --overwrite # Verifica while True presente grep "while True" outputs/scenario_run/logic/*.py # Verifica time.sleep presente grep "time.sleep" outputs/scenario_run/logic/*.py # Esegui in ICS-SimLab cd ~/projects/ICS-SimLab-main/curtin-ics-simlab sudo docker-compose down -v sudo rm -rf simulation sudo ./start.sh /home/stefano/projects/ics-simlab-config-gen_claude/outputs/scenario_run # Verifica nei log sudo docker logs plc1 2>&1 | grep HEARTBEAT sudo docker logs plc2 2>&1 | grep HEARTBEAT ================================================================================ MIGLIORAMENTI PLC E HIL: INIZIALIZZAZIONE + EXTERNAL WATCHER ================================================================================ Data: 2026-01-27 CONTESTO -------- Confrontando con examples/water_tank/logic/plc1.py abbiamo notato che: 1. Il PLC esempio inizializza gli output e chiama i callback PRIMA del loop 2. Il PLC esempio traccia prev_output_valve per rilevare modifiche esterne (HMI) 3. Il nostro generatore non faceva né l'uno né l'altro MODIFICHE EFFETTUATE -------------------- A) PLC Generation (tools/compile_ir.py): 1. Explicit initialization phase PRIMA del while loop: - Setta ogni output a 0 - Chiama callback per ogni output - Aggiorna _prev_outputs per tracking 2. External-output watcher (_check_external_changes): - Nuova funzione che rileva cambi esterni agli output (es. HMI) - Chiamata all'inizio di ogni iterazione del loop - Se output cambiato esternamente, chiama callback 3. _prev_outputs tracking: - Dict globale che tiene traccia dei valori scritti dal PLC - _write() aggiorna _prev_outputs quando scrive - Evita double-callback: se il PLC ha scritto il valore, non serve callback 4. _collect_output_keys(): - Nuova funzione helper che estrae tutte le chiavi output dalle regole - Usata per generare lista _output_keys per il watcher B) HIL Generation (tools/compile_ir.py): 1. Bottle fill threshold: - Bottiglia si riempie SOLO se bottle_fill < 200 (max) - Evita overflow logico C) Validator (services/validation/plc_callback_validation.py): 1. Riconosce pattern _write(): - Se file definisce funzione _write(), skip strict validation - _write() gestisce internamente write + callback + tracking PATTERN GENERATO ORA -------------------- PLC (plc1.py, plc2.py): def logic(input_registers, output_registers, state_update_callbacks): global _prev_outputs # --- Explicit initialization: set outputs and call callbacks --- if 'tank_input_valve' in output_registers: output_registers['tank_input_valve']['value'] = 0 _prev_outputs['tank_input_valve'] = 0 if 'tank_input_valve' in state_update_callbacks: _safe_callback(state_update_callbacks['tank_input_valve']) ... # Wait for other components to start time.sleep(2) _output_keys = ['tank_input_valve', 'tank_output_valve'] # Main loop - runs forever while True: _heartbeat() # Check for external changes (e.g., HMI) _check_external_changes(output_registers, state_update_callbacks, _output_keys) # Control logic with _write() ... time.sleep(0.1) HIL (hil_1.py): def logic(physical_values): ... while True: ... # Conservation: if bottle is at filler AND not full, water goes to bottle if outlet_valve_on: tank_level -= 6 if bottle_at_filler and bottle_fill < 200: # threshold bottle_fill += 6 ... FUNZIONI HELPER GENERATE ------------------------ _write(out_regs, cbs, key, value): - Scrive valore se diverso - Aggiorna _prev_outputs[key] per tracking - Chiama callback se presente _check_external_changes(out_regs, cbs, keys): - Per ogni key in keys: - Se valore attuale != _prev_outputs[key] - Valore cambiato esternamente (HMI) - Chiama callback - Aggiorna _prev_outputs _safe_callback(cb, retries, delay): - Retry logic per startup race conditions - Cattura OSError e ConnectionException VERIFICA -------- # Rebuild .venv/bin/python3 build_scenario.py --overwrite # Verifica initialization grep "Explicit initialization" outputs/scenario_run/logic/plc*.py # Verifica external watcher grep "_check_external_changes" outputs/scenario_run/logic/plc*.py # Verifica bottle threshold grep "bottle_fill < 200" outputs/scenario_run/logic/hil_1.py ================================================================================ FIX: AUTO-GENERAZIONE PLC MONITORS + SCALA THRESHOLD ASSOLUTI ================================================================================ Data: 2026-01-27 PROBLEMI IDENTIFICATI --------------------- 1) PLC monitors vuoti: i PLC non avevano outbound_connections ai sensori e monitors era sempre []. I sensori erano attivi ma nessuno li interrogava. 2) Scala mismatch: HIL usa range interi (tank 0-1000, bottle 0-200) ma i threshold PLC erano normalizzati (0.2, 0.8 su scala 0-1). Risultato: 482 >= 0.8 sempre True -> logica sbagliata. 3) Modifiche manuali a configuration.json non persistono dopo rebuild. SOLUZIONE IMPLEMENTATA ---------------------- A) Auto-generazione PLC monitors (tools/enrich_config.py): - Nuovo tool che arricchisce configuration.json - Per ogni PLC input register: - Trova il HIL output corrispondente (es. water_tank_level -> water_tank_level_output) - Trova il sensore che espone quel valore - Aggiunge outbound_connection al sensore - Aggiunge monitor entry per polling - Per ogni PLC output register: - Trova l'attuatore corrispondente (es. tank_input_valve -> tank_input_valve_input) - Aggiunge outbound_connection all'attuatore - Aggiunge controller entry B) Scala threshold assoluti (models/ir_v1.py + tools/compile_ir.py): - Aggiunto signal_max a HysteresisFillRule e ThresholdOutputRule - make_ir_from_config.py: imposta signal_max=1000 per tank, signal_max=200 per bottle - compile_ir.py: converte threshold normalizzati in assoluti: - low=0.2, signal_max=1000 -> abs_low=200 - high=0.8, signal_max=1000 -> abs_high=800 - threshold=0.2, signal_max=200 -> abs_threshold=40 C) Pipeline aggiornata (build_scenario.py): - Nuovo Step 0: chiama enrich_config.py - Usa configuration_enriched.json per tutti gli step successivi FILE MODIFICATI --------------- - tools/enrich_config.py (NUOVO) - Arricchisce config con monitors - models/ir_v1.py - Aggiunto signal_max ai rule - tools/make_ir_from_config.py - Imposta signal_max per tank/bottle - tools/compile_ir.py - Usa threshold assoluti - build_scenario.py - Aggiunto Step 0 enrichment VERIFICA -------- # Rebuild scenario .venv/bin/python3 build_scenario.py --overwrite # Verifica monitors generati grep -A10 '"monitors"' outputs/configuration_enriched.json # Verifica threshold assoluti nel PLC grep "lvl <=" outputs/scenario_run/logic/plc1.py # Dovrebbe mostrare: if lvl <= 200.0 e elif lvl >= 800.0 grep "v <" outputs/scenario_run/logic/plc2.py # Dovrebbe mostrare: if v < 40.0 # Esegui ICS-SimLab cd ~/projects/ICS-SimLab-main/curtin-ics-simlab sudo docker-compose down -v sudo rm -rf simulation sudo ./start.sh /home/stefano/projects/ics-simlab-config-gen_claude/outputs/scenario_run ================================================================================ FIX: VALORI INIZIALI RULE-AWARE (NO PIU' TUTTI ZERO) ================================================================================ Data: 2026-01-28 PROBLEMA OSSERVATO ------------------ - UI piatta: tank level ~482, bottle fill ~18 (non cambiano mai) - Causa: init impostava TUTTI gli output a 0 - Con tank a 500 (mid-range tra low=200 e high=800), la logica hysteresis non scrive nulla -> entrambe le valvole restano a 0 -> nessun flusso - Sistema bloccato in steady state SOLUZIONE --------- Valori iniziali derivati dalle regole invece che tutti zero: 1) HysteresisFillRule: - inlet_out = 0 (chiuso) - outlet_out = 1 (APERTO) <- questo fa partire il drenaggio - Tank scende -> raggiunge low=200 -> inlet si apre -> ciclo parte 2) ThresholdOutputRule: - output_id = true_value (tipicamente 1) - Attiva l'output inizialmente FILE MODIFICATO --------------- - tools/compile_ir.py - Nuova funzione _compute_initial_values(rules) -> Dict[str, int] - render_plc_rules() usa init_values invece di 0 fisso - Commento nel codice generato spiega il perché VERIFICA -------- # Rebuild .venv/bin/python3 build_scenario.py --overwrite # Verifica init values nel PLC generato grep -A3 "Explicit initialization" outputs/scenario_run/logic/plc1.py # Deve mostrare: outlet = 1, inlet = 0 grep "tank_output_valve.*value.*=" outputs/scenario_run/logic/plc1.py # Deve mostrare: output_registers['tank_output_valve']['value'] = 1 # Esegui e verifica che tank level cambi cd ~/projects/ICS-SimLab-main/curtin-ics-simlab sudo docker-compose down -v && sudo rm -rf simulation sudo ./start.sh /home/stefano/projects/ics-simlab-config-gen_claude/outputs/scenario_run # Dopo ~30 secondi, UI deve mostrare tank level che scende ================================================================================ FIX: HMI MONITOR ADDRESS DERIVAZIONE DA REGISTER MAP PLC ================================================================================ Data: 2026-01-28 PROBLEMA OSSERVATO ------------------ HMI logs mostrano ripetuti: "ERROR - Error: couldn't read values" per monitors (water_tank_level, bottle_fill_level, bottle_at_filler). Causa: i monitors HMI usavano value_type/address indovinati invece di derivarli dalla mappa registri del PLC target. Es: - HMI monitor bottle_fill_level: address=2 (SBAGLIATO) - PLC2 register bottle_fill_level: address=1 (CORRETTO) - HMI tentava di leggere holding_register@2 che non esiste -> errore Modbus SOLUZIONE IMPLEMENTATA ---------------------- File modificato: tools/enrich_config.py 1) Nuova funzione helper find_register_mapping(device, id): - Cerca in tutti i tipi registro (coil, discrete_input, holding_register, input_register) - Ritorna (value_type, address, count) se trova il registro per id - Ritorna None se non trovato 2) Nuova funzione enrich_hmi_connections(config): - Per ogni HMI monitor che polla un PLC: - Trova il PLC target tramite outbound_connection IP - Cerca il registro nel PLC tramite find_register_mapping - Aggiorna value_type, address, count per matchare il PLC - Stampa "FIX:" quando corregge un valore - Stampa "WARNING:" se registro non trovato (non indovina default) - Stessa logica per controllers HMI 3) main() aggiornato: - Chiama enrich_hmi_connections() dopo enrich_plc_connections() - Summary include anche HMI monitors/controllers ESEMPIO OUTPUT -------------- $ python3 -m tools.enrich_config --config outputs/configuration.json \ --out outputs/configuration_enriched.json --overwrite Enriching PLC connections... Fixing HMI monitors/controllers... FIX: hmi_1 monitor 'bottle_fill_level': holding_register@2 -> holding_register@1 (from plc2) Summary: plc1: 4 outbound_connections, 1 monitors, 2 controllers plc2: 4 outbound_connections, 2 monitors, 2 controllers hmi_1: 3 monitors, 1 controllers VERIFICA -------- # Rebuild scenario python3 build_scenario.py --out outputs/scenario_run --overwrite # Verifica che bottle_fill_level abbia address corretto grep -A5 '"id": "bottle_fill_level"' outputs/configuration_enriched.json | grep address # Deve mostrare: "address": 1 (non 2) # Esegui ICS-SimLab cd /home/stefano/projects/ICS-SimLab-main/curtin-ics-simlab sudo docker-compose down -v && sudo rm -rf simulation sudo ./start.sh /home/stefano/projects/ics-simlab-config-gen_claude/outputs/scenario_run # Verifica che HMI non mostri più "couldn't read values" sudo docker logs hmi_1 2>&1 | grep -i error # UI deve mostrare valori che cambiano nel tempo ================================================================================