# MQTT

> Dispositivi che pubblicano attraverso un broker MQTT esterno, in MQTT semplice o Sparkplug B, con il broker modellato come il nodo che condividono tutti i dispositivi sotto di esso, e i dati della stazione stessa pubblicati su un broker.

Il driver MQTT legge e scrive i tag di apparecchiature che pubblicano attraverso un **broker MQTT esterno**: un gateway, un sensore wireless, un flusso Node-RED, un nodo edge che parla Sparkplug B. La stazione è client di quel broker: si abbona ai topic che leggono i suoi tag e pubblica le scritture fatte sui suoi tag, e ogni broker può pubblicare i dati della stazione stessa, i tag, le variabili di Logic e gli allarmi elencati nella sua [scheda Pubblicare](#the-publish-card), come JSON o in Sparkplug B. È client e non broker, quindi l'apparecchiatura e la stazione si collegano entrambe a un broker: se non ce n'è uno, il [broker MQTT](connector-mqtt-broker) della stazione stessa può esserlo, e accenderlo aggiunge a questo driver il broker **Questa stazione**; [Installare un broker](#installing-a-broker) mostra come metterne in piedi uno separato.

Tutti i dispositivi dietro un broker usano la stessa connessione, perciò la topologia è
**Driver → Broker → Dispositivo → Tag**: il **broker** possiede dove risponde e come la
stazione vi accede, scritti una volta sola; ogni **dispositivo** sotto di esso possiede solo
il modo in cui pubblica, **MQTT semplice** o **Sparkplug B**, e i suoi tag. I due protocolli
convivono sotto lo stesso broker. I valori arrivano per abbonamento, non per interrogazione,
quindi nessun dispositivo o tag MQTT ha un intervallo di interrogazione.

## La pagina del driver {#the-drivers-page}

La pagina del driver MQTT elenca broker invece di dispositivi. La frase sotto la sua
intestazione dice « Ogni broker è una connessione a un broker MQTT esterno. Configurare dove risponde e come accede la stazione, poi aggiungere i dispositivi che pubblicano attraverso di esso. »
La tabella **Broker MQTT** porta **Aggiungi broker** a destra del titolo e una riga per
broker: **Nome**, **Indirizzo** (`mqtt://host:port`, oppure `mqtts://host:port` con TLS, vuoto
finché il broker non ha un host), **Dispositivi**, **Stato** (un punto e lo stato del broker
con quello che fanno i suoi dispositivi, come « Connesso · 2/2 dispositivi connessi »), poi
**Apri** e, per Configurare Connector, **Abilita**, **Disabilita** ed **Elimina**. La tabella
vuota dice « Nessun broker. » Un broker che ha rifiutato la stazione porta il segno rosso dell'albero dopo il nome, e la pagina nomina sotto l'intestazione i broker con errore (« 1 broker MQTT con errore: Plant »).

Nell'albero la riga del driver conta i due livelli (« 1 broker MQTT · 2 dispositivi »), e
l'intestazione della pagina mostra il numero di broker tra parentesi. Nel menu Nuovo… della
colonna la voce MQTT offre **Nuovo broker**, poi ogni broker con il suo **Nuovo dispositivo** e i
suoi dispositivi, e il clic destro sulla riga del driver offre **Aggiungi broker**. La barra del
driver non ha Rileva: una scansione ascolta un solo broker, quindi sta nella pagina del broker.
Una volta aggiunto, il driver sta sotto la cartella Driver dell'albero, con o senza broker.

## Il broker {#the-broker}

**Aggiungi broker**, **Nuovo broker** nel menu Nuovo… e il clic destro sulla riga del driver creano un
broker chiamato « Nuovo broker » (poi « Nuovo broker 2 ») e disabilitato, così che la sua
connessione venga compilata prima che la stazione si colleghi. Il registro delle azioni lo dice:
« Broker 'Nuovo broker' aggiunto come disabilitato. Indicare dove risponde, poi abilitarlo. »
Il broker non viene aperto: è evidenziato nella tabella Broker MQTT della pagina del driver, dove la schermata va da Nuovo… o dal clic destro. La sua pagina, raggiunta con **Apri** sulla sua riga o con la sua riga nell'albero, porta la frase « Un broker MQTT: dove risponde e come la stazione vi accede. Ogni dispositivo sotto di esso condivide questa connessione. »
e, nell'intestazione, lo stato, il suo punto e l'indirizzo. Sotto l'intestazione sono nominati i dispositivi del broker che hanno un errore proprio (« 1 dispositivo con errore: Pump »); finché il broker stesso ha un errore, i suoi dispositivi rimandano a lui: la pagina di un dispositivo dice « Questo dispositivo passa per il broker MQTT Plant, che ha un errore. La pagina del broker dice perché. »

### La scheda Connessione al broker {#the-broker-connection-card}

| Campo | Significato | Predefinito |
| --- | --- | --- |
| **Nome** | Il nome descrittivo del broker nell'albero e su ogni dispositivo sotto di esso. Non entra mai in un indirizzo. Vuoto viene rifiutato: « Il nome del broker è obbligatorio. » | « Nuovo broker » |
| **Indirizzo host** | Il nome host o l'indirizzo IP del broker. Si può incollare un indirizzo intero: `mqtt://` o `tcp://` davanti mettono il trasporto in chiaro, `mqtts://`, `ssl://` o `tls://` con TLS, una porta dopo l'host va in **Porta**, e l'host viene conservato nudo. Un altro schema, un percorso o un accesso scritti nell'indirizzo vengono rifiutati sotto la casella, e ogni campo conserva quello che aveva: « 'http://192.168.0.20' non è un indirizzo di broker. Scrivere il nome host o l'indirizzo IP del broker, eventualmente come mqtt://host:port o mqtts://host:port. » | vuoto |
| **Porta** | La porta TCP su cui risponde il broker, da 1 a 65535. | 1883 |
| **Trasporto di rete** | La casella **Usa TLS**. Spuntata, la connessione è cifrata e il certificato del broker viene verificato nell'archivio certificati di Windows o tra i certificati approvati in [Accesso delle apparecchiature](#equipment-access): « Cifrato: Windows deve considerare attendibile il certificato del broker, oppure il certificato deve essere approvato in Accesso delle apparecchiature, nella pagina del driver MQTT. » Non spuntata, la nota avverte « Non cifrato: i valori e la password viaggiano in chiaro. » Cambiarla sposta una porta lasciata a 1883 su 8883, e viceversa; qualsiasi altra porta resta com'è. | non spuntata |
| **Nome utente**, **Password di accesso** | L'account con cui la stazione accede al broker. Un nome utente vuoto accede in modo anonimo. La password è protetta a riposo come le credenziali di un dispositivo e non compare mai in un indirizzo, nel registro o nella risposta di un agente. | vuoto |
| **ID client** | Il nome che porta la sessione della stazione sul broker, da 1 a 128 caratteri senza caratteri di controllo. Un broker tiene una sessione per ID client e fa cadere la più vecchia di due, quindi un ID client appartiene a un solo broker di questa stazione: « L'ID client 'GanterLab01' appartiene già al broker 'Plant'. Due sessioni con lo stesso ID client si escludono a vicenda dal broker: scegline un altro. » Quando due stazioni condividono un broker, ognuna tiene il proprio. | `GanterLab` e 14 cifre esadecimali, generato |
| **Intervallo keep-alive (s)** | Ogni quanto la sessione dimostra di essere viva quando non passa nient'altro: « Il keep-alive deve essere compreso tra 5 e 3600 secondi. » | 60 |

Sotto i campi, una frase dice che cosa stanno facendo il broker e i suoi dispositivi, che cosa
manca ancora al broker, e « Tutti i dispositivi sotto questo broker e ciò che la stazione vi pubblica condividono questa connessione: la sessione si apre quando il primo dispositivo abilitato o la prima riga pubblicata ne ha bisogno. »
Tutti i campi restano modificabili con il broker abilitato. Una modifica salvata di dove
risponde, del trasporto, dell'accesso, dell'ID client o del keep-alive ricollega l'unica
sessione che condividono tutti i dispositivi del broker.

### La barra dei comandi {#the-command-bar}

| Comando | Che cosa fa | Grigio quando (situazione) | Non disegnato quando (ruolo) |
| --- | --- | --- | --- |
| **Abilita** | Lascia che i dispositivi del broker usino la sua sessione. Un broker senza host viene rifiutato: « Inserire il nome host o l'indirizzo IP del broker prima che venga abilitato. » | È già abilitato, la connessione non è completa (« La connessione di questo broker non è ancora completa. »), è in corso una scansione, oppure la stazione rifiuta le modifiche. | Senza Configurare Connector. |
| **Disabilita** | Chiude la sessione per ogni dispositivo del broker; ogni dispositivo conserva la propria scelta Abilitato e torna com'era quando il broker viene riabilitato. | È già disabilitato, è in corso una scansione, oppure la stazione rifiuta le modifiche. | Senza Configurare Connector. |
| **Elimina broker** (inchiostro di pericolo, a destra) | Chiede prima. Un broker vuoto: « Questo broker non ha dispositivi. »; uno con dispositivi: « Questo elimina anche i 2 dispositivi del broker e tutti i loro tag. » Poi viene selezionato il driver MQTT e il registro delle azioni dice « Broker 'Plant' e i suoi 2 dispositivi eliminati. » | È in corso una scansione (« Una scansione di rilevamento sta usando questo broker. Attenderne la fine. »), oppure la stazione rifiuta le modifiche. | Senza Configurare Connector. |

Il clic destro sulla riga di un broker offre **Aggiungi dispositivo**, **Abilita**,
**Disabilita** ed **Elimina**.

### Stati del broker {#broker-states}

Il broker riferisce la sua sessione separatamente dai suoi dispositivi, nella riga
dell'albero, nella tabella dei broker del driver e nella propria intestazione:

| Stato | Punto | Significato |
| --- | --- | --- |
| **Spento** | grigio | Il broker è disabilitato. |
| **Nessuna sessione** | grigio | Abilitato, ma nessun dispositivo abilitato lo usa e la sua scheda Pubblicare non pubblica nulla, quindi non si apre nessuna sessione. |
| **Connessione…** | ambra | Un dispositivo è abilitato, o il broker pubblica, e la sessione si sta collegando, o ricollegando dopo una perdita. |
| **Connesso** | verde | La sessione è collegata. |
| **Non raggiungibile** | rosso | Non è stato possibile raggiungere il broker, oppure ha rifiutato la stazione. La causa è scritta in rosso sotto l'intestazione nella lingua della stazione e, con le parole del driver, come suggerimento del segno di errore della riga, nei termini di che cosa correggere: niente risponde su quella porta, il nome non è noto su questa rete, il nome utente o la password sono stati rifiutati, il certificato non è attendibile o attende l'approvazione in [Accesso delle apparecchiature](#equipment-access), un altro client ha preso la sessione con lo stesso ID client. Un broker che rifiuta la porta o un client senza nome utente è detto con la sua impostazione (vedere [Preparare il broker](#prepare-the-broker)). |

La riga dell'albero mostra lo stato e il numero di dispositivi (« Connesso · 2 dispositivi »),
e il riepilogo sotto la scheda aggiunge che cosa fanno i dispositivi:
« Connesso · 2/2 dispositivi connessi », « Connesso · 1 connessi · 1 con problemi »,
« Nessuna sessione · nessun dispositivo abilitato ». Un dispositivo Sparkplug B che ha
annunciato la propria morte toglie dall'aria solo sé stesso: il broker resta Connesso. La
sessione parla MQTT 5 e ripiega su MQTT 3.1.1 con un broker che non lo parla, parte pulita e
si ricollega da sola, aspettando sempre di più tra un tentativo e l'altro finché il broker
resta assente.

### La scheda Rileva i dispositivi {#the-discover-devices-card}

Disegnata solo per Configurare Connector, sotto la frase « Ascolta su questo broker per un po': i nodi edge e i dispositivi Sparkplug B che si annunciano, e i topic sotto un filtro. »

| Campo | Significato | Predefinito |
| --- | --- | --- |
| **Ascolto (s)** | Per quanto tempo ascolta la scansione, da 3 a 60 secondi. | 10 |
| **Filtro dei topic** | I topic semplici da elencare, come tag di un dispositivo. Sono ammessi i caratteri jolly `+` (un livello) e `#` (questo livello e tutto quello che sta sotto, solo in fondo), da cui « es. plant/# ». Vuoto elenca solo dispositivi Sparkplug B. | vuoto |

**Rileva** avvia la scansione e diventa **Annulla** mentre è in corso. La frase sotto i campi
dice che cosa farà (« Ascolta 192.168.0.20:1883 per 10 s. »), e la frase di avanzamento conta
la finestra (« Ascolto di 192.168.0.20:1883 · 4 di 10 s… »). La scansione ha bisogno di un host
salvato sul broker: fino ad allora la scheda dice « Inserire l'host del broker e lasciare che la modifica venga salvata prima del rilevamento. »
Una modifica in sospeso sul broker viene salvata prima; quando non si può, non parte niente:
« Il rilevamento non è partito perché non è stato possibile salvare la modifica del broker. »

Che cosa fa una scansione sul broker:

- Si collega con un ID client proprio, quindi non fa mai cadere dal broker la sessione dei
  dispositivi, e può girare con il broker abilitato e in uso.
- Si abbona a tutti i topic Sparkplug B (`spBv1.0/#`) e al filtro dei topic. I certificati
  di nascita Sparkplug B non vengono conservati, perciò a un nodo edge sentito senza
  certificato viene chiesto di annunciarsi di nuovo (il comando `Node Control/Rebirth`,
  l'unico messaggio che la scansione pubblica); un nodo edge in funzione risponde dentro la
  finestra.
- Ogni nodo edge e ogni dispositivo dietro di esso che si annuncia (NBIRTH, DBIRTH) diventa
  un candidato con le sue metriche come tag, tipizzate secondo quello che dichiara la
  nascita. Le tubature proprie del nodo edge (`bdSeq` e le metriche `Node Control/…` e
  `Device Control/…`) restano fuori, come le metriche che nessun tag legge (array, set di
  dati, template, insiemi di proprietà, byte, file), contate per motivo. Un nodo edge sentito
  ma non nato dentro la finestra viene offerto senza tag.
- Con un filtro dei topic, i topic semplici sentiti sotto di esso diventano un candidato a
  sé: ogni topic è un tag oppure, per un payload JSON, ogni valore che contiene lo è tramite
  il suo percorso del valore (fino a 32 per topic, quattro livelli di profondità, i primi 8
  elementi di un array). Un numero viene offerto come Double, `true` o `false` come Boolean,
  tutto il resto come String. L'elenco si ferma a 500 topic. Un topic il cui editore conserva
  l'ultimo messaggio si sente subito; qualsiasi altro solo se pubblica durante la finestra.
- Non si salva niente finché non si aggiunge un candidato. Annullare chiude subito la
  finestra.

La scheda dei risultati, **Dispositivi rilevati**, elenca **Pubblica come** (`group/edge node`,
con `/device` dietro un nodo edge, oppure « Topic semplici »), **Dispositivo** (il nome con cui
aggiungerlo, modificabile: l'ID del dispositivo o del nodo edge, oppure i livelli del filtro
prima del suo primo carattere jolly), **Risultato** (« 12 di 12 tag selezionati ») e
**Aggiungi dispositivo**, che crea il dispositivo sotto questo broker con quei tag.
Un'identità Sparkplug B già configurata sul broker mostra « Già configurato » e offre
**Apri** al suo posto. Una scansione che non ha sentito niente dice
« Niente si è annunciato su questo broker mentre la scansione ascoltava. »

Un dispositivo che c'è già trova dalla sua stessa pagina quello che non legge ancora: **Rileva tag**, nell'intestazione della sua scheda dei tag, ascolta 10 s sul suo broker. Un dispositivo Sparkplug B sente solo il proprio edge node, a cui si chiede la nascita mentre a nessun altro si chiede nulla; un dispositivo semplice sente i topic sotto i livelli che i suoi tag hanno in comune (`plant/oven/#` per `plant/oven/temperature` e `plant/oven/count`), e ogni topic del broker quando non ne hanno nessuno in comune. Si offrono solo i valori per cui non ha ancora un tag (vedere [Rilevare i tag di un dispositivo](connector-discovery#discovering-a-devices-tags)).

### La scheda Dispositivi {#the-devices-card}

« Ogni dispositivo legge topic semplici, oppure un nodo edge o un dispositivo Sparkplug B. La connessione è ereditata dal broker e non può essere cambiata qui. »
**Aggiungi dispositivo** crea « Nuovo dispositivo » sotto questo broker, abilitato, in MQTT
semplice, evidenziato in questa tabella e portato in vista: « Dispositivo 'Nuovo dispositivo' aggiunto su 'Plant'. » La tabella
elenca **Dispositivo**, **Pubblica come**, **Tag** e **Stato di salute**, con Apri, Abilita,
Disabilita ed Elimina su ogni riga; vuota, dice « Nessun dispositivo configurato su questo broker. » Un dispositivo con un errore proprio porta il segno rosso dell'albero dopo il nome.

### La scheda Pubblicare {#the-publish-card}

L'ultima scheda pubblica i dati della stazione stessa su questo broker, sotto la frase « La stazione pubblica ogni riga su questo broker come JSON sul suo topic ogni volta che il valore o la sua qualità cambia, e di nuovo tutte le righe ogni volta che la connessione si apre. Il broker conserva l'ultimo messaggio di ogni topic per chi si iscrive più tardi. La stazione si iscrive solo al topic di scrittura di ogni riga in Lettura e scrittura. »
Chi consulta il Connector legge la scheda; chi lo configura la modifica. Il broker Questa stazione
del [broker MQTT](connector-mqtt-broker) della stazione stessa ha la stessa scheda.

| Campo | Significato | Predefinito |
| --- | --- | --- |
| **Formato di pubblicazione** | Come escono le righe: **JSON** sul topic di ogni riga ([Pubblicazione](#publishing)), oppure **Sparkplug B**, la stazione come nodo edge del broker ([Pubblicazione in Sparkplug B](#publishing-in-sparkplug-b)). Il suo suggerimento: « Come escono le righe: JSON sul topic di ogni riga, oppure Sparkplug B, con questa stazione come nodo edge che Ignition e gli altri consumatori Sparkplug B trovano da soli. » | JSON |
| **Prefisso dei topic** | Solo in JSON. Il livello sotto cui si propone il topic di ogni nuova riga. Vuoto, il campo mostra il predefinito e lo usa. « Il prefisso dei topic non può contenere + o #, iniziare con $ né contenere un carattere che nessun topic può contenere. » Cambiarlo lascia invariati i topic già nell'elenco. | `ganterlab/` e il nome della stazione |
| **ID gruppo** | Solo in Sparkplug B, al posto del prefisso. Il gruppo Sparkplug B del nodo edge della stazione su questo broker; vuoto, è `GanterLab`. Un gruppo con `/`, `+` o `#` viene rifiutato sotto il campo, e il broker non viene salvato finché non lo si corregge. | `GanterLab` |

In Sparkplug B la frase sotto il titolo dice invece « La stazione è un nodo edge Sparkplug B su questo broker: le variabili e gli allarmi del Logic sono le sue metriche, e ogni dispositivo con righe è un dispositivo Sparkplug B. Annuncia tutte le metriche quando la connessione si apre e ogni volta che l'elenco cambia, poi pubblica ogni valore o qualità che cambia. Solo una riga in Lettura e scrittura accetta un valore da un comando. », e sotto i campi **ID nodo edge (il nome della stazione)** mostra il nodo edge con cui pubblica.

La tabella elenca **Sorgente**, **Topic MQTT** e **Accesso**, per topic; una riga la cui sorgente
non è ancora scelta dice « Ancora nessuna sorgente ». Senza righe, dice « Ancora nulla è pubblicato su questo broker. Aggiungere una riga con Nuova riga. »
In Sparkplug B la colonna **Topic MQTT** è **Metrica**: un tag dopo il suo dispositivo (`Oven · Temperature`), una variabile o un allarme con il suo nome sul nodo edge (`Logic/Oven/Setpoint`, `Alarms/High`).
Con il broker disabilitato e righe nell'elenco, una nota dice « Il broker è disabilitato, quindi non viene pubblicato nulla finché non è abilitato. »
Con una riga del broker in Lettura e scrittura, una seconda nota dice « Le righe in Lettura e scrittura accettano i valori pubblicati sul loro topic di scrittura. Chiunque possa pubblicare su questo broker può cambiare quelle sorgenti, quindi il nome utente e la password del broker sono la loro unica protezione. » In Sparkplug B dice « Le righe in Lettura e scrittura accettano i valori dei comandi Sparkplug B (NCMD, DCMD). Chiunque possa pubblicare su questo broker può cambiare quelle sorgenti, quindi il nome utente e la password del broker sono la loro unica protezione. »

| Comando | Che cosa fa | Grigio quando (situazione) | Non disegnato quando (ruolo) |
| --- | --- | --- | --- |
| **Nuova riga** | Aggiunge una riga che non pubblica ancora nulla e la seleziona: « Riga aggiunta all'elenco Pubblicare del broker. Sceglierne la sorgente. » | Una prova tiene la stazione, oppure il runtime è fermo. | Senza Configurare Connector. |
| **Elimina riga** (inchiostro di pericolo) | Elimina subito la riga selezionata; il suo suggerimento nomina ciò che pubblica. La stazione smette di pubblicarla; il messaggio che il broker ha conservato sul suo topic resta lì. | Nessuna riga è selezionata (« Selezionare una riga dell'elenco per eliminarla. »), una prova tiene la stazione, oppure il runtime è fermo. | Senza Configurare Connector. |

I campi di una riga si salvano nel momento in cui cambiano:

| Campo | Che cosa fa |
| --- | --- |
| **Sorgente** | Ciò che la riga pubblica: « (nessuno) », poi ogni tag abilitato di un dispositivo abilitato (« Tag di Connector · Simulated · Oven · Temperature »), ogni variabile di Logic che contiene un valore e ogni allarme, nominati con le loro cartelle. Un comando del dispositivo non pubblica nulla e non viene offerto. La riga tiene la sua sorgente per identità, quindi una ridenominazione la lascia su di essa, e la scheda [Usato da](connector-tags#the-used-by-card) della sorgente elenca la riga. |
| **Topic MQTT** | Solo in JSON. Dove la riga pubblica. Scegliere una sorgente propone un topic dal suo nome: `<prefix>/<device>/<tag>` per un tag, `<prefix>/logic/<folders>/<variable>` per una variabile, `<prefix>/alarms/<folders>/<alarm>` per un allarme, ogni nome un livello di topic (uno spazio, una barra o un carattere jolly diventa `_`). Lo si modifica liberamente: un topic che è ancora quello proposto segue una sorgente scelta dopo, quello scritto a mano resta, e rinominare la sorgente lo mantiene. |
| **Accesso** | **Sola lettura**, il predefinito di ogni riga: la stazione pubblica la riga e non si iscrive a nulla per essa. **Lettura e scrittura**: la stazione accetta anche i valori pubblicati sul topic di scrittura della riga, il suo topic seguito da `/set`, e li scrive nella sorgente ([Scrittura di ritorno](#writing-back)). È offerto solo per un tag con accesso in scrittura su un driver che scrive, o una variabile di memoria; altrove resta nell'elenco, in grigio: « Questa sorgente non accetta scritture: le accettano solo un tag con accesso in scrittura o una variabile di memoria. » Scegliere un allarme riporta la riga a sola lettura. In Sparkplug B, Lettura e scrittura accetta, al posto del topic di scrittura, i valori che un comando Sparkplug B invia alla metrica della riga. |

Sotto i campi una frase dice che cosa pubblica la riga:

| La scheda dice | Quando |
| --- | --- |
| « Questa riga non pubblica ancora nulla: scegliere la sua sorgente. » | Nessuna sorgente è scelta. |
| « Pubblica il valore, la sua qualità e il suo orario come JSON ogni volta che il valore o la qualità cambia. » | Un tag o una variabile, di sola lettura. |
| « Pubblica il valore, la sua qualità e il suo orario come JSON ogni volta che il valore o la qualità cambia, e scrive nella sorgente ogni valore pubblicato sul topic di scrittura: ogni scrittura è registrata nel diario tecnico con il nome del broker. » | Un tag o una variabile in Lettura e scrittura. |
| « Pubblica come JSON se l'allarme è attivo e tacitato, il suo stato, la priorità, il messaggio e l'orario ogni volta che uno di essi cambia. » | Un allarme. |
| « La sorgente di questa riga non esiste più, quindi non viene più pubblicato nulla per essa. Il broker mantiene l'ultimo messaggio che ha conservato. » | Il tag, la variabile o l'allarme è stato eliminato. |
| « Pubblica il valore e la sua qualità in Sparkplug B ogni volta che il valore o la qualità cambia. » | Sparkplug B: un tag o una variabile, di sola lettura. |
| « Pubblica il valore e la sua qualità in Sparkplug B ogni volta che il valore o la qualità cambia, e scrive nella sorgente ogni valore che un comando Sparkplug B invia alla sua metrica: ogni scrittura è registrata nel diario tecnico con il nome del broker. » | Sparkplug B: un tag o una variabile in Lettura e scrittura. |
| « Pubblica in Sparkplug B se l'allarme è attivo e tacitato, il suo stato, la priorità e il messaggio ogni volta che uno di essi cambia. » | Sparkplug B: un allarme. |
| « La sorgente di questa riga non esiste più, quindi il nodo edge si annuncia di nuovo senza di essa. » | Sparkplug B: il tag, la variabile o l'allarme è stato eliminato. |

Sotto quella frase, una riga in Lettura e scrittura mostra il suo **Topic di scrittura**, il topic della riga seguito da `/set`.
In Sparkplug B non c'è un topic di scrittura: una seconda frase dice la metrica con cui la riga viene pubblicata, del suo dispositivo Sparkplug B per un tag, del nodo edge della stazione per una variabile e, per un allarme, le cinque metriche sotto il suo nome (Active, Acked, State, Priority e Message).

Una modifica che l'elenco rifiuta non viene salvata: il motivo è scritto in rosso sopra i campi
della riga, e la modifica resta sullo schermo perché la si possa correggere.

| La scheda dice | Quando |
| --- | --- |
| « Questa sorgente è già pubblicata su questo broker: modificare la riga che la pubblica. » | Un'altra riga di questo broker pubblica la sorgente. |
| « Un'altra riga di questo broker pubblica già su questo topic: dare a questa riga un topic proprio. » | Due righe di un broker pubblicherebbero su uno stesso topic. |
| « Non si può pubblicare nulla su un filtro: il topic non può contenere + o #. » | Il topic contiene un carattere jolly. |
| « Un topic che inizia con $ appartiene al broker: scriverne un altro. » | Il topic inizia con `$`. |
| « Questo topic non può essere trasmesso: contiene un carattere che nessun topic può contenere, oppure è troppo lungo. » | Un carattere di controllo, o più di quanto porta un topic MQTT. |
| « Una riga che accetta scritture ha bisogno del tag o della variabile a cui vanno. » | Lettura e scrittura è stato scelto prima di una sorgente. |
| « La sorgente non si può scrivere: scegliere un tag con accesso in scrittura o una variabile di memoria, oppure lasciare la riga di sola lettura. » | Lettura e scrittura su un allarme, una variabile di espressione, un tag di sola lettura o un tag il cui driver non scrive nulla. |
| « Questo topic e il topic di un'altra riga di questo broker differiscono solo per /set, dove una riga accetta scritture: dare a questa riga un altro topic. » | Il topic di una riga è il topic di un'altra seguito da `/set`. |
| « Questa riga dell'elenco non esiste più. Ricaricare e riprovare. » | La riga è stata eliminata altrove mentre la si modificava. |

Che cosa viene pubblicato, e quando, si trova in [Pubblicazione](#publishing).

## Dispositivo {#device}

| Campo | Significato | Predefinito |
| --- | --- | --- |
| **Broker · fisso** | Il nome del broker sopra il suo indirizzo, in sola lettura: un dispositivo viene creato sotto il suo broker e ci resta. « Il broker non ha ancora un host » tiene il posto dell'indirizzo. « Broker mancante » significa che il dispositivo ha perso il suo broker e non può essere salvato: « È obbligatorio un broker MQTT. » | il broker sotto cui viene aggiunto |
| **Protocollo** | Come pubblica il dispositivo. **MQTT semplice**: « Ogni tag nomina il topic che legge e, per un payload JSON, il percorso del suo valore. » **Sparkplug B**: un nodo edge, o un dispositivo dietro di esso: « Ogni tag nomina una metrica come la dichiara il certificato di nascita del nodo edge; il tipo arriva con la metrica. » Cambiarlo ricontrolla ogni tag, perché un indirizzo si legge allora come topic o come metrica. | MQTT semplice |
| **Tempo massimo senza messaggi (s)** | Solo MQTT semplice, da 0 a 86400, dove 0 vuol dire mai: « I tag smettono di valere come letture quando sul loro topic non arriva alcun messaggio per questo tempo. 0: mai, per un topic pubblicato solo quando il valore cambia. Impostarlo per un dispositivo che pubblica a ritmo costante, come un sensore che invia ogni pochi secondi. » Fuori da questo intervallo il dispositivo non viene salvato: « Il tempo massimo senza messaggi deve essere compreso tra 0 e 86400 s. » Un dispositivo Sparkplug B non ne ha: il suo certificato di morte dice già quando è sparito. | 0 |
| **ID gruppo** | Solo Sparkplug B: il gruppo sotto cui pubblica il nodo edge, il livello dopo `spBv1.0` nei suoi topic. « Un dispositivo Sparkplug B ha bisogno del suo ID gruppo. » | vuoto |
| **ID nodo edge** | Solo Sparkplug B: il nodo edge che pubblica le metriche, come lo nomina il suo NBIRTH. « Un dispositivo Sparkplug B ha bisogno del suo ID nodo edge. » | vuoto |
| **ID dispositivo** | Solo Sparkplug B: il dispositivo dietro il nodo edge, come lo nomina il suo DBIRTH. Vuoto, mostrato come « Il nodo edge stesso », legge le metriche proprie del nodo edge. | vuoto |

Il dispositivo non ha host, porta, credenziali né intervallo di interrogazione propri: tutto
questo è del broker. Ogni ID Sparkplug B è un solo livello di topic: « Un ID Sparkplug B non può contenere '/', '+' o '#'. »
Un broker nomina un nodo edge o un dispositivo Sparkplug B una volta sola, quindi un secondo
dispositivo con gli stessi ID di gruppo, nodo edge e dispositivo su quel broker viene
rifiutato: « Il dispositivo Sparkplug B Plant/Line1/Press1 sul broker 'Plant' appartiene già al dispositivo 'Press'. »
I dispositivi in MQTT semplice non nominano niente di proprio sul broker, e due di loro
possono leggere gli stessi topic.

La Base URI che mostra l'intestazione è l'indirizzo di runtime:
`mqtt://<broker id>?host=…&port=…&tls=0&keepAlive=60&clientId=…&mode=plain`, con `&silence=…` quando il dispositivo imposta un tempo massimo senza messaggi, e per un
dispositivo Sparkplug B `mode=sparkplug&group=…&node=…`, con `&device=…` dietro un nodo edge.
Nomina il broker con la sua identità stabile, mai con il nome descrittivo, e non porta mai il
nome utente né la password.

La copia di un dispositivo MQTT resta sullo stesso broker. Una copia Sparkplug B prende la
prima identità che nessun dispositivo di quel broker ha: il suo ID più interno, l'ID del
dispositivo o, per le metriche proprie del nodo edge, l'ID del nodo edge, viene numerato come
il nome della copia (« Press1 (2) »).

## Indirizzamento del tag {#tag-addressing}

Il campo dell'indirizzo è **Topic MQTT** su un dispositivo in MQTT semplice e **Metrica** su
uno Sparkplug B, ed è obbligatorio (« Topic MQTT è obbligatorio per questo driver. »). Un tag
semplice ha altri tre campi; una metrica Sparkplug B non ne ha nessuno, perché porta il proprio
tipo e si scrive con il comando proprio del protocollo.

| Campo | Significato | Predefinito |
| --- | --- | --- |
| **Topic MQTT** | L'unico topic che legge il tag, es. `plant/line1/temperature`. « Un tag legge un solo topic, quindi il suo topic non può contenere i caratteri jolly + o #. » | obbligatorio |
| **Percorso del valore** | Dove sta il valore dentro un payload JSON: membri separati da punti e indici di array tra parentesi quadre, es. `sensors[0].value`. Un membro il cui nome contiene un punto o una parentesi quadra si scrive tra virgolette dentro parentesi quadre (`['flow.rate']`), e un `$.` iniziale è accettato. Vuoto, mostrato come « Payload intero », legge tutto il payload come valore. Un percorso malformato: « Scrivere il percorso del valore come membri separati da punti e indici di array tra parentesi quadre, es. sensors[0].value. » | vuoto |
| **Topic di comando** | Dove viene pubblicata una scrittura. Vuoto lascia il tag in sola lettura: un tag il cui Accesso scrive ne ha bisogno (« Un tag scrivibile ha bisogno del topic su cui vengono pubblicate le sue scritture. »), e non contiene caratteri jolly (« Non si può pubblicare nulla su un filtro: il topic di comando non può contenere + o #. »). Un'apparecchiatura ascolta di rado sul topic su cui riferisce, ed è per questo che è un campo a parte. | vuoto |
| **Modello del payload** | Quello che pubblica una scrittura: JSON con `{value}` una volta, fuori dalle virgolette, dove va il valore scritto, es. `{"setpoint": {value}}`. Vuoto, mostrato come « Il valore come testo », pubblica il valore come testo. « Il modello del payload contiene {value} esattamente una volta, dove va il valore scritto. » / « Scrivere il modello del payload in JSON, con {value} fuori dalle virgolette dove va un valore JSON. » | vuoto |
| **Metrica** | Sparkplug B: il nome della metrica come lo dichiara il certificato di nascita, es. `Inputs/Temperature`. | obbligatorio |

I tipi di dato sono Boolean, Int16, Int32, Int64, Float, Double, String e DateTime. L'anteprima
in sola lettura dell'indirizzo sul filo è il topic seguito da quello che si applica di `path`,
`command` e `payload`
(`plant/line1/temperature?path=sensors[0].value&command=plant/line1/setpoint`), oppure il nome
della metrica; un carattere che chiuderebbe una parte (`%`, `?`, `&`, `=`, `#`, `+`, uno
spazio) si scrive come `%XX`.

## Lettura {#reading}

La sessione tiene l'ultimo messaggio di ogni topic a cui si è abbonata, e un tag legge da lì:

| Situazione | Tag in MQTT semplice | Tag Sparkplug B |
| --- | --- | --- |
| Il valore | L'ultimo messaggio sul topic: tutto il payload come testo, oppure il valore JSON al percorso del valore, convertito nel tipo del tag. | Il valore della metrica nell'ultimo messaggio di nascita o di dati del nodo edge. |
| La sua marca temporale | Quando è arrivato il messaggio. Un messaggio che l'editore ha conservato dà il primo valore appena la stazione si abbona. | La marca temporale propria della metrica. |
| Ancora niente ricevuto | Il tag aspetta: niente valore e niente errore, come con qualsiasi driver prima della prima risposta. Un topic il cui editore non conserva resta senza valore finché non pubblica di nuovo. Con un **Tempo massimo senza messaggi (s)**, l'attesa dura quel tempo da quando la stazione ha cominciato ad ascoltare, poi il tag fallisce la lettura. | Lo stesso finché non arriva il certificato di nascita; la stazione chiede al nodo edge di annunciarsi di nuovo. |
| L'editore tace, il broker resta raggiungibile | Con un **Tempo massimo senza messaggi (s)**, un tag il cui topic non riceve messaggi da più tempo fallisce la lettura, e la regola del tag per [un dispositivo che non ha risposto](connector-value-pipeline#the-device-did-not-answer) decide che cosa mostra; il messaggio successivo lo riporta. Il tempo conta dall'arrivo dell'ultimo messaggio, anche se conservato. A 0 il tag tiene l'ultimo valore ricevuto. | Non si applica: lo dice il certificato di morte. |
| Un messaggio che il tag non sa leggere | Una lettura fallita di quel tag, con il motivo: un messaggio vuoto, un payload che non è JSON sotto un percorso del valore, niente al percorso, oppure un valore che il tipo non può contenere. | Una lettura fallita di quel tag: una metrica che il certificato di nascita non dichiara, oppure un valore che il suo tipo non può contenere. |
| Il broker non raggiungibile | Tutti i dispositivi del broker risultano scollegati, e i loro tag falliscono come quelli di qualsiasi dispositivo. | Lo stesso. |
| Un certificato di morte (NDEATH, DDEATH) | Non si applica. | Il nodo edge e i suoi dispositivi, o quel solo dispositivo, restano senza valore finché non nascono di nuovo; tutti gli altri dispositivi del broker continuano. |

Un tag Boolean accetta `true` o `false`, `1` o `0`, `on` o `off`. Un tag intero accetta un
numero frazionario arrotondato e rifiuta uno fuori dal suo intervallo. Un tag DateTime accetta
testo ISO 8601 o millisecondi Unix. Un tag String accetta il testo così com'è, e un oggetto o
un array JSON come il suo JSON grezzo. Una metrica Sparkplug B di un tipo senza segno si legge
nel tipo con segno immediatamente più largo (UInt8 come Int16, UInt16 come Int32, UInt32 e
UInt64 come Int64), così che tutto il suo intervallo venga letto.

## Scrittura {#writing}

Una scrittura passa per lo stesso imbuto di quella di qualsiasi altro driver: la casella di
scrittura riceve il valore ingegneristico, e le fasi di conversione vengono invertite prima che
il valore grezzo venga pubblicato (vedere [Connector](connector)).

| Tag | Che cosa viene pubblicato | QoS | Che cosa riferisce la scrittura |
| --- | --- | --- | --- |
| MQTT semplice | Il valore sul **Topic di comando** del tag, come testo (`21.5`, `true`, il testo stesso, una data in ISO 8601), oppure messo nel **Modello del payload** come valore JSON (un numero come numero, un booleano come `true` o `false`, un testo tra virgolette e con escape). | 1, non conservato | Fatta quando il broker la conferma; rifiutata, con il motivo del broker, quando un broker MQTT 5 la respinge; nessuna risposta quando nessuna conferma arriva in tempo. |
| Sparkplug B | Un NCMD (una metrica del nodo edge) o un DCMD (una metrica di un dispositivo dietro di esso) che porta la metrica, sotto l'alias che le ha dato la nascita e con il tipo che la nascita ha dichiarato. | 0, non conservato, come la specifica Sparkplug B richiede per i comandi | Fatta una volta inviata. Il nodo edge risponde pubblicando il nuovo valore, che è quello che il tag legge subito dopo. |

Finché il broker non è raggiungibile, o il nodo edge o il dispositivo ha annunciato la propria
morte, una scrittura viene rifiutata come scollegata e non si invia niente. Una scrittura
Sparkplug B su una metrica che il certificato di nascita non dichiara viene rifiutata, perché
il nodo edge non la accetterebbe, e così un valore che il tipo della metrica non può portare.
Una scrittura non viene mai conservata: un setpoint conservato verrebbe applicato di nuovo da
ogni abbonato che si colleghi in seguito.

## Pubblicazione {#publishing}

Questa sezione descrive il **Formato di pubblicazione** su **JSON**; lo Sparkplug B ha la sezione seguente.

Ogni riga della [scheda Pubblicare](#the-publish-card) di un broker viene pubblicata sul suo topic,
conservata, con QoS 1, attraverso la connessione che usano i dispositivi del broker: la stazione
non apre mai una seconda connessione verso un broker. Un broker usato solo per pubblicare non ha
bisogno di dispositivi. Resta sotto il driver MQTT, e il suo stato (Connessione…, Connesso, Non
raggiungibile) è quello di questa connessione. Pubblica finché è abilitato e l'elenco ha righe;
non ha un interruttore proprio.

| Sorgente | Messaggio (JSON) |
| --- | --- |
| Tag | `{"value": 21.5, "quality": "good", "timestamp": "2026-10-04T12:30:15.250Z"}`: il valore ingegneristico che il tag mostra; `quality` è `good` per una lettura arrivata, `bad` con `value` nullo per una che non è arrivata, e `uncertain` per un valore sostitutivo, per l'ultimo valore mantenuto e per letture che smettono di arrivare (resta l'ultimo valore pubblicato); la marca temporale della lettura, in UTC. |
| Variabile di Logic | Gli stessi membri: il valore della variabile e `good` finché è buona, `null` e `bad` altrimenti; la marca temporale della sua ultima scrittura o del suo ultimo calcolo. |
| Allarme | `{"active": true, "acked": false, "state": 1, "priority": "High", "message": "Oven above 80 °C", "timestamp": "2026-10-04T12:30:15.250Z"}`: se è attivo e tacitato, `state` come numero (0 normale, 1 attivo e non tacitato, 2 attivo e tacitato, 3 rientrato e non tacitato), la priorità con il suo nome (Critical, High, Medium, Low), il messaggio (vuoto se non c'è) e la marca temporale della sua ultima transizione, come lo serve il [server OPC UA](connector-opcua-served-nodes#where-a-client-finds-each-source). |

Un valore viaggia nel suo tipo JSON: un numero, `true` o `false`, testo, una data e un'ora come
testo ISO 8601; un numero che il JSON non porta (NaN, un infinito) viaggia come `null`. Un tag di
un dispositivo disabilitato pubblica `null` e `bad`.

| Situazione | Che cosa viene pubblicato |
| --- | --- |
| Il valore o la qualità cambia | Quella riga, controllata cinque volte al secondo. Un tag letto di nuovo con lo stesso valore non viene ripubblicato: la sola marca temporale non è un cambiamento. |
| La connessione si apre, o si riapre | Ogni riga, con il suo stato attuale. |
| Il broker non è raggiungibile | Nulla, e nulla aspetta in coda: quando il broker torna, ogni riga viene pubblicata con il suo stato di quel momento. |
| Il broker rifiuta un messaggio, o non lo conferma | Viene ritentato dopo 30 secondi. Il diario tecnico lo annota nell'area Connector, componente `MQTT publication`, una volta e poi in un riepilogo ogni pochi minuti finché dura. |
| Una sorgente viene eliminata | Più nulla; il broker mantiene l'ultimo messaggio che ha conservato. |
| Una riga viene eliminata | Più nulla; il suo messaggio conservato resta sul topic finché non vi si pubblica altro. |

La stazione legge ciò che pubblica e non calcola nulla per farlo: l'ultima lettura di un tag, il
valore di una variabile, lo stato di un allarme. Non si iscrive a nessun topic di una riga di sola lettura, quindi un messaggio che un altro sistema pubblica lì non raggiunge mai la stazione; una riga in Lettura e scrittura accetta valori sul suo topic di scrittura ([Scrittura di ritorno](#writing-back)). Un agente legge gli
elenchi con `mqtt_publish_list` e li modifica con `mqtt_publish_upsert` e `mqtt_publish_delete`
([Strumenti](agent-tools#connector--configure)):

```text
mqtt_publish_upsert(brokerId: "<id from mqtt_broker_list>", source: "Connector.Simulated.Oven.Temperature")
mqtt_publish_upsert(brokerId: "<id from mqtt_broker_list>", source: "Alarms.Oven.HighTemperature", topic: "plant/oven/alarms/high")
mqtt_publish_upsert(id: "<id from mqtt_publish_list>", access: "ReadAndWrite")
mqtt_publish_list()
```

## Pubblicazione in Sparkplug B {#publishing-in-sparkplug-b}

Con **Formato di pubblicazione** su **Sparkplug B**, il broker pubblica lo stesso elenco secondo la specifica
Sparkplug B, con la stazione come nodo edge: un host Sparkplug B, come il MQTT Engine di Ignition, elenca la
stazione sotto il suo gruppo e il suo nodo edge, ogni dispositivo sotto di essa e ogni metrica con il suo
tipo, senza alcuna mappatura costruita a mano. La connessione è quella che usano i dispositivi del broker,
come in JSON.

| Nome | Che cos'è |
| --- | --- |
| Gruppo | **ID gruppo**, `GanterLab` finché è vuoto. |
| Nodo edge | Il nome della stazione, con ogni `/`, `+` e `#` sostituito da `_` (i tre caratteri che un ID Sparkplug B non può contenere). |
| Dispositivo | Ogni dispositivo del Connector con una riga di uno dei suoi tag, nominato allo stesso modo. |
| Metrica di un tag | Il nome del tag, sul suo dispositivo. |
| Metrica di una variabile | `Logic/<folders>/<variable>`, sul nodo edge. |
| Metriche di un allarme | Cinque, sul nodo edge, sotto `Alarms/<folders>/<alarm>/`: `Active` e `Acked` (Boolean), `State` (un numero: 0 normale, 1 attivo e non tacitato, 2 attivo e tacitato, 3 rientrato e non tacitato), `Priority` e `Message` (testo). |

Una `/` dentro un nome diventa `_`, quindi le cartelle sono gli unici livelli. Due dispositivi, o due
metriche, i cui nomi diventano uguali ricevono `_2`, `_3` nell'ordine dei loro nomi. Le righe conservano i
loro topic, su cui in Sparkplug B non viene pubblicato nulla e su cui l'elenco torna a pubblicare quando il
broker torna a JSON.

| Messaggio | Quando |
| --- | --- |
| NDEATH, la morte del nodo edge | Consegnato al broker come ultima volontà della connessione ogni volta che si apre, perché il broker lo pubblichi se la stazione cade. La stazione lo pubblica anche da sé prima di smettere di pubblicare: il broker riportato a JSON, disabilitato o eliminato, l'elenco svuotato, il gruppo o il nome della stazione cambiato, il runtime fermato. |
| NBIRTH, la nascita del nodo edge | Ogni volta che la connessione si apre, quando un host chiede una rinascita (`Node Control/Rebirth`) e quando una variabile o un allarme entra, esce o viene rinominato: ogni metrica del nodo edge con il suo valore attuale, con `bdSeq` e `Node Control/Rebirth`. |
| DBIRTH, la nascita di un dispositivo | Dopo ogni NBIRTH, per ogni dispositivo, e per un dispositivo che entra nell'elenco: ogni tag del dispositivo con il suo valore attuale. |
| NDATA, DDATA | Cinque volte al secondo, le metriche il cui valore o la cui qualità è cambiato: un messaggio per il nodo edge, uno per dispositivo. |
| DDEATH, la morte di un dispositivo | Quando l'ultima riga di un dispositivo esce dall'elenco, e prima che un dispositivo i cui tag sono cambiati rinasca. |

Ogni messaggio esce con QoS 0, mai conservato, tranne l'NDEATH. Il numero di sequenza `seq` riparte da 0 a
ogni NBIRTH e conta ogni messaggio successivo fino a 255, poi ricomincia; `bdSeq` conta le connessioni,
così l'NBIRTH e l'NDEATH di una connessione portano lo stesso numero. Nulla resta in coda mentre il broker
non è raggiungibile.

| Tipo della sorgente | Tipo Sparkplug B |
| --- | --- |
| `Boolean` | `Boolean` |
| `SByte`, `Byte` | `Int8`, `UInt8` |
| `Int16`, `UInt16` | `Int16`, `UInt16` |
| `Int32`, `UInt32` | `Int32`, `UInt32` |
| `Int64`, `UInt64` | `Int64`, `UInt64` |
| `Float`, `Double` | `Float`, `Double` |
| `String` | `String` |
| `DateTime` | `DateTime` |
| `Guid`, `ByteString` | `UUID`, `Bytes` |

Un tag viene pubblicato nel suo tipo; una variabile nel tipo con cui la serve il [server OPC UA](connector-opcua-served-nodes#where-a-client-finds-each-source) (un numero intero come Int64, un numero reale come Double, un valore di presentazione come testo). Ogni metrica porta la sua qualità nella proprietà `Quality`, la convenzione che leggono gli host Sparkplug B: 192 buona, 64 incerta (con l'ultimo valore pubblicato quando la lettura ha smesso di arrivare), 0 cattiva (la metrica resta allora senza valore).

| Comando | Che cosa fa la stazione |
| --- | --- |
| NCMD `Node Control/Rebirth` | Pubblica di nuovo l'NBIRTH e ogni DBIRTH, con i valori attuali. |
| NCMD o DCMD con la metrica di una riga in Lettura e scrittura | Scrive il valore nella sorgente, come fa la [Scrittura di ritorno](#writing-back): nell'ordine in cui arrivano i comandi, registrata nel diario tecnico con l'origine `MQTT <broker name>`. |
| Qualsiasi altra metrica, un comando senza valore o uno che il broker consegna conservato | Non viene scritto nulla; il diario tecnico lo annota nell'area Connector, componente `MQTT publication`. |

La stazione ascolta sempre i comandi per il nodo edge, perché un host possa chiedere una rinascita, e i
comandi per un dispositivo solo finché una delle sue righe è in Lettura e scrittura. Passare un broker a
Sparkplug B fa riaprire la sua connessione una volta, perché il broker tenga l'ultima volontà del nodo edge;
i dispositivi del broker la attraversano come ogni riconnessione. La stazione è un nodo edge e non
un'applicazione host primaria: non aspetta il messaggio STATE di un host per nascere.

```text
mqtt_broker_upsert(id: "<id from mqtt_broker_list>", publishFormat: "SparkplugB", sparkplugGroupId: "Plant1")
mqtt_publish_list()
```

## Scrittura di ritorno {#writing-back}

Una riga di una [scheda Pubblicare](#the-publish-card) in **Lettura e scrittura** accetta valori sul
suo topic di scrittura, il topic della riga seguito da `/set`: una dashboard nel cloud che legge
`plant/oven/setpoint` invia un nuovo setpoint su `plant/oven/setpoint/set`. La stazione lo scrive
come un client del suo [server Modbus](connector-modbus-server) o del suo
[server OPC UA](connector-opcua-served-nodes) scrive una riga marcata in lettura e scrittura.

| Situazione | Che cosa succede |
| --- | --- |
| Una riga passa a Lettura e scrittura | La stazione si iscrive al suo topic di scrittura, con QoS 1, sulla connessione che usano i dispositivi del broker, e di nuovo ogni volta che quella connessione si apre. Una riga di sola lettura non viene iscritta, quindi un messaggio sul suo topic di scrittura non raggiunge mai la stazione. |
| Arriva un messaggio sul topic di scrittura | `{"value": 65}` o il valore semplice (`65`, `true`, `Ricetta A`) viene scritto nella sorgente, nell'ordine in cui arrivano i messaggi: un tag per la stessa via della scrittura di un operatore, una variabile di memoria così com'è. Un numero JSON, `true` o `false` e un testo JSON arrivano come ciò che portano; ciò che non è JSON si legge come testo. Il valore viene convertito nel tipo della sorgente come ogni scrittura, e un valore che il tipo non può portare viene rifiutato. |
| Il messaggio non porta alcun valore | Non si scrive nulla: un messaggio vuoto, `null`, un oggetto JSON senza `value`, un array. |
| Il broker consegna il messaggio che ha conservato | Non si scrive nulla. Il broker lo consegna quando la stazione si iscrive, e un setpoint lasciato lì da un'ora non è un valore che qualcuno invia adesso. Un valore pubblicato mentre la stazione ascolta viene scritto, conservato o no. |
| La sorgente non accetta più scritture | Non si scrive nulla: un tag tornato a sola lettura, una variabile eliminata. |
| La riga torna a Sola lettura, cambia topic o viene eliminata, oppure il broker viene disabilitato | La stazione smette di ascoltare quel topic di scrittura in un attimo, e un messaggio che arriva nel frattempo non scrive nulla. |

Il diario tecnico annota ogni scrittura con l'origine `MQTT <nome del broker>` nell'area Connector,
come annota la scrittura di un operatore. Un messaggio che non è stato scritto viene annotato
nell'area Connector, componente `MQTT publication`, una volta e poi in un riepilogo ogni pochi
minuti finché si ripete.

Il nome utente e la password del broker sono l'unica protezione: chi il broker lascia pubblicare su
un topic di scrittura può cambiare quella sorgente, e su un broker nel cloud condiviso con altre
persone sono più persone dell'impianto. Dare all'account della dashboard sul broker solo i topic di
cui ha bisogno. Su un broker in Sparkplug B le stesse righe
accettano comandi Sparkplug B (NCMD, DCMD) al posto di un topic di scrittura ([Pubblicazione in Sparkplug B](#publishing-in-sparkplug-b)).

```text
"C:\Program Files\mosquitto\mosquitto_pub" -h localhost -u dashboard -P <password> -t plant/oven/setpoint/set -m 65
```

## Sicurezza {#security}

Con **Usa TLS**, il certificato del broker viene accettato in uno di due modi: la sua catena e
il suo nome host si convalidano nell'archivio certificati di Windows, come in qualsiasi
connessione HTTPS di questo computer, oppure è stato approvato proprio quel certificato in
[Accesso delle apparecchiature](#equipment-access), nella pagina di questo driver. Un broker il
cui certificato viene da un'autorità di certificazione propria diventa attendibile appena
quell'autorità viene installata nell'archivio di Windows, e uno con un certificato autofirmato
non richiede nulla di installato una volta approvato il suo certificato. La revoca non viene
controllata, e nulla accetta un certificato alla cieca: uno sconosciuto viene rifiutato, il
broker resta Non raggiungibile, con il motivo, e i suoi dispositivi attendono l'approvazione.
La password viaggia dentro la connessione TLS; senza TLS viaggia in chiaro, come avverte la
scheda.

## Accesso delle apparecchiature {#equipment-access}

Un broker con un certificato autofirmato, o emesso da un'autorità propria, presenta un
certificato di cui nessuna radice installata in Windows si fa garante. Non serve installare
quella radice su questo computer. Con **Usa TLS**, la prima connessione viene rifiutata, il
broker resta Non raggiungibile, con il motivo, i suoi dispositivi leggono **In attesa
dell'approvazione del certificato** nell'albero e nelle loro pagine, il cui pulsante
**Verifica il certificato** apre questa scheda sul certificato, e il certificato attende in
**Accesso delle apparecchiature**, alla fine della pagina del driver MQTT, la stessa scheda che
porta la pagina del driver OPC UA, finché non si decide:

| Gruppo | Che cosa elenca | Che cosa si può fare |
|---|---|---|
| In attesa di approvazione | Ogni certificato rifiutato, con il nome che dichiara: la sua impronta SHA-256, l'indirizzo del broker a cui è stato incontrato, quando ha tentato l'ultima volta e quante volte, e i suoi dettagli (soggetto, emittente, nomi di dominio, date di validità). Quello aperto da **Verifica il certificato** di un dispositivo è segnato come Selezionato per la verifica. | **Approva** accetta il certificato dal tentativo di connessione successivo della sessione. **Blocca** continua a rifiutarlo senza elencarlo di nuovo qui. **Scarta tutti i certificati in attesa** svuota l'elenco senza approvare né bloccare nulla. |
| Apparecchiature approvate | I certificati approvati o importati, con chi ha deciso e quando, e l'indirizzo del broker a cui ciascuno è stato incontrato l'ultima volta. | **Ritira l'approvazione** chiude la sessione del broker e riporta il suo certificato nell'elenco. **Importa certificato** approva un file di certificato pubblico già in proprio possesso (.der, .cer, .crt o .pem) prima della prima connessione. |
| Apparecchiature bloccate | I certificati bloccati. Un certificato bloccato viene rifiutato anche se Windows lo considera attendibile. | **Sblocca** riporta il certificato nell'elenco, senza approvarlo. |

Verificare l'impronta con chi gestisce il broker prima di approvare il certificato.
Un'approvazione accetta quel certificato e nessun altro, e solo entro le sue date di validità;
sostituisce la radice che manca a Windows e il nome che il certificato porta, così un broker
raggiunto con il suo indirizzo IP si collega anche se il suo certificato nomina un altro host,
e nient'altro del certificato viene allentato. Quando il broker risponde a un indirizzo di cui
era stato approvato il certificato con un altro, i suoi dispositivi smettono di leggere, il
nuovo attende qui e la stazione segnala « Certificato cambiato: `<nome>` » con un pulsante
**Verifica**. Un rilevamento del broker dalla scheda Rileva i dispositivi incontra le stesse
decisioni. Un certificato che Windows considera già attendibile viene accettato come prima e
non compare mai qui; nulla viene approvato automaticamente.

La causa del broker dice il motivo invece di un errore TLS senza nome: « Il certificato del
broker a `<host>:<porta>` (i primi 16 caratteri dell'impronta) attende la vostra approvazione
in Accesso delle apparecchiature, nella pagina del driver MQTT. La lettura inizia appena lo
approvate. » Uno bloccato dice « …è bloccato in questa stazione. Verificarlo in Accesso delle
apparecchiature, nella pagina del driver MQTT. » Un certificato fuori dalle sue date di
validità, revocato o con la firma non valida viene rifiutato con quel motivo e non viene mai
elencato, perché nessuna approvazione lo rende valido.

Le decisioni sono conservate nella cartella dati della stazione, in `ua\pki\mqtt`, e
sopravvivono a un riavvio. I verbi vengono disegnati solo per un ruolo che può configurare il
Connector, e agiscono solo nella finestra della stazione stessa: un browser su un altro
dispositivo legge la scheda senza cambiarla, e nessuno strumento di agente approva un
certificato (`connector_browse` segnala i dispositivi del broker con `awaitingCertificate`).
L'elenco tiene fino a 100 certificati in attesa di approvazione; quando è pieno, uno nuovo
viene ancora rifiutato ma non elencato, e la scheda lo dice.

## Preparare il broker {#prepare-the-broker}

Un broker deve accettare le connessioni dal computer della stazione, e accettare l'accesso
della stazione. Eclipse Mosquitto 2, il broker più diffuso, non fa né l'una né l'altra cosa di
fabbrica: senza un listener nel suo mosquitto.conf ascolta solo sul proprio computer, e appena
ne ha uno rifiuta un client senza nome utente. MQTT non dice quale broker ha risposto, quindi
la stazione nomina Mosquitto come il caso comune, mai come una certezza.

| Il broker risponde | Che cosa dice la pagina del broker | Che cosa lo cambia |
| --- | --- | --- |
| Il suo computer rifiuta la porta | Il broker accetta connessioni solo dal proprio computer | In mosquitto.conf, la riga listener seguita dalla porta (listener 1883), poi un riavvio del broker; la porta consentita nel firewall del suo computer |
| Rifiuta un client senza nome utente | Il broker non accetta un client senza nome utente | Nome utente e Password di accesso nella scheda Connessione al broker, con un account del broker; su Mosquitto 2 quell'account si crea con mosquitto_passwd e si indica con password_file in mosquitto.conf, oppure i client anonimi entrano con allow_anonymous true |
| Rifiuta il nome utente o la password | Il rifiuto del broker stesso, che rimanda a Nome utente e Password di accesso nella scheda Connessione al broker | L'account, sul broker o nella scheda |

I primi due sono detti sulla pagina del broker, al posto del suo errore in inglese, con questi
passi e la porta; i suoi dispositivi continuano a rimandare al broker. L'avviso scompare da solo
non appena la sessione si connette. Il segno dell'albero e il registro conservano la causa in
inglese, che termina con gli stessi passi di Mosquitto.

## Installare un broker {#installing-a-broker}

La stazione ha bisogno di un broker a cui collegarsi, e l'apparecchiatura anche. Dove non ce
n'è uno, Eclipse Mosquitto è un piccolo broker che gira come servizio di Windows:

1. Scaricare il programma di installazione per Windows dalla pagina dei download di
   mosquitto.org ed eseguirlo. Installa il servizio **Mosquitto Broker** in
   `C:\Program Files\mosquitto`.
2. Di serie, Mosquitto risponde solo sul computer su cui gira. Per lasciare entrare le
   apparecchiature della rete, aggiungere un listener e un file di password al
   `mosquitto.conf` di quella cartella:

   ```
   listener 1883
   password_file C:\Program Files\mosquitto\passwd
   ```

   Creare il file con l'account che useranno la stazione e le apparecchiature (il comando
   chiede la password):

   ```
   "C:\Program Files\mosquitto\mosquitto_passwd" -c "C:\Program Files\mosquitto\passwd" station
   ```

3. Riavviare il servizio da un prompt dei comandi di amministratore, e consentire l'ingresso
   della porta TCP 1883 in Windows Defender Firewall per la rete delle apparecchiature:

   ```
   net stop mosquitto
   net start mosquitto
   ```

4. Nella pagina del broker, inserire `localhost` come **Indirizzo host** quando il broker gira
   sul computer stesso della stazione, oppure l'indirizzo di quel computer negli altri casi,
   **Porta** 1883 e l'account creato, poi **Abilita**.

Per provare un tag semplice prima che le apparecchiature siano collegate, pubblicare un valore
di prova conservato dalla stessa cartella e puntare su di esso il **Topic MQTT** di un tag:

```
"C:\Program Files\mosquitto\mosquitto_pub" -h localhost -u station -P <password> -t plant/line1/temperature -m 21.5 -r
```

## Limiti da conoscere {#limits-worth-knowing}

- Il driver MQTT è un client, non un broker: le apparecchiature e la stazione si collegano a
  un broker, il [broker MQTT](connector-mqtt-broker) della stazione stessa o un altro.
- La scheda Pubblicare di un broker esce come JSON o come Sparkplug B, mai entrambi insieme. In
  Sparkplug B la stazione è un nodo edge, non un'applicazione host primaria, e non usa alias,
  modelli né valori storici.
- La stazione non è un'applicazione host Sparkplug B: non pubblica nessun messaggio STATE. Un
  nodo edge impostato per aspettare un host primario prima di pubblicare resta muto.
- Il MQTT semplice non dice niente di un editore che si è zittito: un tag semplice tiene
  l'ultimo valore ricevuto finché la connessione al broker regge, datato dal suo arrivo, a meno
  che il suo dispositivo non imposti un **Tempo massimo senza messaggi (s)**. Un topic pubblicato solo quando il
  valore cambia non distingue un editore zittito da un valore stabile, quindi il suo dispositivo
  resta a 0. Lo Sparkplug B invece lo dice, con il certificato di morte.
- La sessione parte pulita: quello che è stato pubblicato mentre la stazione non era
  collegata non viene riproposto, ed è il messaggio conservato di un topic a ridare un valore
  al suo tag.
- Un tag legge un topic, mai un filtro, e un valore di esso; una metrica Sparkplug B che è un
  array, un set di dati, un template o un insieme di proprietà non viene letta.
- Solo TCP, con o senza TLS: niente MQTT su WebSockets, niente certificato client, e nessun
  broker più vecchio di MQTT 3.1.1.
- Un dispositivo non può essere spostato su un altro broker: crearlo sotto l'altro broker ed
  eliminare quello vecchio.
