Rela AIRela AI Docs
Sicurezza Alimentare

Catena del Freddo

Monitoraggio della temperatura per refrigerazione e trasporto con deviazioni tracciabili, grace configurabile e journey/leg per le spedizioni.

Catena del Freddo

La catena del freddo è il controllo ininterrotto della temperatura dalla produzione al consumo. Se un asset o una spedizione refrigerata esce dalla banda di sicurezza, il prodotto può essere compromesso. Rela-ai monitora ogni asset, ogni lotto e ogni tratta di trasporto, lasciando un audit trail verificabile per i regolatori.

Sintesi esecutiva

Catena del freddo senza cry-wolf: ogni deviazione attraversa filtri di staleness, flapping e grace configurabile prima di raggiungere l'inbox — e finisce collegata al lotto e al leg di trasporto che l'hanno provocata.

Prima di questa iterazione, una deviazione di 40 secondi per l'apertura di una porta poteva generare tre alert separati e zero informazioni sul lotto interessato. Adesso:

PrimaAdesso
Sensore morto che invia 0 °C fabbricava deviazioni falseFiltro CC-H1: sorgenti stale scartate
Cold chain alertava separatamente dal resto del sistemaCC-H2: feed all'alert aggregator con source_system=cold_chain
Impossibile sapere QUALE sensore aveva aperto la deviazioneCC-H3: source_id persistito su ogni deviazione
Apri/chiudi/apri porta = 2 incidenti artificialiCC-M2: riapertura dentro una flap window di 5 min
Mutazioni di temperature_max invisibiliCC-M5: audit trail fire-and-forget
Grace rigida "warning a critical"CC-M1: modalità escalate o suppress (stile HACCP)
Impossibile sapere QUALE lotto era dentroCC-L1: batch_id e product_type sulla deviazione
Impossibile sapere QUALE leg di trasporto aveva fallitoCC-M6: journey + leg con excursion_ids

A cosa serve

  • Rilevare temperature fuori banda in congelatori, celle frigorifere e veicoli.
  • Distinguere una porta aperta 30 secondi da un guasto del compressore di 2 ore.
  • Attribuire ogni breach al lotto e al leg di trasporto responsabili.
  • Produrre audit trail immutabile per HACCP, GDP, WHO TRS 961, FSMA.
  • Unificare gli alert con il resto del pipeline (manutenzione, qualità, SPC).

Come funziona

flowchart LR
  SENSOR[Sensore] --> READING[Lettura]
  READING --> STALE{Sorgente stale?}
  STALE -- sì --> DROP[Scarto silenzioso]
  STALE -- no --> BAND{Fuori banda?}
  BAND -- no --> CLEAR[Pulisce buffer + risolve attiva]
  BAND -- sì --> ACTIVE{Deviazione attiva?}
  ACTIVE -- sì --> UPDATE[Update peak, duration, sample_count]
  ACTIVE -- no --> FLAP{Flap window 5 min?}
  FLAP -- sì --> REOPEN[Riapre deviazione precedente]
  FLAP -- no --> MODE{Grace mode?}
  MODE -- escalate --> OPEN[Apre in warning, escala a critical se dura]
  MODE -- suppress --> BUFFER[Buffer volatile: promuove solo se dura]
  OPEN --> AGG[Alert Aggregator]
  REOPEN --> AGG
  BUFFER --> AGG
  OPEN --> JOURNEY[Attach al leg attivo se applicabile]
  BUFFER --> JOURNEY

Filtri e protezioni

  1. CC-H1 — staleness: una sorgente contrassegnata stale dal sensor watchdog viene ignorata prima di valutare la banda. Un sensore morto che invia 0 °C non può fabbricare deviazioni false.
  2. CC-M2 — flap window: se una deviazione risolta della stessa direzione riappare entro 5 minuti, viene riaperta invece di crearne una nuova. Evita di frammentare un incidente reale.
  3. CC-H2 — aggregator feed: ogni deviazione nuova o escalata pubblica all'alert aggregator con source_system=cold_chain, severità canonica e riferimento excursion_id.
  4. CC-M1 — grace configurabile:
    • escalate (default): ogni deviazione apre in warning, promuove a critical al superamento del grace.
    • suppress (stile HACCP): i transienti dentro il grace non vengono persistiti; una deviazione si apre solo se supera il grace, e apre direttamente in critical.

Configurazione dell'asset

Dashboard

  1. Asset → apparecchiatura → modifica.
  2. Attiva Catena del Freddo.
  3. Compila:
    • Temperatura min / max: limiti di sicurezza.
    • Unità: °C o °F.
    • Minuti di grace: da 1 a 1440. Fino a 24 ore per spedizioni farmaceutiche (WHO TRS 961 Annex 9, EU GDP).
    • Modalità grace: escalate o suppress.
    • Sorgente eventi: il source_id che fornisce le letture. Validata contro _machine_event_sources (CC-M4).

API

PATCH /api/v1/assets/{asset_id}
{
  "cold_chain_enabled": true,
  "cold_chain_source_id": "src_fridge_01",
  "cold_chain_metric": "temperature",
  "temperature_min": -22,
  "temperature_max": -16,
  "temperature_unit": "C",
  "excursion_grace_minutes": 15,
  "cold_chain_grace_mode": "escalate"
}

CC-M5: qualsiasi modifica agli 8 campi regolati (cold_chain_enabled, cold_chain_source_id, cold_chain_metric, temperature_min, temperature_max, temperature_unit, excursion_grace_minutes, cold_chain_grace_mode) scrive una entry in _audit_trail con actor, timestamp, snapshot precedente e nuovo. Un auditor può rispondere "chi ha spostato temperature_max da −18 a −12 il 3 marzo".

Ingestione delle letture

Due percorsi, stesso pipeline (stessi filtri, stessa dedup, stesso aggregator).

Percorso 1 — via machine events

Configura una event_source di tipo http / mqtt / opcua. Ogni evento con il campo temperature (o quello configurato in cold_chain_metric) attiva check_cold_chain_from_event.

Percorso 2 — ingestione diretta POST /api/v1/cold-chain/readings

Per sensori fuori dal pipeline di machine_events: logger Bluetooth, sonde portatili al ricevimento, registratori di trasporto che caricano in batch all'arrivo.

POST /api/v1/cold-chain/readings
{
  "source_id": "logger-bt-042",
  "asset_id": "65a...",
  "temperature": -12.4,
  "batch_id": "LOT-2026-03-A",
  "product_type": "vaccino mRNA"
}

Se ometti asset_id, il servizio risolve tutti gli asset con quel cold_chain_source_id e valuta ciascuno.

CC-L1: batch_id e product_type vengono propagati sulla deviazione generata. Un breach su un frigorifero che conteneva insulina alle 14:00 e yogurt alle 17:00 sono due storie di compliance diverse — l'asset è lo stesso, il lotto dentro ruota.

Modalità di grace — escalate vs suppress

escalate (default)

Ogni deviazione viene registrata dalla prima lettura. Adatto per food-service in cui ogni apertura di porta conta.

t=0s   Lettura fuori banda    -> Deviazione aperta (severity=warning)
t=5s   Lettura fuori banda    -> Update: sample_count=2, peak aggiornato
t=30s  Lettura in banda       -> Deviazione risolta

Risultato: una riga in _cold_chain_excursions con duration=0.5 min.

suppress (stile HACCP)

Le deviazioni transienti vengono perdonate. Si persistono solo quelle che superano il grace, e si aprono direttamente in critical. Adatto per farmaceutica e GDP dove il cry-wolf alerting distrugge il segnale.

t=0s    Lettura fuori banda    -> Buffer aperto (NESSUNA deviazione persistita)
t=30s   Lettura fuori banda    -> Buffer aggiorna sample_count + peak
t=2min  Lettura in banda       -> Buffer cancellato silenziosamente (0 deviazioni)

Se la deviazione dura abbastanza:

t=0s        Fuori banda         -> Buffer aperto
t=15min     Fuori banda         -> elapsed >= grace: promuove
                                   deviazione persistita con severity=critical,
                                   started_at=t=0s, duration=15min

Cambia modalità per asset via cold_chain_grace_mode. Il default escalate preserva il comportamento storico per asset precedenti al flag.

Journey / Leg — spedizioni refrigerate

CC-M6. Un journey è una spedizione end-to-end. Un leg è un segmento di custodia (magazzino poi camion poi centro di distribuzione poi farmacia). I passaggi di consegna tra leg sono i momenti di maggiore rischio nella catena del freddo e sono dove sorgono le domande di attribuzione ("di chi è il camion che si è scaldato?").

Modellare una spedizione

POST /api/v1/cold-chain/journeys
{
  "journey_code": "JRN-2026-03-001",
  "origin": "DC Madrid",
  "destination": "Farmacia Valencia",
  "batch_id": "LOT-42",
  "product_type": "insulina",
  "legs": [
    {"sequence": 0, "from_location": "DC Madrid",    "to_location": "Cross-dock"},
    {"sequence": 1, "from_location": "Cross-dock",   "to_location": "Camion 7", "asset_id": "65a..."},
    {"sequence": 2, "from_location": "Camion 7",     "to_location": "Farmacia"}
  ]
}

Ciclo di vita

stateDiagram-v2
  [*] --> planned
  planned --> in_transit: start_leg (prima volta)
  in_transit --> in_transit: start_leg / complete_leg
  in_transit --> delivered: ultimo leg completato
  planned --> cancelled
  in_transit --> cancelled
  • POST /api/v1/cold-chain/journeys/{id}/legs/{leg_id}/start — marca il leg come active e, alla prima attivazione, stampa actual_start_at sul journey. Attivazioni successive NON sovrascrivono la partenza originale.
  • POST /api/v1/cold-chain/journeys/{id}/legs/{leg_id}/complete — marca il leg come completed con ended_at.

Attribuzione automatica delle deviazioni

Quando check_excursion apre una nuova deviazione per un asset_id, il servizio cerca un journey in_transit con un leg active che referenzi quell'asset. Se lo trova, aggiunge l'excursion_id all'array legs.$.excursion_ids del leg. Fire-and-forget: un fallimento sul lato journey non interrompe mai il pipeline della catena del freddo.

L'audit risponde "quale leg di trasporto ha fallito?" senza join manuali tra collection.

Dashboard di stato

GET /api/v1/cold-chain/status restituisce, per asset, i tre numeri che un operatore legge a colpo d'occhio:

CampoSignificato
peak_deviationDi quanto è uscito dalla banda (unità dell'asset).
duration_minutesDa quanto tempo è fuori.
sample_countQuante letture sostengono la deviazione (CC-L3).

Un critical con 1 campione è un glitch; un critical con 20 campioni in 20 minuti è un evento reale. Il terzo numero è ciò che separa il triage reale dal rumore.

[
  {
    "id": "65a...",
    "name": "Freezer Unit A",
    "temperature_min": -22,
    "temperature_max": -16,
    "has_excursion": true,
    "peak_deviation": 3.2,
    "duration_minutes": 18.5,
    "sample_count": 37,
    "excursion": { "id": "...", "severity": "critical", "source_id": "src_fridge_01", "batch_id": "LOT-42" }
  }
]

Endpoint

MetodoPathCosa fa
POST/api/v1/cold-chain/readingsIngestione diretta con batch_id / product_type.
GET/api/v1/cold-chain/statusStato per asset + peak/duration/sample.
GET/api/v1/cold-chain/excursionsStorico con filtri asset_id, resolved.
POST/api/v1/cold-chain/journeysCrea journey + leg.
GET/api/v1/cold-chain/journeysLista, filtra per status e batch_id.
POST/api/v1/cold-chain/journeys/{id}/legsAggiunge un leg.
POST/api/v1/cold-chain/journeys/{id}/legs/{leg_id}/startMarca leg active.
POST/api/v1/cold-chain/journeys/{id}/legs/{leg_id}/completeMarca leg completed.

Scenari reali

1. Apertura breve di porta su freezer carne

Grace mode escalate, grace 15 min. Un operatore apre la porta per 40 secondi. La temperatura sale da −20 a −16.5 °C.

  • t=0s: deviazione aperta (warning, duration 0).
  • t=20s: update (sample_count=2, peak 3.5 °C).
  • t=40s: in-banda poi resolve. Duration 0.7 min.
  • Dashboard mostra peak=3.5, duration=0.7, sample_count=2. L'operatore scarta.

2. Spedizione di vaccini con breach al leg 2

Grace mode suppress, grace 60 min. Journey con 3 leg. Il camion resta imbottigliato nel traffico e la temperatura supera 8 °C per 75 minuti.

  • Leg 2 attivo, asset = "Camion 7".
  • t=0: buffer aperto (nessuna deviazione persistita).
  • t=60min: buffer supera il grace poi promuove. Deviazione critical con started_at = t=0, duration=60min, severity=critical, batch_id=LOT-42, product_type=insulina.
  • L'hook CC-M6 aggiunge excursion_id al leg 2. Audit risponde: "il breach è avvenuto sul leg Cross-dock poi Camion 7".

3. Sensore morto che invia 0 °C

Il sensor watchdog contrassegna src_fridge_05 come stale. Il reading pipeline riceve 0 °C (fuori banda contro un pavimento di −18). CC-H1 scarta la lettura alla prima riga di check_excursion. Nessuna deviazione fantasma.

Limitazioni e ipotesi

  • cold_chain_enabled=True richiede un cold_chain_source_id valido in _machine_event_sources (CC-M4). Se la sorgente viene eliminata successivamente, il servizio logga un warning ma non blocca; la validazione è un early-warning.
  • La flap window è fissa a 5 minuti. Regolabile solo nel codice (_COLD_CHAIN_FLAP_WINDOW_MINUTES).
  • suppress non si applica al flap-reopen: se una deviazione PERSISTITA risolve e ne appare una nuova nella flap window, riapre indipendentemente dalla modalità (la decisione sul rumore è già stata presa).
  • journey.status non si chiude automaticamente quando l'ultimo leg viene completato; richiede un PATCH esplicito a delivered.

Collection MongoDB per-tenant

CollectionContenuto
_cold_chain_excursionsDeviazioni persistite (schema completo con batch_id, product_type, source_id, sample_count, peak_deviation, duration_minutes).
_cold_chain_excursion_buffersBuffer volatili di modalità suppress. Cancellati al rientro in banda o alla promozione.
_cold_chain_journeysSpedizioni con leg embedded.
_audit_trailEntry fire-and-forget con action=asset_cold_chain_updated.

Findings chiusi in questa iterazione

IDCosa è stato chiuso
CC-H1Filtro di staleness prima del range check.
CC-H2Feed all'aggregator con source_system dedicato.
CC-H3source_id persistito sulla deviazione (propagato dal webhook).
CC-M1Flag cold_chain_grace_mode con modalità HACCP-style.
CC-M2Flap window di 5 min per il reopen di deviazioni consecutive.
CC-M3Endpoint POST /readings per ingestione senza machine_events.
CC-M4Validazione di cold_chain_source_id in asset create/update.
CC-M5Audit trail sulle mutazioni degli 8 campi regolati.
CC-M6Journey + Leg con attribuzione automatica.
CC-L1batch_id + product_type persistiti sulla deviazione.
CC-L2Range di excursion_grace_minutes allargato a 1 fino 1440.
CC-L3peak_deviation, duration_minutes, sample_count nello status.

Benefici chiave

  • Audit trail verificabile per HACCP, GDP, WHO TRS 961, FSMA.
  • Zero cry-wolf: staleness + flapping + modalità di grace rimuovono il rumore strutturale.
  • Attribuzione reale: ogni breach conosce lotto, prodotto e leg.
  • Inbox unificato: stesso inbox di manutenzione e qualità; l'operatore non cambia schermata.
  • Pharma-ready: grace fino a 24 h, modalità suppress, tracking batch, modello journey.

In questa pagina