Dal terminale al PLC: costruire un industrial bridge in C# (e perché gli errori sono i migliori alleati)
Scritto da Marco Morello il 22 luglio 2026
💡 In sintesi: torno da tre settimane di trasferta in cantiere. Quando rientri a casa stanco ma con l'adrenalina addosso, senti il bisogno di riaccendere la passione sporcandoti le mani con il codice. Così è nato PlcBridge, un simulatore TCP/IP in C# per colmare la distanza tra il software gestionale e l'hardware industriale, accettando l'errore come parte fondamentale del processo di crescita.
Torno da tre settimane di trasferta in cantiere. Quando rientri a casa dopo giorni passati a risolvere grane sul campo, stanco ma con quella tipica adrenalina addosso, senti il bisogno di riaccendere la passione. Volevo qualcosa che mi restituisse la costanza, che mi costringesse a sedermi alla scrivania e a sporcarmi le mani con il codice.
La verità è che, a rigor di logica, avrei dovuto finire MotoLogPro e gli altri mille progetti personali che ho in cantiere. Ma sono fatto così: con i progetti personali tendo a spaziare, a saltare da un'idea all'altra seguendo l'ispirazione del momento. Sul lavoro invece sono una macchina, cerco di chiudere un task prima di passare al successivo per non lasciare nulla in sospeso e non accumulare mille code aperte. A casa, però, il codice è libertà pura.
Così è nato PlcBridge, un progetto di studio nato per colmare la distanza tra il software gestionale in .NET e l'hardware industriale, costruendo un simulatore TCP/IP passo dopo passo. Senza la pretesa di aver inventato nulla, ma con la determinazione di capire come funzionano le cose sotto il cofano.
1. La filosofia del "learn by doing": perché la teoria non basta
Nel software industriale, la distanza tra un concetto astratto e la sua implementazione pratica è immensa. Puoi leggere quanti libri vuoi sui socket TCP/IP, ma capirai cosa significa davvero gestire una connessione solo quando la porta 5000 rimarrà bloccata in background per un processo zombie, impedendoti di compilare.
Il learn by doing non significa improvvisare a caso. Significa:
- Accettare l'errore come parte del processo di compilazione mentale.
- Costruire mattoncino per mattoncino, partendo dalla riga di comando più scarna fino a un'architettura robusta.
- Validare rigorosamente, usando strumenti professionali come CI/CD e test automatizzati, perché il codice che scrivo oggi deve funzionare anche quando non sono al PC.
2. L'architettura: perché il polling batte il push continuo
All'inizio la tentazione è semplice: creare un server che spara dati a raffica verso chiunque si connetta. Ma nel mondo industriale la realtà è diversa. I PLC parlano quando è necessario, e i sistemi di supervisione interrogano i registri secondo una logica precisa.
Ho scelto quindi un'architettura request-response di tipo master-slave:
- Il client (HMI di monitoraggio): interroga attivamente il server inviando comandi testuali specifici (come la lettura della pressione).
- Il server (PLC virtuale): valuta la richiesta, simula fluttuazioni fisiche reali tramite logiche stocastiche (perché i sensori reali non sono mai piatti) e risponde con i dati formattati.
4. Refactoring Architetturale: Dallo "Spaghetti" al Modulare (IoC & DI)
Durante lo sviluppo, mi sono accorto che il mio Program.cs stava diventando troppo pesante.
Il Server creava direttamente il PLC (new PlcController()). Questo creava un
accoppiamento forte: se avessi voluto cambiare logica o testare il PLC isolatamente,
avrei dovuto modificare il server stesso.
Per risolvere questo problema strutturale, ho applicato l'Inversione di Controllo (IoC) e la Dependency Injection (DI). Il salto di qualità è evidente:
Il server istanzia direttamente il controller tramite la
parola
chiave new. I due componenti sono saldati insieme, rendendo impossibile
testare
il PLC isolatamente.
// Accoppiamento rigido (Tight Coupling)
public class PlcServer
{
// Il Server 'possiede' direttamente l'istanza
private PlcController _plc = new PlcController();
}
Il server riceve un'astrazione. Posso testare il PLC
isolatamente iniettando finte istanze nel Server tramite l'interfaccia
IPlcController.
// Inversione di controllo (Dependency Injection)
public class PlcServer
{
private readonly IPlcController _plc;
// Il Server riceve il contratto via costruttore
public PlcServer(IPlcController plc) => _plc = plc;
}
IPlcController. Questo mi permette di sostituire l'implementazione o
testare la logica di business senza far partire un intero server TCP. Il "new" è il nemico
della manutenibilità, e imparare a passare le dipendenze
via costruttore è stato il salto di qualità definitivo verso un software professionale.
4. Dalla riga di comando scarna a una TUI professionale
Un programma che funziona ma è illeggibile è un software a metà. Se un operatore in cleanroom deve monitorare un impianto, non può interpretare stringhe grezze perse in un terminale nero.
Prima di fare il salto verso il web, ho scelto di evolvere la user interface introducendo una libreria per creare interfacce a riga di comando avanzate, le cosiddette TUI:
- Tabelle formattate: i dati chiave come pressione, temperatura e stato della pompa vengono presentati con una griglia pulita e leggibile a colpo d'occhio.
- Menu interattivi (selection prompt): invece di far digitare comandi a mano all'operatore — esponendo il fianco a errori di battitura e distrazioni umane — ho inserito un menu interattivo navigabile con le frecce. È il principio del poka-yoke, cioè a prova di errore, applicato alla CLI.
▶ CLIENT/MONITOR di Supervisione (TUI)
? Seleziona il comando di controllo o lettura:
READ_PRESSURE
READ_TEMP
> START_PUMP
STOP_PUMP
SYSTEM_STATUS
[Invio]
Connessione al PLC in corso (127.0.0.1:5000)...
✔ SUCCESS: Pump is now RUNNING
5. Da stateless a stateful: la memoria del PLC
Un vero PLC non si limita a generare numeri casuali: ha uno stato interno. Accendere una pompa richiede che il sistema ricordi l'azione nel tempo.
Nel mio PlcBridge ho introdotto una memoria di stato persistente per variabili come lo stato della pompa e i valori correnti. Ora il sistema accetta comandi di scrittura e controllo attuatori, trasformandosi da semplice script di test in un vero nodo bidirezionale.
// Stato interno del PLC simulato (memoria dei registri)
private bool _isPumpRunning = false;
private double _currentPressure = 12.0;
public string StartPump()
{
_isPumpRunning = true;
return "SUCCESS: Pump is now RUNNING";
}
6. Lezioni dal cantiere digitale: gli errori come indicatori di rotta
Costruire software significa inevitabilmente sbattere contro i muri. Invece di nascondere la polvere sotto il tappeto, ho tracciato ogni singola anomalia di questo sprint. Ecco le cicatrici tecniche che mi hanno insegnato di più:
-
1. La porta fantasma e l'igiene di sistema (MSB3026)
Quando il server TCP resta appeso, la porta 5000 non perdona. Il compilatore si blocca perché l'eseguibile è in uso. Imparare a usare
taskkille a gestire correttamente la chiusura del Socket è stato un prezioso bagno di realtà sistemistica. -
2. Il labirinto dell'Annidamento (.sln e CS0579)
Aver creato la cartella dei test dentro quella del progetto principale ha mandato in crisi il compilatore, generando decine di attributi duplicati. La soluzione? Ristrutturare tutto usando una Solution (.sln) alla radice, il "direttore d'orchestra" che isola e gestisce i progetti .NET in modo pulito.
-
3. Referenziare con precisione chirurgica
Il comando
dotnet add referencenon vuole cartelle generiche, esige il puntamento esatto al file.csproj. Un dettaglio sintattico che mi ha costretto a studiare come il framework risolve le dipendenze interne tra i moduli. -
4. I messaggi di Git non sono minacce
Il classico "Remote origin already exists" all'inizio spaventa. Poi capisci che Git non ti sta bloccando: ti sta chiedendo chiarezza. Riscrivere l'URL con
set-urlmi ha insegnato a padroneggiare il versioning senza farmi intimorire dalla console.
Ma la soddisfazione più grande? Vedere la pipeline CI/CD su GitHub Actions illuminarsi di verde. Sapere che ogni singola modifica viene compilata e validata dai miei test unitari in un ambiente cloud isolato, mi dà la tranquillità e la solidità di un workflow aziendale vero e proprio.
E adesso? Verso il web con Blazor
Questo progetto non è un punto d'arrivo, ma una solida fondamenta. Ho un server robusto, un protocollo chiaro, una CLI curata e una pipeline CI/CD pronta all'uso.
Il prossimo passo? Prenderò questo backend di comunicazione e lo porterò sul web con Blazor, per creare una dashboard interattiva accessibile da qualsiasi browser o tablet industriale.
La strada è lunga e l'umiltà tanta, ma i mattoni sono posati dritti. Meglio fatto ma non perfetto che niente: rimettere in movimento il sito e continuare a scrivere codice un pezzetto alla volta è l'unico modo per crescere davvero. Ci risentiamo alla prossima iterazione.