Obsidean_VM/04-SIDEL/09 - SAE452 - Diet as Regul.../FB2120 - MasseliTCP/FB2120 - MasseliTCP Read - ...

23 KiB


Panoramica del Sistema

Il Function Block FB2120 MaselliTCP è stato sviluppato per leggere direttamente i valori inviati dai sensori Maselli UR62 o UR29 utilizzando il protocollo ADAM tramite un gateway 485-Ethernet. Il sistema utilizza TCP per migliorare la resilienza dei dati, sfruttando la gestione automatica delle ritrasmissioni e il mantenimento dell'ordine dei frame.

Vantaggi del Sistema

  • Comunicazione digitale diretta: Evita la doppia conversione DAC-ADC, mantenendo la piena risoluzione
  • Resilienza TCP: Gestione automatica delle ritrasmissioni e mantenimento dell'ordine dei dati
  • Sistema di riconnessione intelligente: Gestisce diversi timeout per ristabilire la connessione
  • Monitoraggio avanzato: Contatori per diversi tipi di errori e metriche di affidabilità
  • Compatibilità parallela: Può funzionare in parallelo al sistema ADAM esistente

Architettura del Sistema

@startuml
!define RECTANGLE class
!theme plain

package "Sensore Maselli" #E8F4F8 {
  [UR62/UR29] as sensor #4A90A4
}

package "Gateway 485-Ethernet" #E8F5E8 {
  [Waveshare RS485-TCP] as gateway #2E7D32
}

package "PLC Siemens S7-300" #E3F2FD {
  [FB2120 MaselliTCP] as fb #1565C0
  [TCON] as tcon #42A5F5
  [TRCV] as trcv #42A5F5  
  [TDISCON] as tdiscon #42A5F5
}

package "Rete Ethernet" #FFF3E0 {
  [TCP Connection] as tcp #FF8F00
}

sensor -right-> gateway : RS485\nProtocollo ADAM
gateway -right-> tcp : TCP Trasparente
tcp -right-> fb : Frame #XX12.345CKCR
fb -down-> tcon : Connessione
fb -down-> trcv : Ricezione
fb -down-> tdiscon : Disconnessione

note right of gateway #E8F5E8
  **Gateway trasparente 485-Ethernet**
  • Converte RS485 in TCP
  • Server TCP sulla porta 8899
  • Comunicazione bidirezionale
end note

note right of fb #E3F2FD
  **FB2120 gestisce:**
  • State machine TCP
  • Buffer circolare ottimizzato
  • Parsing frame ADAM
  • Calcolo Brix digitale
  • Gestione errori avanzata
end note
@enduml
@startuml

skinparam cloud {
  BackgroundColor Gold
  BorderColor Orange
}

' Definición de los componentes con colores
component "🔌 UR29" as Meter <<meter>> #LightBlue {
  [RS485] as MeterRS485 #SkyBlue
}

component "🌐 Gateway RS485-Ethernet" as Gateway <<gateway>> #LightGreen {
  [RS485] as GatewayRS485 #SkyBlue
  [Ethernet] as GatewayEth #LightYellow
}

component "🖥️ PLC 315F 2PN/DP" as PLC <<plc>> #LightBlue {
  [PN] as PLCEth #LightYellow
  [FB2120] as FB2120 #LightPink
  [DB2120] as DB2120 #LightPink
}

' Fuente de alimentación con estilo
cloud "⚡ Alimentatore\n24V DC" as PowerSupply #Gold

' Conexiones de datos con colores y etiquetas mejoradas
MeterRS485 -[#Blue,thickness=3]-> GatewayRS485 : "📡 RS485\nADAM Protocol"
GatewayEth -[#Green,thickness=3]-> PLCEth : "🌐 Ethernet\nTCP Trasparente"

' Conexión interna del PLC
PLCEth -[#Purple,thickness=2]-> FB2120 : "Comm TCP"
FB2120 -[#Purple,thickness=2]-> DB2120 : "Dati"

' Conexiones de alimentación
PowerSupply -[#Red,dashed,thickness=2]-> Meter : "24V DC"
PowerSupply -[#Red,dashed,thickness=2]-> Gateway : "24V DC"
PowerSupply -[#Red,dashed,thickness=2]-> PLC : "24V DC"

' Notas explicativas con colores
note right of Meter #LightBlue
  **Reflectometer**
  • Protocollo: Transparent TCP
  • Interfaccia: RS485
  • Velocità: 115200 bps
end note

note top of Gateway #LightGreen
  **Gateway di Comunicazione**
  • Conversione RS485 ↔ Ethernet
  • Protocollo: TCP Trasparente
  • IP: 10.1.33.18
end note

note left of PLC #LightBlue
  **PLC Siemens 315**
  • CPU 315-2DP
  • Interfaccia Ethernet
  • FB2120: Function Block comunicazione
  • DB2120: Data Block instanza
  • IP: 10.1.33.11
end note

' Título del diagrama
title **Sistema di Comunicazione Industriale**\n//Contatore → Gateway → PLC//

@enduml
@startuml
!theme toy

' Layout horizontal para enfatizar flujo de datos
left to right direction

skinparam cloud {
  BackgroundColor Gold
  BorderColor Orange
}

' Definición de los componentes con colores
component "🔌 UR29" as Meter <<meter>> #LightBlue {
  [RS485] as MeterRS485 #SkyBlue
}

component "🌐 Gateway RS485-Ethernet" as Gateway <<gateway>> #LightGreen {
  [RS485] as GatewayRS485 #SkyBlue
  [Ethernet] as GatewayEth #LightYellow
}

component "🖥️ PLC 315F 2PN/DP" as PLC <<plc>> #LightBlue {
  [PN] as PLCEth #LightYellow
  [FB2120] as FB2120 #LightPink
  [DB2120] as DB2120 #LightPink
}

' Alimentación minimalista en la parte superior
note top #Gold : **⚡ Alimentazione 24V DC**\n//Tutti i componenti//

' CONEXIONES DE DATOS PRINCIPALES - MUY VISIBLES
MeterRS485 -[#Blue,thickness=5]-> GatewayRS485 : "📡 **RS485**\n//ADAM Protocol//\n**115200 bps**"
GatewayEth -[#Green,thickness=5]-> PLCEth : "🌐 **ETHERNET**\n//TCP Trasparente//\n**10.1.33.18 → 10.1.33.11**"

' Conexiones internas del PLC con flujo claro
PLCEth -[#Purple,thickness=4]-> FB2120 : "**TCP Data**"
FB2120 -[#Purple,thickness=4]-> DB2120 : "**Memory**"

' Indicadores de dirección de flujo
note bottom of Meter #LightCyan : **📤 TX Data**
note bottom of PLC #LightCyan : **📥 RX Data**

' Notas técnicas compactas
note right of Gateway #LightGreen
  **Gateway Configuration**
  • RS485 ↔ Ethernet Bridge
  • Transparent TCP Protocol
  • IP: 10.1.33.18
  • Port: Configurable
end note

' Título enfocado en comunicación
title **🔗 Flusso Dati di Comunicazione Industriale**\n//UR29 Reflectometer → TCP Gateway → Siemens PLC//

@enduml

State Machine Principale

Il FB2120 utilizza una state machine principale per gestire la connessione TCP:

@startuml
!theme plain

[*] --> IDLE : Sistema avviato

IDLE : Entry: xConnected = FALSE
IDLE : Do: Retry delay attivo
IDLE --> CONNECT : xEnable = TRUE AND tonRetryDelay.Q

CONNECT : Entry: Preparazione connessione
CONNECT : Do: iCyclesToWait + 1
CONNECT --> WAIT_CONNECT : iCyclesToWait >= 2

WAIT_CONNECT : Entry: xTconReq = TRUE
WAIT_CONNECT : Do: TCON attivo, tonConnTimeout attivo
WAIT_CONNECT --> READING : TCON.DONE
WAIT_CONNECT --> DISCONNECT : TCON.ERROR OR tonConnTimeout.Q
WAIT_CONNECT --> DISCONNECT : NOT xEnable

READING : Entry: xConnected = TRUE
READING : Do: TRCV attivo, lettura frame
READING : Do: Reading state machine attiva
READING --> DISCONNECT : TRCV.ERROR OR tonNoDataTimeout.Q
READING --> DISCONNECT : NOT xEnable

DISCONNECT : Entry: xTdisconReq = TRUE
DISCONNECT : Do: TDISCON attivo
DISCONNECT --> IDLE : TDISCON.DONE OR TDISCON.ERROR

note right of READING
  Stato più complesso con
  sub-state machine per
  la gestione della lettura
end note
@enduml

Reading State Machine

Durante lo stato READING, una sub-state machine gestisce la lettura ottimizzata dei frame:

@startuml
!theme plain

[*] --> ACTIVE_READING : Ingresso in READING

state "Fase Inizializzazione" as INIT {
  ACTIVE_READING : Entry: xTrcvTimeToCall = TRUE
  ACTIVE_READING : Do: Lettura intensiva ogni ciclo
  ACTIVE_READING : Do: Accumulo diAccumCycleMs
  ACTIVE_READING --> COMPLETED : Frame valido ricevuto
  
  COMPLETED : Entry: xTrcvTimeToCall = FALSE
  COMPLETED : Do: Calcolo intervallo frame
  COMPLETED : Do: Reset diAccumCycleMs = 0
  COMPLETED --> ACTIVE_READING : xInitPhaseFinish = FALSE
}

state "Fase Normale" as NORMAL {
  ACTIVE_READING2 : Entry: xTrcvTimeToCall = TRUE
  ACTIVE_READING2 : Do: Lettura intensiva
  ACTIVE_READING2 --> PAUSED : Troppo tempo al prossimo frame
  ACTIVE_READING2 --> COMPLETED2 : Frame valido ricevuto
  
  PAUSED : Entry: xTrcvTimeToCall = FALSE
  PAUSED : Do: Attesa finestra frame
  PAUSED --> ACTIVE_READING2 : Vicino al prossimo frame
  
  COMPLETED2 : Entry: Elaborazione frame
  COMPLETED2 : Do: Aggiornamento statistiche
  COMPLETED2 --> PAUSED : Completato
}

INIT --> NORMAL : xInitPhaseFinish = TRUE

note right of INIT
  Durante l'inizializzazione
  legge continuamente per
  sincronizzarsi con il flusso
end note

note right of NORMAL
  In funzionamento normale
  ottimizza la lettura con
  finestre temporali
end note
@enduml

Formato Frame ADAM

Il protocollo ADAM utilizza frame di 12 byte fissi:

@startuml
!theme plain

package "Frame ADAM - 12 Bytes" {
  rectangle "Byte 0\n#" as b0
  rectangle "Byte 1-2\nAdam ID" as b12
  rectangle "Byte 3-4\nXX" as b34
  rectangle "Byte 5\n." as b5
  rectangle "Byte 6-8\nYYY" as b678
  rectangle "Byte 9-10\nChecksum" as b910
  rectangle "Byte 11\nCR" as b11
}

b0 -right-> b12
b12 -right-> b34  
b34 -right-> b5
b5 -right-> b678
b678 -right-> b910
b910 -right-> b11

note bottom of b0
  Start marker '#'
  (0x23)
end note

note bottom of b12
  Device ID
  "01" = Dispositivo 1
  "02" = Dispositivo 2
end note

note bottom of b34
  Parte intera
  corrente mA
  "02" = 2mA
end note

note bottom of b5
  Separatore decimale
  '.' (0x2E)
end note

note bottom of b678
  Parte decimale
  corrente mA
  "145" = 0.145mA
end note

note bottom of b910
  Checksum esadecimale
  Somma bytes 0-8
  modulo 256
end note

note bottom of b11
  Carriage Return
  (0x0D)
end note
@enduml

Esempio Frame: #0102.145CkCr

  • #: Start marker
  • 01: Device ID = 1
  • 02.145: Corrente = 2.145 mA
  • Ck: Checksum calcolato
  • Cr: Carriage return

Sequenza di Comunicazione TCP

@startuml
!theme plain

participant "FB2120" as fb
participant "TCON" as tcon
participant "Gateway" as gw
participant "TRCV" as trcv
participant "Sensore" as sensor

== Fase Connessione ==
fb -> tcon : REQ=TRUE, ID=W#16#10
tcon -> gw : TCP SYN a IP:porta
gw -> tcon : TCP SYN-ACK
tcon -> fb : DONE=TRUE

== Fase Lettura ==
loop Lettura Continua
  sensor -> gw : Frame ADAM via RS485
  gw -> fb : Frame TCP trasparente
  fb -> trcv : EN_R=TRUE (se xTrcvTimeToCall)
  trcv -> fb : NDR=TRUE + dati
  fb -> fb : Parsing frame, validazione
  fb -> fb : Calcolo Brix, aggiornamento FIFO
end

== Gestione Errori ==
alt Errore di comunicazione
  trcv -> fb : ERROR=TRUE
  fb -> fb : Analisi errore, riconnessione
end

== Disconnessione ==
fb -> tdiscon : REQ=TRUE
tdiscon -> gw : TCP FIN
gw -> tdiscon : TCP FIN-ACK  
tdiscon -> fb : DONE=TRUE
@enduml

Gestione Buffer Circolare

Il sistema utilizza un buffer circolare per gestire efficacemente i frame ricevuti:

@startuml
!theme plain

package "Buffer Circolare - 256 Bytes" {
  rectangle "Circular Buffer\naCircBuffer[0..255]" as buffer
  rectangle "Write Pointer\niWritePtr" as wptr
  rectangle "Read Pointer\niReadPtr" as rptr
  rectangle "Data Count\niDataInBuffer" as count
}

package "Frame Processing" {
  rectangle "Ricerca '#'" as search
  rectangle "Validazione Formato" as validate
  rectangle "Parsing Dati" as parse
  rectangle "Calcolo Checksum" as checksum
}

buffer -down-> search : Scan per start marker
search -right-> validate : Frame trovato
validate -right-> parse : Formato OK
parse -right-> checksum : Dati estratti
checksum -up-> buffer : Frame processato, avanzamento puntatori

note right of buffer
  Buffer di 256 byte per gestire
  ricezione asincrona e parsing
  efficiente dei frame
end note

note bottom of search
  Ricerca sequenziale del
  carattere '#' per inizio frame
end note
@enduml

Codici di Errore

Il sistema utilizza codici di errore gerarchici (priorità crescente):

Codice Priorità Descrizione
0 - Nessun errore
1 Bassa Errore checksum
2 Bassa Formato frame non valido
3 Bassa Caratteri non numerici
4 Media Dati fuori range (4-20mA)
5 Media Frame consecutivi errati
6 Media Frame persi rilevati
7 Alta Buffer TRCV troppo piccolo
8 Alta Errore comunicazione TRCV
9 Critica Timeout ricezione dati
10 Critica Timeout connessione
11 Critica Connessione TCP fallita
12 Critica Connessione TCP terminata

Parametri di Configurazione

Input Parameters

  • xEnable: Abilitazione comunicazione
  • aRemoteIP: IP del gateway [10,1,33,100]
  • iRemotePort: Porta TCP (default: 8899)
  • rBrixMax: Valore Brix massimo a 20mA (default: 80.0)
  • wConnectionID: ID connessione TCP
  • xEnableChecksum: Abilitazione validazione checksum
  • rFramesPerSecond: Frame attesi per secondo (default: 3.0)
  • iDeviceID: ID dispositivo da validare (0=disabilitato)

Output Parameters

  • xConnected: Stato connessione
  • xDataValid: Pulse per nuovo dato valido
  • xError: Errore attivo
  • iErrorCode: Codice errore strutturato
  • rCurrentMA: Valore corrente in mA
  • rBrixValue: Valore Brix calcolato
  • diTimeBetweenFramesMs: Tempo tra frame consecutivi

Calcolo Brix

La conversione dalla corrente 4-20mA al valore Brix segue la formula:

rBrixValue = (rCurrentMA - 4.0) * rBrixMax / 16.0

Esempio:

  • Corrente ricevuta: 12.0 mA
  • Brix Max configurato: 80.0
  • Calcolo: (12.0 - 4.0) * 80.0 / 16.0 = 40.0 Brix

Sistema FIFO (Debug Mode)

Quando xEnableFifoDebug = TRUE, il sistema mantiene le ultime 10 letture:

  • arBrixHistory[0..9]: Valori Brix (newest → oldest)
  • auiTimeHistory[0..9]: Timestamp (newest → oldest)

I dati vengono spostati ad ogni nuovo valore valido, con il più recente sempre in posizione [0].

Vantaggi Implementativi

Ottimizzazioni v1.8

  • Buffer ottimizzato: Ridotto da 128 a 23 byte
  • State machine migliorata: Fase di inizializzazione separata
  • Lettura adattiva: Finestre temporali ottimizzate
  • Gestione errori avanzata: Codici gerarchici strutturati
  • Sincronizzazione migliorata: Flag xInitPhaseFinish

Resilienza del Sistema

  • Riconnessione automatica: Gestione intelligente dei timeout
  • Buffer circolare: Gestione efficace dei dati asincroni
  • Validazione multipla: Formato, checksum, range dati
  • Statistiche dettagliate: Monitoraggio prestazioni e affidabilità

Configurazione Hardware Richiesta

Gateway Suggerito

  • Waveshare Industrial Grade Serial Server RS232/485 to WiFi And Ethernet
  • Configurazione: Server TCP trasparente
  • Porta: 8899 (configurabile)
  • Modalità: Trasparente RS485 → TCP

PLC Requirements

  • Siemens S7-300 con supporto TCP/IP
  • Function Blocks: TCON, TRCV, TDISCON (standard Siemens)
  • Memoria: ~2KB per istanza FB
  • Nessuna configurazione NetPro richiesta

Utilizzo Pratico

  1. Istanziare FB2120 con parametri specifici del sensore
  2. Configurare IP/Porta del gateway nel codice
  3. Impostare xEnable = TRUE per avviare la comunicazione
  4. Monitorare xDataValid per nuovi dati
  5. Verificare iErrorCode per diagnostica
  6. Leggere rBrixValue e rCurrentMA** per i valori processati

Il sistema è progettato per funzionare in parallelo ai sistemi ADAM esistenti, offrendo una soluzione digitale più precisa e affidabile per la lettura dei sensori Maselli.

Reading State Machine e Burst-Reading

Durante lo stato READING, una sub-state machine gestisce la lettura ottimizzata dei frame con la tecnica del burst-reading per ottimizzare le risorse del PLC:

@startuml
!theme plain

[*] --> ACTIVE_READING : Ingresso in READING

state "Fase Inizializzazione" as INIT {
  ACTIVE_READING : Entry: xTrcvTimeToCall = TRUE
  ACTIVE_READING : Do: Lettura intensiva ogni ciclo
  ACTIVE_READING : Do: Accumulo diAccumCycleMs
  ACTIVE_READING : Do: Ricerca sincronizzazione frame
  ACTIVE_READING --> COMPLETED : Frame valido ricevuto
  
  COMPLETED : Entry: xTrcvTimeToCall = FALSE
  COMPLETED : Do: Calcolo intervallo frame iniziale
  COMPLETED : Do: Aggiornamento stTiming.diEstFrameMs
  COMPLETED : Do: Reset diAccumCycleMs = 0
  COMPLETED --> ACTIVE_READING : xInitPhaseFinish = FALSE
  COMPLETED --> INIT_COMPLETE : Buffer sincronizzato E tempo > 2*intervallo
  
  INIT_COMPLETE : Entry: xInitPhaseFinish = TRUE
  INIT_COMPLETE : Do: Transizione a funzionamento normale
}

state "Fase Funzionamento Normale" as NORMAL {
  ACTIVE_READING_BURST : Entry: xTrcvTimeToCall = TRUE
  ACTIVE_READING_BURST : Do: Lettura burst intensiva
  ACTIVE_READING_BURST : Do: diReadingStartMs = diAccumCycleMs
  ACTIVE_READING_BURST --> PAUSED : (diNextFrameMs - diAccumCycleMs) > iMaxActiveReadingMs
  ACTIVE_READING_BURST --> COMPLETED_NORMAL : Frame valido ricevuto
  
  PAUSED : Entry: xTrcvTimeToCall = FALSE
  PAUSED : Do: Risparmio risorse CPU
  PAUSED : Do: Attesa finestra frame calcolata
  PAUSED --> ACTIVE_READING_BURST : (diNextFrameMs - diAccumCycleMs) <= iPreReadOffsetMs
  
  COMPLETED_NORMAL : Entry: xTrcvTimeToCall = FALSE
  COMPLETED_NORMAL : Do: Elaborazione frame completa
  COMPLETED_NORMAL : Do: Aggiornamento statistiche avanzate
  COMPLETED_NORMAL : Do: Smoothing diEstFrameMs
  COMPLETED_NORMAL : Do: Rilevamento frame persi
  COMPLETED_NORMAL --> PAUSED : Sempre
}

INIT --> NORMAL : xInitPhaseFinish = TRUE

note right of INIT
  **Fase Inizializzazione:**
  - Lettura continua per sincronizzazione
  - Apprendimento timing frame
  - Pulizia buffer da dati residui
  - Stima intervallo frame iniziale
end note

note right of NORMAL
  **Burst-Reading Ottimizzato:**
  - Lettura intensiva solo quando necessario
  - Pausa durante attese lunghe
  - Risparmio risorse CPU del 60-80%
  - Finestre temporali adattive
end note

note bottom of PAUSED
  **Ottimizzazione Risorse:**
  - TRCV non chiamato
  - CPU dedicata ad altri task
  - Calcolo dinamico finestra lettura
end note
@enduml

Sequenza Dettagliata Burst-Reading

@startuml
!theme plain
title Sequenza Burst-Reading - Ottimizzazione Risorse PLC

actor "Ciclo PLC" as plc
participant "iReadingState\nMachine" as rsm
participant "TRCV" as trcv
participant "Buffer\nCircolare" as buffer
participant "Gateway\nTCP" as gateway
participant "Sensore\nMaselli" as sensor

== Fase Inizializzazione (xInitPhaseFinish = FALSE) ==

loop Sincronizzazione Iniziale
  plc -> rsm : Ciclo PLC (ogni 10-50ms)
  rsm -> rsm : iReadingState = 0 (ACTIVE_READING)
  rsm -> rsm : xTrcvTimeToCall = TRUE
  rsm -> trcv : TRCV(EN_R=TRUE) - **Lettura Intensiva**
  
  alt Frame disponibile
    sensor -> gateway : Frame ADAM #01XX.YYYCKHR
    gateway -> trcv : TCP Data
    trcv -> rsm : NDR=TRUE + 12 bytes
    rsm -> buffer : Copia in buffer circolare
    rsm -> rsm : Parsing e validazione frame
    
    alt Frame valido
      rsm -> rsm : iReadingState = 1 (COMPLETED)
      rsm -> rsm : Calcolo primo intervallo
      rsm -> rsm : diAccumCycleMs = 0 (reset timer)
      rsm -> rsm : iConsecutiveGoodFrames++
      
      alt Sincronizzazione completa
        rsm -> rsm : xInitPhaseFinish = TRUE
        rsm -> rsm : **Transizione a Burst-Reading**
      end
    end
  else Nessun frame
    rsm -> rsm : Continua accumulo diAccumCycleMs
  end
end

== Fase Funzionamento Normale (Burst-Reading) ==

loop Ciclo Ottimizzato
  plc -> rsm : Ciclo PLC
  rsm -> rsm : Calcolo finestra frame
  
  alt Finestra Attiva (vicino al frame atteso)
    rsm -> rsm : iReadingState = 0 (ACTIVE_READING_BURST)
    rsm -> rsm : xTrcvTimeToCall = TRUE
    rsm -> trcv : **TRCV(EN_R=TRUE) - Burst Intensivo**
    
    note right : **Lettura Burst:**\nTRCV chiamato ogni ciclo\nper massima reattività
    
    alt Frame ricevuto
      trcv -> rsm : NDR=TRUE + frame data
      rsm -> rsm : iReadingState = 1 (COMPLETED_NORMAL)
      rsm -> rsm : Elaborazione completa frame
      rsm -> rsm : Aggiornamento statistiche
      rsm -> rsm : Smoothing diEstFrameMs
      rsm -> rsm : diNextFrameMs = nuovo calcolo
      rsm -> rsm : iReadingState = 2 (PAUSED)
    else Timeout burst
      rsm -> rsm : iReadingState = 2 (PAUSED)
      note right : Evita lettura continua\neccessiva
    end
    
  else Finestra Inattiva (troppo presto per il prossimo frame)
    rsm -> rsm : iReadingState = 2 (PAUSED)
    rsm -> rsm : xTrcvTimeToCall = FALSE
    rsm -> trcv : **TRCV(EN_R=FALSE) - Risparmio CPU**
    
    note right : **Modalità Pausa:**\nTRCV non chiamato\nCPU disponibile per altri task\nRisparmio energetico
    
    rsm -> rsm : Calcolo: (diNextFrameMs - diAccumCycleMs)
    rsm -> rsm : Se <= iPreReadOffsetMs → Attiva burst
  end
end

note over plc, sensor
  **Vantaggi Burst-Reading:**
  • Risparmio CPU: 60-80% in modalità PAUSED
  • Reattività massima durante burst
  • Sincronizzazione automatica con sensore
  • Adattamento dinamico agli intervalli frame
  • Riduzione interferenze con altri task PLC
end note
@enduml

Fase di Inizializzazione Dettagliata

La fase di inizializzazione è critica per sincronizzare il PLC con il flusso dati del sensore Maselli:

@startuml
!theme plain
title Sequenza Fase di Inizializzazione - Sincronizzazione con Sensore

participant "FB2120" as fb
participant "Buffer\nCircolare" as buffer
participant "Frame\nParser" as parser
participant "Timing\nEngine" as timing
participant "Statistiche" as stats

== Avvio Inizializzazione ==
fb -> fb : xInitPhaseFinish = FALSE
fb -> fb : iReadingState = 0 (ACTIVE_READING)
fb -> fb : diAccumCycleMs = 0
fb -> fb : iConsecutiveGoodFrames = 0
fb -> timing : diInitPhaseStartMs = diAccumCycleMs

== Raccolta Dati Iniziale ==
loop Lettura Continua (xTrcvTimeToCall = TRUE)
  fb -> buffer : Ricezione dati TCP asincroni
  buffer -> buffer : Accumulo bytes in buffer circolare
  buffer -> parser : Ricerca pattern #XX.YYY
  
  alt Buffer contiene dati residui/parziali
    parser -> buffer : Pulizia dati non validi
    parser -> buffer : Allineamento a boundary frame
    note right : Rimozione garbage data\nda precedenti connessioni
  end
  
  alt Frame completo trovato
    parser -> parser : Validazione formato
    alt Frame strutturalmente valido
      parser -> fb : Frame OK + dati estratti
      fb -> fb : iConsecutiveGoodFrames++
      fb -> fb : Calcolo intervallo grezzo
      fb -> timing : Aggiornamento diEstFrameMs iniziale
      fb -> stats : Incremento contatori successo
      
      alt Prima sincronizzazione (frames >= 2)
        fb -> timing : diEstFrameMs = intervallo_misurato
        fb -> timing : Calcolo finestre ottimizzate
        note right : **Parametri Calcolati:**\ndiFrameIntervalMs = 1000/rFramesPerSecond\niPreReadOffsetMs = intervallo/6\niMaxActiveReadingMs = intervallo/3
      end
    else Frame non valido
      parser -> stats : Incremento errori formato
      parser -> buffer : Scarta frame e continua
    end
  end
  
  alt Condizioni per completamento inizializzazione
    fb -> fb : Verifica: diAccumCycleMs > (diFrameIntervalMs * 2)
    fb -> fb : Verifica: stBuffer.iRxLength = 12 (buffer pulito)
    fb -> fb : Verifica: iConsecutiveGoodFrames >= 2
    
    alt Tutte le condizioni soddisfatte
      fb -> fb : **xInitPhaseFinish = TRUE**
      fb -> timing : Finalizzazione parametri timing
      fb -> fb : Transizione a funzionamento normale
      
      note over fb : **Inizializzazione Completata:**\n• Buffer sincronizzato con frame\n• Timing affidabile stabilito\n• Sistema pronto per burst-reading
    end
  end
end

== Parametri Calcolati Automaticamente ==
timing -> timing : diFrameIntervalMs = 1000.0 / rFramesPerSecond
timing -> timing : iPreReadOffsetMs = MAX(20, diFrameIntervalMs/6)
timing -> timing : iMaxActiveReadingMs = MAX(50, diFrameIntervalMs/3)
timing -> timing : diNextFrameMs = diEstFrameMs

note over timing
  **Calcoli Adattivi:**
  • Intervallo teorico da configurazione
  • Intervallo reale da misurazione
  • Finestre dinamiche per ottimizzazione
  • Sicurezza con valori minimi
end note
@enduml