Flotta eterogenea — 3 marche di PLC diverse, un solo tunnel VPN (Modbus TCP)
Un panificio ha mescolatrice Schneider, camera Carel e forno WAGO. Tutti parlano Modbus TCP ma con quirks diversi. Come configurarli in un solo tenant Rela AI senza perdere ore a debuggare il byte order.
Flotta eterogenea — 3 marche di PLC, un tunnel VPN
Modbus TCP è la lingua franca dell'automazione industriale: praticamente ogni marca di PLC lo parla. Ma "parla Modbus" non significa "parla allo stesso modo" — ogni costruttore ha quirks su byte order, convenzione di indirizzi e limiti di connessioni concorrenti. Questo caso mostra come Rela AI gestisce una flotta mista senza che il cliente debba normalizzare nulla lato PLC.
Riassunto esecutivo
Il punto di svolta: un solo tenant Rela tratta PLC di marche diverse come se fossero dello stesso fabbricante. AHI, agente IA, regole di allarme e KPI operano su eventi unificati — l'eterogeneità resta intrappolata nella configurazione di ogni fonte e non si propaga alla pipeline predittiva.
| Prima | Dopo |
|---|---|
| Ogni marca esige un'integrazione SCADA diversa | 3 fonti Modbus in Rela, un click ciascuna |
| Gli operatori imparano 3 SCADA diversi (uno per marca) | Un solo dashboard, stesso linguaggio per le 3 macchine |
| Regole duplicate per marca: una per forni Schneider, un'altra per forni WAGO | Una sola regola "forno temperatura > 280" — tag_enrichment discrimina |
| Quando una sonda deriva su una marca, nessun apprendimento incrociato | Il rilevatore ML impara pattern per asset, indipendente dalla marca |
A cosa serve
- Sorvegliare macchine di marche diverse già in fabbrica senza sostituire nulla.
- Centralizzare l'inbox allarmi: una sola schermata, un solo agente IA, escalation comune.
- Applicare regole deterministiche e manutenzione predittiva sugli asset più importanti, non solo su quelli già in uno SCADA omogeneo.
- Capire le differenze tra marche (byte order, addressing) una sola volta in fase di configurazione, e dimenticarsene dopo.
Prima di iniziare — perché sei tu a configurare il peer
Anche per una flotta multi-brand, il modello di accesso non cambia: il cliente crea il proprio peer WireGuard, Rela non riceve mai credenziali di VPN aziendali.
Tre ragioni critiche (le 9 complete sono in Perché un tunnel dedicato):
- Principio del minimo privilegio: la tua VPN aziendale apre l'intera rete (ERP, mail, SharePoint, OT). Rela ha bisogno solo della subnet dei 3 PLC. Una violazione lato Rela colpisce una subnet OT, non tutta l'azienda.
- Revoca in pochi secondi: elimini il peer dal tuo router quando vuoi, senza ticket né coordinamenti con noi. La chiave privata vive nel tuo apparato e non esce mai.
- Conformità (IEC 62443, NIST SP 800-82, SOC 2, ISO 27001): tutte richiedono la separazione IT/OT tramite tunnel dedicato. Condividere credenziali aziendali è un findable diretto in qualsiasi audit.
Il modello peer-dedicato funziona identico per 1, 3 o N PLC dietro la stessa LAN — è il sito che decide il numero di tunnel, non le macchine. Condividere credenziali esce dal modello self-service ed entra nel deployment enterprise custom.
Come funziona
flowchart LR
M["Schneider M340<br/>Mescolatrice<br/>192.168.10.55"] --> R[("Router del cliente<br/>WireGuard + DNAT")]
C["Carel pCO5 plus<br/>Camera lievitazione<br/>192.168.10.50"] --> R
W["WAGO 750-881<br/>Forno rotativo<br/>192.168.10.65"] --> R
R -- "1 solo tunnel" --> CONC[("Concentratore<br/>Rela VPN")]
CONC --> WORKER[("Cloud Run worker<br/>3 modbus_listener tasks")]
WORKER --> PIPE[("Pipeline unificata<br/>_machine_events")]
PIPE --> AGENT[("Agente IA<br/>Sorvegliante Stabilimento")]
PIPE --> MAST[Asset Mescolatrice]
PIPE --> CAST[Asset Camera]
PIPE --> WAST[Asset Forno]Il trucco: i 3 task listener girano sullo stesso worker. Ognuno decodifica con la sua configurazione (byte order specifico per registro), ma tutti emettono allo stesso _machine_events. L'agente IA e le regole non sanno — né vogliono sapere — che provengono da marche diverse.
Parametri / Configurazione
Byte order per marca (la gotcha più comune)
modbus_byte_order si configura per registro, non per fonte — perché alcuni PLC mescolano convenzioni a seconda del firmware o del DB. Default realistici:
| Marca / firmware | Default float32 | Default 32-bit int | Note |
|---|---|---|---|
| Schneider Modicon M340 / M580 | big_endian_swap (CDAB) | big_endian (ABCD) | Il word-swap è una stranezza storica Modicon. Verificare con mbpoll |
| Carel pCO5+ / pCOWeb | big_endian (ABCD) | big_endian | Standard Carel |
| WAGO 750-881 | big_endian (ABCD) | big_endian | Configurabile, default ABCD |
| Siemens S7 con modulo Modbus | big_endian (ABCD) | big_endian | Big-endian nativo S7 |
| Allen-Bradley via gateway Modbus | little_endian (DCBA) | little_endian | Inverso a Schneider |
| Omron CJ/CP | big_endian (ABCD) | big_endian | Standard |
Suggerimento operativo: in caso di dubbio, leggere un registro con un valore noto (es: setpoint =
25.0) con i 4 byte order e prendere quello che restituisce il numero corretto. Cinque minuti che evitano giorni di debug.
Convenzione degli indirizzi
Rela usa l'indirizzo Modbus 0-based della wire (quello che pymodbus invia). Conversione rapida dalla doc del costruttore:
| La doc del vendor dice | In Rela metti |
|---|---|
Modicon 40101 (5-digit) | address 100, FC 3 |
Modicon 30015 | address 14, FC 4 |
| Carel "register 1" | address 0, FC 3 |
| WAGO "%MW100" | address 100, FC 3 |
| Siemens DB1.DBW0 via Modbus | dipende dal mapping in TIA |
Limiti di connessioni concorrenti
| PLC | Connessioni concorrenti | Implicazione |
|---|---|---|
| Schneider M340 | 8 | Confortevole per Rela + SCADA + HMI |
| Schneider M580 | 16 | Abbondante |
| Carel pCOWeb | 4 | Critico: SCADA + Rela + HMI + tablet già saturano |
| WAGO 750-881 | 4-8 (dipende dal firmware) | Verificare prima di aggiungere client |
| Siemens S7 con CM 1241 | 1-3 | Molto stretto — Rela può essere l'unico client |
Mappatura Modbus della flotta (riassunto eseguibile)
Mescolatrice — Schneider Modicon M340 (10.200.7.55:502, unit_id 1)
| Registro | Address | FC | Tipo | Byte order | Unità |
|---|---|---|---|---|---|
| motor_torque | 100 | 3 | float32 | big_endian_swap | % |
| motor_speed | 102 | 3 | float32 | big_endian_swap | rpm |
| motor_temperature | 104 | 3 | float32 | big_endian_swap | °C |
| mixing_time_seconds | 106 | 3 | uint16 | big_endian | s |
| motor_running | 50 | 1 | bool | — | 0/1 |
| alarm_overpressure | 60 | 1 | bool | — | 0/1 |
Camera lievitazione — Carel pCO5+ (10.200.7.50:502, unit_id 1)
| Registro | Address | FC | Tipo | Byte order | Unità |
|---|---|---|---|---|---|
| measured_temperature | 2 | 3 | float32 | big_endian | °C |
| measured_humidity | 4 | 3 | float32 | big_endian | % |
| cycle_phase | 10 | 3 | uint16 | big_endian | 1-5 |
| door_open | 1 | 2 | bool | — | 0/1 |
| probe_t_alarm | 20 | 2 | bool | — | 0/1 |
Forno rotativo — WAGO 750-881 (10.200.7.65:502, unit_id 1)
| Registro | Address | FC | Tipo | Byte order | Unità |
|---|---|---|---|---|---|
| chamber_temperature | 200 | 3 | float32 | big_endian | °C |
| deck_temperature | 202 | 3 | float32 | big_endian | °C |
| fan_speed | 204 | 3 | uint16 | big_endian | % |
| batch_count | 206 | 3 | uint32 | big_endian | pezzi |
| door_open | 10 | 2 | bool | — | 0/1 |
| alarm_overheat | 11 | 2 | bool | — | 0/1 |
Come usarlo
Passo 1 — Creare il tunnel VPN (uno solo, come per le flotte omogenee)
Sidebar → Settings → Connectivity → etichetta Panificio - Produzione → scaricare .conf → importarlo nel router.
Passo 2 — DNAT delle 3 IP sul router
/ip firewall nat
add chain=dstnat dst-address=10.200.7.55 dst-port=502 \
protocol=tcp action=dst-nat to-addresses=192.168.10.55 to-ports=502
add chain=dstnat dst-address=10.200.7.50 dst-port=502 \
protocol=tcp action=dst-nat to-addresses=192.168.10.50 to-ports=502
add chain=dstnat dst-address=10.200.7.65 dst-port=502 \
protocol=tcp action=dst-nat to-addresses=192.168.10.65 to-ports=502
/ip firewall filter
add action=accept chain=forward in-interface=rela-vpn src-address=10.200.0.0/16Passo 3 — Creare i 3 asset
| Campo | Mescolatrice | Camera | Forno |
|---|---|---|---|
| Nome | Mescolatrice a Spirale | Camera Lievitazione 1 | Forno Rotativo 1 |
| Asset code | MES-01 | LIE-01 | FOR-01 |
| Asset type | mixer | fermentation_chamber | rotary_oven |
| Criticità | medium | high | high |
| Plant | Panificio Centrale | Panificio Centrale | Panificio Centrale |
| Area | Produzione | Produzione | Produzione |
Passo 4 — Agente IA comune
Sidebar → Allarmi → Agenti macchina → + Nuovo.
Nome: Sorvegliante Stabilimento
Modello: gemini-3.1-pro-preview
Auto-task: sì → Reparto "Manutenzione"
Auto-notify: sì → WhatsApp del capo turno
Escalation: sì (5 min / 15 min / 30 min)Un solo agente copre le 3 marche. Le regole filtrano per tag_enrichment.machine_type quando serve specializzazione.
Passo 5 — Creare le 3 fonti Modbus con la config specifica per marca
Sidebar → Allarmi → Sources → + Nuova fonte, ripetere 3 volte.
Fonte 1: Mescolatrice Schneider
Source ID: mescolatrice-schneider
Agente: Sorvegliante Stabilimento
Protocollo: Modbus TCP
Modbus host: 10.200.7.55
Modbus port: 502
Unit ID: 1
Registri:
- motor_torque addr=100 fc=3 type=float32 byte_order=big_endian_swap unit="%"
- motor_speed addr=102 fc=3 type=float32 byte_order=big_endian_swap unit="rpm"
- motor_temperature addr=104 fc=3 type=float32 byte_order=big_endian_swap unit="°C"
- alarm_overpressure addr=60 fc=1 type=bool emit=bit_flip
Field mapping:
tag_enrichment:
machine_type: mixer
brand: schneider
model: modicon-m340Fonte 2: Camera Carel
Source ID: camera-carel
Agente: Sorvegliante Stabilimento
Protocollo: Modbus TCP
Modbus host: 10.200.7.50
Modbus port: 502
Unit ID: 1
Registri:
- measured_temperature addr=2 fc=3 type=float32 byte_order=big_endian unit="°C"
- measured_humidity addr=4 fc=3 type=float32 byte_order=big_endian unit="%"
- door_open addr=1 fc=2 type=bool emit=bit_flip
Field mapping:
tag_enrichment:
machine_type: proofer
brand: carel
model: pco5plusFonte 3: Forno WAGO
Source ID: forno-wago
Agente: Sorvegliante Stabilimento
Protocollo: Modbus TCP
Modbus host: 10.200.7.65
Modbus port: 502
Unit ID: 1
Registri:
- chamber_temperature addr=200 fc=3 type=float32 byte_order=big_endian unit="°C"
- deck_temperature addr=202 fc=3 type=float32 byte_order=big_endian unit="°C"
- fan_speed addr=204 fc=3 type=uint16 unit="%"
- alarm_overheat addr=11 fc=2 type=bool emit=bit_flip
Field mapping:
tag_enrichment:
machine_type: oven
brand: wago
model: 750-881Passo 6 — Collegare ogni fonte al suo asset
Sidebar → Asset → modificare ognuno → aggiungere il event_source_ids corrispondente.
Passo 7 — Una sola regola che copre più marche
Sidebar → Allarmi → Regole → + Nuova regola.
Nome: Forno surriscaldamento
Source: (vuoto — applica globalmente)
Conditions:
- field: machine_type
operator: eq
value: oven
- field: chamber_temperature
operator: gt
value: 280
Recurrence:
count_threshold: 3
window_minutes: 2
Actions:
- change_severity: critical
- create_task:
title: "Surriscaldamento forno"
priority: urgent
department_id: <Manutenzione>
- trigger_escalationPerché è importante: questa regola si applica a TUTTI i forni del tenant (oggi WAGO 750-881; domani se aggiungi un Schneider o un Siemens). La discriminazione la fa tag_enrichment.machine_type, non il source_id. Riusabile e senza duplicazioni.
Casi d'uso reali
Stessa logica di allarme per 3 marche diverse
L'operatore lascia la porta del forno aperta e il door_open del WAGO flippa a 1. Stessa logica dell'evento door_open della camera Carel nel caso proofer-fleet-modbus — ma il forno ha il suo bit_flip e il suo asset. L'agente invia due WhatsApp (uno per macchina), non uno mescolato.
Manutenzione predittiva cross-brand
Il motore della mescolatrice (Schneider) mostra motor_temperature con deriva di +3°C costante per 7 giorni. L'AHI scende da A a C. Il rilevatore ML ha imparato il pattern normale di quella mescolatrice (non la media delle mescolatrici Schneider in generale — il modello è per asset). Genera un task di manutenzione preventiva "Verificare raffreddamento motore" 2 settimane prima del guasto previsto.
Lo stesso accadrebbe se la marca cambiasse a Siemens o ABB — il modello è per asset, non per marca. Zero training pregresso, zero migrazione.
Benchmark cross-brand
/dashboard/operational mostra i 3 asset uno accanto all'altro:
- Mescolatrice MES-01: AHI 92 (A), ultimo guasto 180 giorni fa, RUL 90 giorni.
- Camera LIE-01: AHI 78 (B), 2 allarmi critici negli ultimi 7 giorni, RUL 30 giorni.
- Forno FOR-01: AHI 85 (B), uso 1.200 ore/mese, RUL 120 giorni.
Il manager vede questi dati e decide di prioritizzare la camera anche se è di marca diversa. Senza Rela starebbe confrontando 3 dashboard SCADA incompatibili.
Limitazioni e ipotesi
- Ogni marca esige il suo manuale Modbus. Niente magia. Indirizzi, byte order e FC vengono dal manuale del costruttore o si scoprono con
mbpolldalla LAN prima della configurazione. - Se una marca espone il dato tramite un protocollo non-Modbus che il PLC parla nativamente (tipico: Siemens parla S7comm nativamente, Allen-Bradley parla EtherNet/IP CIP), conviene usare quel protocollo in Rela invece di forzare Modbus per compatibilità. I listener nativi estraggono più metadata (data type senza ambiguità, status codes, timestamp di sorgente).
- Connessioni concorrenti sature: se il cliente ha già SCADA + HMI che leggono un PLC con
max_connections=4(tipico Carel), aggiungere Rela può far cadere la connessione più vecchia. Coordinare il rollout col team di automazione. - Mischiare marche moltiplica i vettori di fallimento in config. Il troubleshooting senza metodologia costa giorni. Per questo il documento include la matrice di byte_order — è la prima cosa da verificare con letture strane.
Troubleshooting
| Sintomo | Causa probabile | Soluzione |
|---|---|---|
Mescolatrice mostra motor_speed = 1.4e-39 (numero assurdo) | byte_order errato: hai provato big_endian e l'M340 voleva big_endian_swap | Cambiare byte_order del registro a big_endian_swap e verificare con mbpoll -t 4 -1 -r 102 -c 2 -F |
| Carel disconnesso ma Schneider e WAGO OK | Carel pCOWeb saturo: 4 connessioni occupate | Chiudere 1 client (HMI duplicata, SCADA duplicato) o upgrade a pCOWeb Pro |
| Forno con eventi solo ogni 30 min invece di 10 s | min_interval troppo alto, o firmware WAGO non aggiornato | Abbassare min_interval a 10 s; se persiste, aggiornare il firmware WAGO |
3 fonti verdi ma machine_type non appare negli eventi | tag_enrichment non applicato (fonte senza field_mapping in config) | Modificare la fonte e aggiungere il blocco field_mapping.tag_enrichment |
Le regole con field: machine_type non si attivano | tag_enrichment è nella fonte ma la regola valuta prima dell'iniezione | Verificare l'ordine della pipeline nei log: field_mapping gira prima di event_rules_engine |
Vantaggi chiave
- 1 tenant, 1 VPN, N marche: qualsiasi combinazione di PLC Modbus TCP entra nello stesso dashboard senza overhead organizzativo.
- Regole che scalano con lo stabilimento:
tag_enrichmentpermette di scrivere 1 regola per tipo di macchina invece di 1 per marca; aggiungere una nuova marca non richiede di duplicare regole. - Manutenzione predittiva agnostica del brand: il modello ML impara pattern per asset, non per marca. Sostituire un PLC su una macchina (upgrade tecnologico) non richiede ri-allenamento.
- Onboarding pedagogico: la matrice byte_order e la convenzione di addressing sono documentate qui cosicché il team di implementazione non scopra le gotchas nel momento peggiore (cliente in produzione).
- Benchmark cross-brand: vedere AHI e RUL di macchine eterogenee fianco a fianco nello stesso dashboard — impossibile con SCADA per marca.
Vedi anche
- Flotta omogenea — 3 camere di lievitazione stesso modello — più semplice, stesso principio del tunnel unico.
- Macchine multiple, un solo tunnel VPN — il modello mentale "1 tunnel = 1 sito fisico".
- Perché un tunnel dedicato — le 9 ragioni per cui non condividiamo la VPN aziendale del cliente.
Panetteria — Camera di Fermentazione via VPN (da zero a produzione)
Passo per passo reale: una panetteria collega la sua camera di fermentazione a Rela AI tramite VPN WireGuard, senza sapere la marca del PLC e senza router enterprise.
Più macchine, un solo tunnel VPN
Come aggiungere forno, impastatrici, compressori e sensori sotto lo stesso tunnel VPN della panetteria — senza riconfigurare o toccare il Mikrotik.