ics-simlab-config-gen-claude/APPUNTI.txt

894 lines
30 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

================================================================================
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
================================================================================