# PlanciaConsole Plancia configurabile su Modbus RTU / RS485. Due modalità, decise dalla riga di comando; la configurazione vive in un file XML letto all'avvio. Riusa `..\uModbusRTU.pas` e `..\uGauge.pas` del programma di test (`ProjectPlancia`): i due progetti condividono quei due file, non ne esistono copie. Il bus gira in un thread a parte (`uModbusWorker.pas`), così la finestra non si ferma mai; vedi "Il bus in un thread a parte". ## Avvio PlanciaConsole.exe aboard, legge plancia.xml PlanciaConsole.exe aboard PlanciaConsole.exe config configurazione PlanciaConsole.exe config -f rotta.xml configurazione su un altro file PlanciaConsole.exe rotta.xml aboard su un altro file Senza indicazioni viene riaperta l'ultima plancia usata, e in mancanza di quella `plancia.xml` accanto all'eseguibile. `-f `, `-file=` o un parametro libero indicano un file diverso. ### Modalità aboard Legge l'XML, apre la porta seriale indicata nel file, si connette da sola e opera. A vista c'è solo la plancia più una barra di stato con porta, baud, LED verde/rosso e ultimo messaggio. Nessun comando di configurazione. La finestra si dimensiona sulla plancia. Se il pannello è più grande dello schermo lo zoom si riduce automaticamente per farcelo stare: a bordo è meglio una plancia rimpicciolita che una da raggiungere scorrendo. ### Cambiare plancia a bordo In fondo a destra, nella barra di stato, una casella elenca le plance della stessa cartella del file caricato: tutti gli XML che sono configurazioni di plancia, mostrati con il loro titolo. Scegliendone una: - la porta seriale viene chiusa e riaperta con porta e baud del nuovo file; - la finestra si ridimensiona sul nuovo pannello; - **le bobine non vengono toccate**: i relè restano come sono e la nuova plancia ne rilegge lo stato al primo ciclo di polling. Il file viene letto per prova prima di lasciare quello in servizio: se è rotto resta la plancia di prima e l'errore compare nella barra di stato. La casella compare solo in aboard e solo se nella cartella ci sono almeno due plance. In configurazione si usa "Apri", che chiede anche delle modifiche non salvate. Dopo la scelta il fuoco viene tolto alla casella, così le frecce della tastiera non cambiano plancia per sbaglio. ### Modalità configuration Palette a sinistra, pannello a destra con le proprietà dell'elemento selezionato, barra in alto con Nuovo / Apri / Salva / Salva con nome, griglia e zoom. - **Inserire**: trascina una voce della palette sul pannello. L'elemento nasce centrato sul punto di rilascio; se lì c'è già qualcosa scala in diagonale finché trova posto libero, così non si impilano tutti nello stesso punto. - **Spostare**: trascina l'elemento. Un click senza trascinare non lo sposta: il movimento parte solo oltre la soglia di trascinamento di Windows (pochi pixel), e un asse su cui il mouse non si è mosso non viene riagganciato alla griglia. - **Ridimensionare**: trascina una delle otto maniglie dell'elemento selezionato, oppure scrivi larghezza e altezza nel pannello proprietà. - **Ritocchi da tastiera**, sull'elemento selezionato: **Ctrl+freccia** lo sposta di un pixel, **Maiusc+freccia** cambia le misure di un pixel (destra e giù ingrandiscono, sinistra e su rimpiccioliscono). Un pixel esatto anche con la griglia attiva. Non funzionano mentre si scrive in una casella, dove le frecce restano del testo: cliccare l'elemento riporta i tasti sul pannello. - **Annulla / Ripeti**: pulsanti nella barra, oppure **Ctrl+Z** e **Ctrl+Y** (o Ctrl+Maiusc+Z). Coprono spostamenti, misure, proprietà, tipo, elementi aggiunti e cancellati e le impostazioni generali, fino a 100 passi. Le modifiche in fila sulla stessa cosa, entro un secondo e mezzo l'una dall'altra, contano come un passo solo: le cifre di una larghezza digitata, o Ctrl+freccia tenuto premuto. Lo storico si azzera con Nuovo e Apri. Un'immagine rimossa dalla libreria non torna con Annulla: tornano i riferimenti degli elementi, ma il file va riaggiunto. - **Griglia**: con "Griglia 10 px" attiva posizioni e bordi si agganciano a multipli di 10. - **Zoom**: da 50% a 200%. È solo una lente di lavoro: le coordinate salvate nell'XML restano sempre quelle reali al 100%. - **Configurare**: seleziona l'elemento e compila i campi. Le modifiche si vedono subito. - **Eliminare**: pulsante "Elimina elemento" o tasto Canc. - **Chiusura**: chiudendo la finestra la plancia viene **salvata da sola** nel file su cui si stava lavorando, senza chiedere niente. Solo se il salvataggio non riesce (disco pieno, file di sola lettura) viene chiesto se chiudere comunque. Nuovo, Apri e il passaggio in plancia invece continuano a chiedere cosa fare delle modifiche: lì si può volerle buttare. **Come viene scritto il file.** Il salvataggio non scrive mai sopra il file buono: scrive un `.tmp` accanto e poi scambia i nomi, tenendo la versione precedente come `.bak`. Così un programma chiuso male, un disco pieno o due copie del programma che salvano insieme non possono lasciare un XML troncato, che alla riapertura non si leggerebbe più. Se un file non si apre, accanto c'è il `.bak` della volta prima: si rinomina e si riparte da lì. **Se il file non si è aperto**, chiudendo non ci si salva sopra: il programma avverte e propone "Salva con nome", perché salvare il pannello vuoto cancellerebbe quello che il file contiene. All'avvio senza indicazioni viene riaperta **l'ultima plancia usata**: il percorso è ricordato in `plancia-ultima.txt` accanto all'eseguibile, e si aggiorna a ogni apertura, salvataggio e cambio di plancia a bordo. Se quel file non c'è, o la plancia è stata spostata, si riparte da `plancia.xml`. Un file indicato sulla riga di comando ha comunque la precedenza. In configurazione la porta seriale **non** viene aperta: si disegna la plancia senza rischiare di comandare relè per sbaglio. Per provare i canali si usa il programma di test `ProjectPlancia`. ### Passare da una modalità all'altra Chi è partito con `config` trova in fondo a destra, nella barra di stato, un pulsante **"Vai in plancia"**: la palette e le proprietà spariscono, la finestra si veste sulla plancia e la porta seriale si apre. Da lì il pulsante diventa **"Torna a configurare"** e riporta indietro, chiudendo la porta. Serve a provare quello che si è appena disegnato senza chiudere e riaprire il programma con un'altra riga di comando. Tre cose da sapere: - Se ci sono modifiche non salvate viene chiesto cosa farne **prima** di passare in plancia, altrimenti si perderebbero in silenzio. - Tornando a configurare la porta seriale viene **chiusa**, e le spie tornano a "dato non disponibile": una plancia che non sta leggendo non deve sembrare in servizio. - Posizione della finestra e zoom di lavoro vengono ritrovati come li si era lasciati, perché in plancia la finestra si ridimensiona sul pannello. **Chi è partito in `aboard` non vede il pulsante.** Una plancia avviata in servizio non deve offrire la strada per essere modificata per sbaglio; per configurarla si riavvia con `config`. ### Modalità notturna Accanto c'è il pulsante **"Notte"**, questo disponibile in entrambe le modalità. Scurisce l'intera plancia: fondo quasi nero, immagine di sfondo ricalcolata scura, e ogni colore abbassato alla stessa frazione, così i rapporti fra i colori restano quelli del giorno e il quadro resta leggibile. Serve in navigazione notturna, dove un pannello chiaro a tutto schermo brucia l'adattamento al buio e per qualche minuto non si vede più fuori. Le scritte non vengono abbassate ma **sostituite con un ambra**: una serigrafia nera abbassata resterebbe nera, cioè invisibile sul fondo scuro. L'ambra è anche il colore che disturba meno la visione notturna. Vale per etichette, legende dei selettori, marchi e scale dei gauge. Si torna al giorno con lo stesso pulsante, che nel frattempo dice "Giorno". La scelta **non** viene salvata nell'XML: è una condizione del momento, non una proprietà della plancia. ## Tipi di elemento | Tipo | Funzione Modbus | Comportamento | |---|---|---| | Pulsante | `WriteSingleCoil` (05) | Chiude alla pressione, riapre al rilascio (es. horn) | | Interruttore | `WriteSingleCoil` (05) + `ReadCoils` (01) | Commuta alla pressione del mouse e resta premuto | | Selettore | `WriteSingleCoil` (05) + `ReadCoils` (01) | Manopola rotativa a 2 o 3 posizioni, anche a ritorno di molla | | Spia | `ReadDiscreteInputs` (02) | Sola lettura, LED di colore configurabile | | Gauge | `ReadHoldingRegisters` (03) | Colonna con scala, soglie e valore | | Display | `ReadHoldingRegisters` (03) | Numero a sette segmenti rossi su fondo nero | | Immagine | nessuna | Grafica decorativa: loghi, sagome, cornici | | Testo | nessuna | Scritta serigrafata sul pannello | Il campo `channel` è l'indirizzo Modbus della bobina o del registro, `slave` l'indirizzo del nodo sul bus. Immagine e Testo non hanno né slave né canale. Gli interruttori e i selettori rileggono le bobine ad ogni ciclo: se un relè cambia stato per altra via, o una scrittura non va a segno, il comando a video si riallinea. I pulsanti momentanei non vengono riallineati, perché il loro stato dipende dal mouse. ### All'ingresso in plancia la plancia si allinea al campo Entrando in modalità plancia (all'avvio, tornando dalla configurazione o cambiando plancia) la prima lettura serve a mettere tutto nella posizione in cui è il campo, non solo a controllare: - **interruttori e selettori** dalle bobine (funzione 01); - **pulsanti** dalle bobine, ma **solo in questa prima lettura**: se un canale momentaneo è rimasto eccitato va mostrato chiuso, mentre dopo lo stato del pulsante lo decide il mouse e rileggerlo lo farebbe lampeggiare; - **spie** dagli ingressi digitali (funzione 02), come sempre. **In questa prima lettura le bobine si chiedono una per una**, non a blocco. Un blocco che arriva oltre l'ultimo canale del modulo fallisce tutto, e con lui fallirebbe l'allineamento anche dei comandi su canali che esistono: chiedendo un canale per volta fallisce solo la richiesta del canale che non c'è. Costa un giro più lento, una volta sola. Finito l'allineamento si torna alle richieste raggruppate, che sono quelle che tengono leggero il polling. Chiudendo il programma non viene scritto niente sul bus: i relè restano come sono, ed è la plancia che alla ripartenza si adatta a loro. Quando l'allineamento è fatto la barra di stato dice quanti canali sono stati letti, quanti comandi risultano chiusi e quanti canali non hanno risposto; se non risponde nessuna bobina si riprova al ciclo dopo, invece di dare per aperto quello che non si sa. Una spia già in allarme all'avvio fa suonare il suo cicalino. ### Più comandi sulla stessa bobina Lo stesso `slave` e `channel` si possono mettere su più elementi: lo stesso relè comandato da due punti della plancia, o un pulsante e un interruttore sullo stesso circuito. Premendone uno **gli altri si muovono subito**, senza aspettare la rilettura: mezzo secondo di disaccordo fra due comandi che sono la stessa cosa si nota. Vale in tutte le combinazioni, compresi i selettori (per quelli a 3 posizioni conta la bobina del lato). Le spie no: leggono gli ingressi digitali, che sono un altro spazio di indirizzi, quindi una spia sullo stesso numero di canale di una bobina non è la stessa cosa e non viene toccata. Una risposta **più vecchia del comando appena dato** viene scartata. Le letture partono a giri regolari e la risposta può arrivare dopo un click: crederle farebbe tornare indietro il comando per un giro, e si vedrebbe il comando spegnersi, riaccendersi e rispegnersi. Ogni comando segna l'istante in cui ha scritto la sua bobina, e le risposte partite prima di quel momento non vengono applicate a quella bobina. **Attenzione**: questo riallineamento è anche il motivo per cui un interruttore può *sembrare* un pulsante. Se la rilettura della bobina risponde 0 — relè assente, indirizzo sbagliato, scrittura non andata a segno — l'interruttore torna su da solo entro un ciclo di polling, e a occhio sembra che non resti premuto. Il tipo dell'elemento non c'entra: si guarda la barra di stato. ### Cambiare tipo dopo il disegno Pulsante, interruttore e spia si scambiano fra loro in qualsiasi momento, dalla casella **Tipo** in cima al pannello proprietà, come si fa con la forma o il canale. Condividono tutti i campi — canale, colori, forma, immagini, etichetta — quindi il passaggio non perde niente. Cambia solo il funzionamento: il pulsante chiude il contatto finché lo tieni premuto, l'interruttore commuta e resta, la spia non si preme e si accende quando l'ingresso è chiuso. Serve soprattutto per le lenti colorate che sulla foto di un quadro sembrano pulsanti ma sono allarmi, come FIRE ALARM e OIL STEERING di ZEBRA. **Attenzione al canale** passando da comando a spia o viceversa: il numero resta lo stesso ma cambia significato. Per pulsanti e interruttori è una bobina da comandare, per la spia un ingresso digitale da leggere (funzione 02), che di solito sta su un altro modulo. La barra di stato lo ricorda al momento del cambio. Cambiando tipo l'elemento riparte da spento, perché un pulsante non viene più riletto dal campo e resterebbe illuminato per sempre. Se l'etichetta era ancora quella predefinita segue il nuovo tipo, altrimenti resta la tua. ### Selettore rotativo La leva **ruota**: passando da una posizione all'altra si vede girare, sempre passando per l'alto, in 130 millisecondi con partenza e arrivo morbidi. È solo quello che si vede: il comando sul bus parte subito, all'inizio del movimento. In configurazione la leva sta ferma dove dice la definizione. Si "gira" premendo dal lato verso cui lo si vuole portare: pressione a sinistra della manopola per scendere di una posizione, a destra per salire. La legenda sopra la manopola è testo libero (`OFF ◄ 0 ► ON`, `STOP ◄ 0 ► START`, …). Il cablaggio dipende dal numero di posizioni: - **2 posizioni**: una bobina, quella di `channel`. Chiusa = posizione destra. - **3 posizioni**: **due bobine**, `channel` e (salvo indicazione) `channel+1`. La prima chiusa = posizione sinistra, la seconda chiusa = destra, entrambe aperte = centro (0). Quindi un selettore a 3 posizioni **occupa due canali**: nel numerare i canali va lasciato il buco. Per configurarlo: tipo Selettore, **Posizioni = 3**, e in **Canale** la bobina del lato sinistro. La bobina di destra si imposta nel riquadro "Selettore", in **Canale lato destro**: lasciandolo vuoto è il canale successivo (l'etichetta ricorda quale sarebbe), e si compila solo quando sul modulo le due bobine non sono contigue. Nell'XML è l'attributo `channel2`, scritto solo se indicato. Il suggerimento a comparsa dell'elemento mostra entrambe le bobine. #### Ritorno a molla Con **Ritorno a molla** spuntato (nell'XML `momentary="true"`) il selettore non resta dove lo porti: tiene la posizione finché lo tieni premuto e torna al centro appena lasci il mouse, aprendo entrambe le bobine. È il comando di avviamento dei quadri veri, `STOP ◄ 0 ► START`: si tiene su START finché il motore parte, e si molla. - Si preme direttamente dal lato voluto, senza passare per lo zero. - Al rilascio torna al centro **sempre**, anche se il mouse è finito fuori dall'elemento e anche se la scrittura di andata era fallita: un comando di avviamento non deve mai restare eccitato. - Finché è premuto il polling non lo sposta, altrimenti una lettura arrivata in quell'istante lo farebbe scattare al centro sotto il dito. - Vale anche a 2 posizioni: premuto = chiusa, lasciato = aperta. In `antago.xml` e in `plancia-mfd.xml` è così il selettore **GENERATOR**. Le due bobine **non vengono mai chiuse insieme**: a ogni cambio di posizione si apre prima quella del lato opposto e solo dopo si chiude l'altra. Se la seconda scrittura fallisce il selettore resta al centro, con tutte e due aperte. Questa però è una protezione del programma: se i due lati comandano qualcosa che non deve mai partire insieme (due sensi di marcia, due alimentazioni), serve comunque l'interblocco elettrico fra i due relè. ### Gauge a quadrante Con `style="dial"` il gauge non è una colonna ma uno strumento a lancetta, come quelli dei display di plancia: scala su 270 gradi con tacche e numeri, lancetta, e sotto il perno la lettura in cifre con le unità (`decimals` ne fissa i decimali). Se l'elemento è più alto che largo il titolo va sotto al quadrante, altrimenti dentro. La fascia della scala è grigia. Con `warnBelow` / `warnAbove` impostati diventa verde nell'intervallo buono e rossa fuori: senza soglie resta grigia, perché un quadrante tutto verde direbbe "tutto a posto" senza saperlo. Senza dato valido la lancetta non c'è e la lettura mostra `---`. Stile e decimali del quadrante per ora si impostano solo nell'XML; il pannello proprietà non li mostra ma il salvataggio li conserva. ### Display Mostra il registro scalato in unità reali, allineato a destra sul numero di cifre richiesto, con zeri davanti come i display veri. `digits` è il numero di cifre, `decimals` quante dopo la virgola. Senza dato valido i segmenti restano tutti spenti. ### Spia Il colore da accesa si imposta con l'attributo `color` (`#RRGGBB`): rosso per gli allarmi, verde per i consensi, giallo e arancio per i livelli. Se l'etichetta è vuota il LED — o l'immagine PNG che lo sostituisce — occupa tutto l'elemento, così si possono appoggiare spie piccole sopra una grafica. Vale per qualunque elemento con l'etichetta fuori dal disegno: senza etichetta non viene riservata nessuna fascia. #### Allarme sonoro Ogni spia può far suonare qualcosa quando si accende. Nel pannello proprietà, riquadro **Allarme sonoro**: - **Suona quando si accende**: la casella che accende o spegne l'allarme. - **File**: si sceglie fra i suoni presenti nella cartella **`suoni\` accanto al file XML della plancia** (MP3 e WAV). Come per le immagini, i suoni viaggiano con la plancia quando si copia la cartella. - **Aggiorna**: rilegge la cartella. Serve quando si aggiunge un file mentre il programma è già aperto, senza doverlo riavviare. Nell'XML sono gli attributi `alarm="true"` e `sound="cicalino.mp3"` sulla spia. Il nome del file resta salvato anche con l'allarme spento, così si può riaccendere senza riscegliere il suono. Come suona: - **Parte sul fronte**, quando la spia passa da spenta ad accesa, e poi **va in ciclo**: finito il file ricomincia, e continua finché l'allarme c'è. Un cicalino che suona una volta sola lo si perde se in quel momento si sta guardando altrove. Si ferma quando l'allarme rientra, quando lo si zittisce premendo la spia, o lasciando la plancia. - **Se l'allarme è già attivo all'avvio** suona appena arriva la prima lettura: una plancia che parte con una sentina piena deve dirlo. - **Non blocca niente**: il suono va per conto suo, la plancia resta comandabile. - Due allarmi diversi si sovrappongono; lo stesso allarme che si ripete riparte da capo. - **Quando l'allarme rientra il suono si ferma subito**, senza aspettare la fine del file: serve per i suoni lunghi, una sirena che continua a suonare su un allarme già passato è peggio del silenzio. Se lo stesso file è assegnato a più spie, tace solo quando si è spenta l'ultima ancora accesa. - **Se il file manca o non si può suonare**, la barra di stato lo dice: un cicalino muto senza avviso è peggio di nessun cicalino. - Uscendo dalla plancia (torna a configurare, chiusura) i suoni si fermano. #### Zittire un allarme **Si preme la spia che sta suonando**: è il gesto che viene naturale, si preme quello che dà fastidio. Ogni pressione allunga il silenzio: | Pressioni | Silenzio | |---|---| | 1 | un minuto | | 2 | dieci minuti | | 3 | un'ora | | 4 | allarme di nuovo attivo | Le pressioni contano finché il silenzio dura: dopo che è scaduto si ricomincia da un minuto. Il suono in corso si ferma subito alla prima pressione. **Il silenzio non spegne la spia**: il LED resta acceso, con una sbarra sopra, perché guardando il quadro si deve capire che quell'allarme c'è ancora e sta suonando a vuoto. Premere una spia spenta, o una senza allarme, non fa niente. **Quando il silenzio scade, se la condizione è ancora presente l'allarme torna a suonare**: è il senso di zittire "per un minuto" invece che per sempre. In basso a destra, nella barra di stato, compare il tasto **"N allarmi zittiti (tempo) — riattiva"**, che dice quanti sono e quanto manca al primo che torna a suonare. Premendolo si riattivano tutti subito, e quelli ancora presenti suonano di nuovo. Il tasto si vede solo finché c'è almeno un allarme zittito. Se l'allarme rientra da solo, il silenzio si azzera con lui: la volta dopo si riparte da un minuto. Tornando a configurare, o cambiando plancia, i silenzi si azzerano tutti. In `suoni\` c'è `cicalino.wav`, due bip generati per provare subito; va sostituito con il suono vero. La spia accetta anche la forma (`shape`) dei pulsanti, per somigliare alle lenti di allarme dei quadri: - **`round`** — ghiera metallica e lente tonda. Spenta la lente resta del suo colore ma scura (`colorOff`, o `color` scurito se manca), accesa prende `color` pieno con un riflesso. La ghiera non cambia: è una spia, non un comando premuto. - **`screen`** — tasto a video che si riempie di `color` quando l'ingresso è chiuso. - le altre forme — il LED tondo di sempre. In ogni forma cliccare una spia non fa nulla e non manda niente sul bus. ### Aspetto di pulsanti, interruttori e spie **La lente di un pulsante o di un interruttore non cambia mai colore.** Lo stato si legge solo dal bordo, come su un quadro vero, dove il vetro è sempre dello stesso colore e quello che cambia è la luce dietro. - Forma **tonda**: la ghiera attorno alla lente è grigia a riposo e diventa **azzurra luminosa** quando il comando è acceso, o mentre un pulsante è tenuto premuto — più chiara contro la lente e più carica all'orlo, come se la luce venisse da dentro. - Forma **squadrata o a pillola**: non c'è ghiera, quindi è il bordo stesso che si ingrossa e prende lo stesso azzurro. Il colore della lente è **`colorOff`**. Su pulsanti e interruttori `color` non tocca più il vetro: resta usato dalle spie, dove il cambio di colore è tutto quello che c'è da vedere. Anche l'etichetta scritta *dentro* al comando resta uguale: prima diventava bianca e in grassetto da acceso, ed era un secondo modo di dire la stessa cosa che ora dice il bordo. Tre attributi permettono di far somigliare i comandi a quelli del quadro vero, e si impostano anche dal riquadro "Aspetto" del pannello proprietà: - **`shape`** — `auto` (pillola per i pulsanti, squadrato per gli interruttori), `round`, `rect`, `pill`, `screen`. I quadri di bordo hanno quasi sempre pulsanti tondi: con `round` l'elemento viene disegnato con ghiera metallica e lente, come un pulsante illuminato da incasso. `screen` ("a video") è il tasto di un display multifunzione ed è l'eccezione alla regola della lente: spento è un riquadro scuro con il filo chiaro (`colorOff` per tingerlo), acceso **si riempie** del colore `color`. La scritta sopra passa da chiara a nera quando il fondo diventa chiaro. - **`captionPos`** — `center` (etichetta scritta sul comando), `below`, `above`. Sui quadri veri l'etichetta è serigrafata **sotto** al pulsante: con `below` il disegno si restringe per farle posto e il testo resta nero sulla lamiera invece di stare sopra la lente. Le etichette lunghe vanno a capo e la fascia si allarga da sola. - **`color`** e **`colorOff`** — colore della lente accesa e a riposo. Serve perché un pulsante STOP è rosso anche da spento e si limita a illuminarsi: senza `colorOff` la lente a riposo è grigio-azzurra neutra. ### Testo e marchi L'elemento Testo non serve solo alle scritte: con una cornice diventa un marchio serigrafato, disegnato **con il font e non con un'immagine**, quindi nitido a qualsiasi zoom invece di sgranare come farebbe un PNG ingrandito. - **`fontName`** — nome del font, per esempio `Times New Roman` o `Arial Black`. Vuoto = quello del pannello. Funziona su qualsiasi elemento, non solo sul Testo. - **`spacing`** — pixel in più fra una lettera e l'altra. I marchi hanno quasi sempre le lettere larghe e senza questo non somigliano. - **`frame`** — `none`, `oval`, `rect`, `round` (rettangolo stondato, cioè a pastiglia). Disegnata con GDI+ in antialiasing, altrimenti un ovale grande verrebbe scalettato. - **`frameWidth`** — spessore del tratto della cornice. - **`color`** — colore di inchiostro e cornice. Un marchio su più righe si compone sovrapponendo più elementi: uno con la sola cornice (etichetta vuota), uno con il nome in grande, uno con il sottotitolo piccolo. È così che sono fatti i loghi ZEBRA e ANTAGO negli esempi. Per andare a capo dentro un'etichetta si usa ` `, per esempio `caption="PORT ENGINE"`. ## Immagini La libreria immagini è nel pannello di sinistra: "Aggiungi..." accetta PNG, JPG e BMP (selezione multipla), "Rimuovi" toglie l'immagine e la sgancia dagli elementi che la usavano, chiedendo conferma. Non è una `TImageList`: quella imporrebbe a tutte le immagini la stessa dimensione, mentre qui un LED da 32 px e un logo da 600 px devono convivere. **Nell'XML si salvano nome e percorso, non i pixel.** I file restano sul disco e si possono sostituire senza toccare la configurazione. I percorsi sono relativi alla cartella del file XML quando possibile, così l'intera plancia si trasporta copiando una cartella; "Salva con nome" in un'altra cartella ricalcola i percorsi da solo. Dove si usano: - **Pulsante, interruttore, spia**: due immagini, a riposo e attiva. Se il campo "immagine a riposo" è vuoto si torna al disegno vettoriale predefinito. Se è valorizzata solo quella a riposo, l'elemento non cambia aspetto quando si attiva. - **Immagine**: una sola, decorativa. - **Sfondo del pannello**: si sceglie in "Generale → Immagine di sfondo", ed è stirata su tutto il pannello. Le PNG con trasparenza funzionano: gli elementi non riempiono il proprio sfondo, quindi un logo con alpha si fonde con lo sfondo della plancia. L'etichetta continua a essere scritta sopra l'immagine, in bianco con contorno nero perché resti leggibile su fondo chiaro e scuro; sulle spie va in basso per non coprire il LED. Se l'immagine contiene già la scritta, basta svuotare il campo Etichetta. Se un file manca o non si apre, nella libreria il nome compare seguito da `[!]` e l'elemento mostra un riquadro tratteggiato in configurazione. ## Il file XML `kind` vale `button`, `switch`, `rotary`, `lamp`, `gauge`, `display`, `image` o `label`. - `fontSize`: altezza del testo in pixel logici; assente = font del pannello. - `imageOff` / `imageOn`: nomi presi da ``; solo per pulsanti, interruttori e spie. - `image`: l'immagine dell'elemento decorativo. - `background` su ``: immagine di sfondo. - `ink` su ``: colore della serigrafia, `#RRGGBB`, nero se assente. Vale per etichette sotto/sopra i comandi, legende e titoli di selettori, display e quadranti, e per i `label` senza `color`. Le plance a fondo scuro lo mettono chiaro. Si imposta solo nell'XML. - `style` (`bar` o `dial`) e `decimals`: aspetto del `gauge`, vedi sopra. - `color` / `colorOff`: colore acceso e a riposo di spie, pulsanti e interruttori, `#RRGGBB`. Su `label` è il colore di inchiostro e cornice. - `shape` e `captionPos`: forma del comando e posizione dell'etichetta. - `fontName` e `spacing`: font e spaziatura fra le lettere. - `frame` e `frameWidth`: cornice attorno a un `label`. - `alarm` e `sound`: solo per `lamp`, allarme sonoro all'accensione; il file sta nella cartella `suoni\` accanto all'XML. - `positions` (2 o 3), `legend`, `channel2` e `momentary`: solo per `rotary`. `channel2` è la bobina del lato destro di un selettore a 3 posizioni; assente = `channel+1`. `momentary="true"` è il ritorno a molla. - `digits` e `decimals`: solo per `display`. - `raw*`, `eng*` e `units` valgono per `gauge` e `display`: `rawMin`/`rawMax` sono il fondo scala del modulo (4095 = ADC a 12 bit), `engMin`/`engMax` i valori reali corrispondenti. - `warnBelow` / `warnAbove`: solo per `gauge`, sono le soglie oltre le quali la colonna diventa rossa (`-1E30` e `1E30` significano "soglia non impostata"). I numeri decimali si scrivono con il punto. Il file si può modificare a mano: un attributo mancante prende il valore di default. Per i caratteri speciali nelle legende si usano le entità XML, per esempio `◀` e `▶` per le frecce ◄ ►. ## File di esempio - `esempio-plancia.xml` — solo elementi vettoriali, nessuna immagine. - `esempio-immagini.xml` — usa la cartella `immagini\`: sfondo, logo con trasparenza, pulsante e LED con grafica. Le immagini sono segnaposto generate per la prova, da sostituire con quelle vere. - `antago.xml` — riproduzione del quadro elettrico ANTAGO, ricavata dalla foto del quadro reale. Il pannello è 1270×950 come la foto, così ogni elemento sta dove sta sul quadro vero. - `zebra.xml` — riproduzione della pulsantiera ZEBRA: 7 colonne × 6 righe di pulsanti tondi illuminati, i due selettori delle ventole, i comandi motore con gli ovali PORT/STBD ENGINE e il selettore PARALLEL a tre posizioni. La foto è in prospettiva, quindi la griglia è stata ridisegnata dritta (passo 120 in orizzontale, 145 in verticale); etichette, forme e colori delle lenti vengono dalla foto. - `plancia-mfd.xml` — tutti i comandi, le spie e le misure di ZEBRA e ANTAGO su una sola plancia, nello stile della foto `immagini\Esempio plancia.jpg`: cornice scura con tasti a video ai lati, schermo nero con quattro quadranti, i due display dei caricabatterie, il sinottico della barca con le spie e i tasti illuminati di motori e servizi; sotto, due file di selettori. Pannello 1900×1080. Gli indirizzi di ZEBRA sono quelli di `zebra.xml`; **le bobine di ANTAGO sono spostate da slave 1 a slave 4**, perché sullo slave 1 si sovrapponevano a quelle di ZEBRA, e i due comandi di prova "Luce" e "Horn" di `antago.xml` (che stavano sul canale 0, lo stesso del selettore VOLTMETER) sono sui canali 26 e 27 dello slave 4. In tutti **slave e canali sono inventati** e vanno rimappati sui moduli reali prima di collegare il bus. ## Strumenti `strumenti\` contiene gli script PowerShell usati per preparare la grafica dei due quadri: | Script | Cosa fa | |---|---| | `ruota.ps1` | Raddrizza una foto scattata in verticale | | `ritaglia-grafica.ps1` | Ritaglia un pezzo di serigrafia dalla foto e rende trasparente il fondo chiaro, lasciando solo il tratto | | `genera-sfondo.ps1` | Disegna la lamiera del pannello con cornice e viti, di qualsiasi misura | | `genera-mfd.ps1` | Disegna cornice e schermo di `plancia-mfd.xml` e la sagoma della barca ANTAGO in chiaro per il fondo nero | Il ritaglio dalla foto conviene solo per i disegni che non si possono ricostruire, come la sagoma della barca del quadro ANTAGO. **Le scritte no**: i marchi ZEBRA e ANTAGO e gli ovali PORT/STBD ENGINE sono elementi Testo con font e cornice, non immagini, così restano nitidi a ogni ingrandimento. ### Diagnostica del bus Due programmi a riga di comando servono quando un modulo non si legge. Vanno lanciati **con PlanciaConsole chiusa**, perché la porta si apre una volta sola. Si ricompilano con i `build-*.bat` accanto ai sorgenti. | Programma | Cosa fa | |---|---| | `scanbus.exe [porta] [baud] [ultimoIndirizzo]` | Interroga gli indirizzi uno per uno e dice chi risponde, con quali funzioni e che dati | | `dumpframe.exe [porta] [baud] [slave] [funzione] [start] [quantità]` | Manda una sola richiesta e stampa i byte grezzi della risposta con la verifica della CRC | | `monitorio.exe [porta] [baud] [slave] [canali] [cicli]` | Mostra gli ingressi digitali in tempo reale, una riga ad ogni cambiamento | | `ringtest.exe [file.bmp]` | Disegna i comandi tondi spenti e accesi a tre misure e salva l'immagine: serve a giudicare una modifica ai colori guardandola | `scanbus` serve a trovare l'indirizzo di un modulo appena montato. `dumpframe` serve quando `scanbus` dà risultati incoerenti: mostrando il frame byte per byte distingue un modulo che non risponde da due moduli che rispondono insieme. **Due nodi con lo stesso indirizzo** è il guasto più frequente quando si aggiunge un modulo: i moduli escono quasi tutti con indirizzo 1, e sul bus le due risposte si sovrappongono. Il sintomo è una risposta di lunghezza variabile, spesso con byte a zero in testa, e la CRC che non torna. ## Il bus in un thread a parte Il thread principale non parla mai con la porta seriale. Tutto il Modbus vive in un thread suo (`uModbusWorker.pas`), che apre la COM, fa le letture e le scritture, e lascia i risultati in una cassetta condivisa. La finestra li ritira con il suo timer. Così un timeout — mezzo secondo per richiesta — costa tempo al thread del bus e non alla plancia, che resta trascinabile, ridisegnabile e cliccabile anche con tutti i moduli staccati. **Un thread solo, non uno per le letture e uno per le scritture.** Su RS485 il filo è uno: due richieste insieme si sovrappongono e le risposte arrivano mescolate. Chi comanda deve comunque aspettare la fine della richiesta in corso, quindi un secondo thread aggiungerebbe solo un lucchetto attorno alla porta. La reattività dei comandi si ottiene con la **precedenza**: la coda delle scritture viene svuotata prima di ogni lettura, non solo a ogni giro, quindi un pulsante premuto parte al massimo dopo la richiesta in corso. Come si comporta: - **Cliccare un comando torna subito.** La scrittura va in coda; l'elemento si accende fidandosi. Se la scrittura non va a segno lo dice la barra di stato, e la rilettura delle bobine rimette l'elemento a posto entro un ciclo. - **L'ordine è garantito**: le due bobine di un selettore a 3 posizioni, o la pressione e il rilascio di un pulsante, partono nella sequenza in cui sono state messe in coda. - **La porta si riapre da sola.** Se non c'è, o se cade (adattatore USB staccato), il thread riprova ogni due secondi: non serve più riavviare il programma. - **Chiudendo**, l'attesa del thread dura al massimo quanto la richiesta in corso. ## Aggiungere slave e canali Niente nel programma è legato ai moduli montati adesso: una plancia si può disegnare prima, e i moduli si aggiungono quando arrivano. - **Slave**: qualsiasi indirizzo da 1 a 247, quanti se ne vuole sullo stesso bus. Ogni slave nuovo aggiunge solo le sue richieste al giro di polling. - **Canali**: qualsiasi numero da 0 a 65535, senza doverli tenere contigui. Canali molto distanti sullo stesso slave allargano però l'intervallo letto (vedi Polling), quindi conviene raggrupparli. - **Limiti di una singola richiesta**, imposti dal protocollo: 2000 bit per bobine e ingressi, 125 registri. Oltre, la richiesta viene saltata e la barra di stato lo dice: si spezza l'intervallo o si sposta l'elemento su un altro slave. - **Nuove porte**: la porta è una per plancia (``). Due bus separati si fanno con due plance, e si passa dall'una all'altra con la casella in basso a destra. **Una richiesta che non riesce si divide da sola.** Se la lettura di un blocco di bobine fallisce, il programma la spezza a metà e riprova con le due metà, e così via: un modulo da 32 canali interrogato fino al 38 finisce per farsi leggere i suoi 32 in poche richieste, invece di non dare più niente. Ogni divisione compare nella barra di stato. Le divisioni imparate valgono finché non si cambia plancia o non si riapre la porta. **Le richieste che non rispondono non danno fastidio.** Dopo tre errori di fila una richiesta muta viene messa in pausa e riprovata ogni cinque secondi, invece di costarne il timeout a ogni giro: così una plancia disegnata in anticipo resta scorrevole e i moduli che rispondono vengono letti alla velocità giusta. Appena il modulo viene collegato riparte da solo, entro quei cinque secondi. La barra di stato distingue i due casi: `Timeout: nessuna risposta` è una richiesta che si sta ancora provando, `non risponde, riprovo fra 4 s` è una messa in pausa. La pausa si azzera da sola quando si cambia plancia, quando si modificano i canali in configurazione e quando la porta viene riaperta. La pausa vale **per singola richiesta**, non per modulo: sullo stesso nodo le bobine possono rispondere benissimo mentre gli ingressi, che quel modulo non ha, danno errore. Mettere in pausa tutto il nodo per colpa degli ingressi lascerebbe la plancia cieca proprio su quello che si legge. ## Polling Ad ogni ciclo (`pollMs`) gli elementi vengono raggruppati per slave e tipo, e per ogni gruppo parte **una sola** richiesta che copre i canali da quello più basso a quello più alto. Aggiungere elementi contigui non aumenta il traffico sul bus; canali molto distanti sullo stesso slave sì, perché allargano l'intervallo letto. Ogni richiesta va per conto suo: se un modulo non risponde, gli altri gruppi vengono letti comunque. Solo le spie e i gauge di **quella** richiesta passano a "dato non disponibile"; il resto della plancia continua a leggere. Interruttori e selettori invece non si azzerano, perché la loro posizione è un comando dato, non una misura. L'errore finisce nella barra di stato dicendo **cosa** non si è potuto leggere: slave, funzione e intervallo di canali, per esempio Lettura fallita su 4 richieste, la prima: slave 3, ingressi 0-14 (FC02): timeout Così si capisce subito se manca un modulo, se l'indirizzo è sbagliato o se si sta leggendo oltre i canali che il modulo ha (tipico: un modulo da 8 bobine interrogato per 37, perché un elemento ha un canale alto). Il polling continua e si riallinea da solo appena il bus risponde.