Vai al contenuto

Architettura Backend Macchine

Documentazione tecnica del backend Go per la gestione dei tipi macchina (pivot, lineare, motopompa, rotolone, ippodromo).

Indice


Pattern comune

Ogni tipo macchina segue lo stesso schema architetturale, implementato come package Go separato in internal/<tipo>/. Il framework condiviso in internal/shared/ gestisce il flusso generico tramite GenericDataHandler, parametrizzato con tipi e funzioni specifiche per macchina.

Endpoint

Ogni tipo macchina espone due endpoint HTTP (POST):

Endpoint Handler Descrizione
/<tipo>Data DataHandler Ricezione dati dalla macchina (variabili/costanti)
/<tipo>Update UpdateHandler Invio comandi dall'app web alla macchina

Esiste inoltre un endpoint condiviso POST /cleanCommands (autenticato) che invoca CleanCommands per tutti i tipi macchina.

Modello dati

Ogni tipo macchina utilizza un insieme di tabelle DB con prefisso convenzionale:

Tabella Prefisso esempio (lineare) Contenuto
<tipo>_stato lin_stato / lst_ Variabili real-time (telemetria)
<tipo>_alarm lin_alarm / lal_ Flag di allarme
<tipo>_costanti lin_costanti / lco_ Configurazione macchina
<tipo>_portale lin_portale / lpo_ Comandi pendenti (bidirezionale)
machine_log plo_ (condiviso) Log attivita per tutte le macchine

Ciclo di vita di una richiesta

1. Macchina -> Server (DataHandler)

Macchina --[POST JSON]--> DataHandler ---> GenericDataHandler
                                              |
                            +-----------------+-----------------+
                            |                                   |
                       [Variables]                         [Constants]
                            |                                   |
                     saveVariables()                     saveConstants()
                            |                                   |
                     INSERT stato + alarm              UPSERT costanti
                     UPDATE portale (reset)             UPSERT portale
                     UPDATE machine_log                 (+ programmi/settori)
                            |                                   |
                            +-----------------+-----------------+
                                              |
                               [Check asincroni, timeout 10s]
                                  checkTurnOff()
                                  checkNotifications()
                                              |
                                      getCommands()
                                              |
                                    Risposta alla macchina

Risposta alla macchina

getCommands legge <tipo>_portale cercando record con aggiornato IN ('1', '3'):

aggiornamento Significato Contenuto risposta
0 Nessun comando pendente {"aggiornamento": "0"}
1 Comandi da applicare Tutti i campi modificabili (costanti, comandi, settori...)
3 Conferma ricezione Solo aggiornamento + TimeStamp

2. App Web -> Server (UpdateHandler)

App Web --[POST form]--> UpdateHandler
                             |
                    Parse machine_id + aggiornato + dati
                             |
                  +----------+----------+
                  |          |          |
             aggiornato  aggiornato  default
               = "1"    = "0"/"3"     -> 400
                  |          |
           Parse JSON    UPDATE semplice
           Build query   (solo aggiornato + timestamp)
           dinamica
                  |
           UPDATE portale
           (comandi + costanti + settori)

L'UpdateHandler costruisce la query SQL dinamicamente: solo i campi presenti nel JSON vengono aggiornati, tramite un strings.Builder con parametri posizionali incrementali.

3. Pulizia comandi scaduti (CleanCommands)

Eseguito periodicamente via cron. Resetta aggiornato = '0' per tutti i record pendenti la cui timestamp e piu vecchia di una soglia configurata, evitando che comandi obsoleti vengano inviati dopo una lunga disconnessione della macchina.

4. Check asincroni

Eseguiti in goroutine separate con timeout 10s dopo il salvataggio delle variabili:

checkTurnOff (opzionale): - Legge gli ultimi 2 record da <tipo>_stato - Se RunCiclo passa da 1 a 0, invia notifica di spegnimento al portale esterno - Parametro tip: identifica il tipo macchina (P = pivot, L = lineare, R = rotolone, ecc.)

checkNotifications: - Legge gli ultimi 2 record da <tipo>_alarm - Per ogni flag di allarme, se passa da 0 a 1, invia notifica al servizio AWS - Ogni tipo macchina ha action code distinti (vedi tabella sotto)


Struttura dei package

Ogni package macchina segue questa struttura:

internal/<tipo>/
    handler.go    -- DataHandler + UpdateHandler
    saves.go      -- saveVariables + saveConstants
    commands.go   -- getCommands + CleanCommands
    checks.go     -- checkTurnOff + checkNotifications
    types.go      -- struct Go: variables, constants, alarms, updateData

handler.go

  • DataHandler: istanzia GenericDataHandlerParams con i tipi e le funzioni del package, poi delega a shared.GenericDataHandler
  • UpdateHandler: endpoint per l'app web, costruisce query UPDATE dinamica su <tipo>_portale

saves.go

  • saveVariables: in una transazione DB, inserisce stato + allarmi, resetta comandi pendenti confermati (aggiornato=3), aggiorna machine_log
  • saveConstants: in una transazione DB, upsert costanti + eventuale gerarchia (programmi, settori), upsert portale con valori di default

commands.go

  • getCommands: query su <tipo>_portale WHERE aggiornato IN ('1','3'), restituisce map[string]any serializzato come JSON nella risposta
  • CleanCommands: UPDATE batch per reset comandi scaduti

checks.go

  • checkTurnOff: confronto ultimi 2 RunCiclo, notifica spegnimento
  • checkNotifications: confronto ultimi 2 set allarmi, notifica transizioni 0->1

types.go

  • variables: campi telemetria (embed shared.BaseMachineData)
  • constants: campi configurazione (embed shared.BaseMachineData)
  • alarms: struct per pgx scan degli allarmi (tag db:)
  • updateData: struct per parsing JSON dall'UpdateHandler (campi puntatore per distinguere assenti da zero-value)

Confronto tra tipi macchina

Struttura file

File Pivot Lineare Motopompa Rotolone Ippodromo
handler.go (Data + Update) si + si si + si si + si si + si si (stub)
saves.go si si si si -
commands.go si si si si stub
checks.go si si si si -
types.go si si si si -

Strategia di storage

Tipo Variabili Costanti Note
Pivot Colonne individuali Colonne individuali Schema classico
Lineare Colonne individuali Colonne + sotto-tabelle Programmi/settori/gun in tabelle dedicate
Motopompa JSON blob (mvr_data) JSON blob (mco_data) Schema semplificato
Rotolone JSON blob (rst_data) Colonne + sotto-tabelle Ibrido
Ippodromo N/A N/A Solo stub, implementazione incompleta

Funzionalita opzionali

Feature Pivot Lineare Motopompa Rotolone Ippodromo
CheckTurnOff si si - si -
PreprocessInput - - - si -
PutSpace - - - - -

Action code notifiche

Tipo Allarmi Fine ciclo Spegnimento
Pivot 4 6 trigger tip=P
Lineare 12 14 trigger tip=L
Motopompa 15 (error), 16 (warn), 17 (spegnimento) - via notifica
Rotolone 8 10 trigger tip=R

Dettagli per tipo

Pivot

Macchina irrigatrice a pivot centrale. Gestione settori angolari con angle boost e scheduler fino a 15 blocchi.

  • Tabelle: pivot_stato, pivot_alarm, pivot_portale
  • Comandi specifici: startAvanti, startIndietro, stop, stopInSlotAngle
  • Costanti notevoli: 12 settori angolari (SectorPivotEnd), fertirrigazione avanti/indietro, scheduler (Day/Hour/Min Start/End x 15 blocchi)
  • Allarmi: 7 flag (emergency line, encoder, comunicazione, pressione, fine ciclo, SIS, fuori campo)

Lineare

Macchina irrigatrice a movimento lineare. Gestione multi-programma con settori lineari e endgun.

  • Tabelle: lin_stato, lin_alarm, lin_costanti, lin_programmi, lin_settori, lin_settori_gun, lin_portale
  • Comandi specifici: startAvanti, startIndietro, stop
  • Costanti notevoli: fino a 10 programmi, ciascuno con fino a 20 settori e 10 settori gun per gun (2 gun), posizioni cambio idrante
  • Allarmi: 39 flag (inclusi opzionali Stonex GPS, encoder guida GA/GI)

Motopompa

Motopompa a controllo remoto. Schema dati semplificato con storage JSON.

  • Tabelle: motop_variabili (JSON), motop_costanti (JSON), motop_portale
  • Comandi specifici: start, stop
  • Costanti notevoli: regolazione motore, set point velocita/pressione, start ritardato, ore/minuti di lavoro STOP
  • Allarmi: estratti dinamicamente dal JSON (AllarmiAttivi_1..4), 20 error + 20 warn

Rotolone

Macchina irrigatrice a rotolone (avvolgitubo). Supporta preprocessamento input per JSON malformato.

  • Tabelle: roto_stato (JSON), roto_alarm, roto_costanti + sotto-tabelle, roto_portale
  • Comandi specifici: start, stop
  • PreprocessInput: regex per correggere JSON malformato dalla macchina
  • Allarmi: 17 flag (emergenza, pressione, avvolgimento, fine ciclo, fuori campo, comunicazione)

Ippodromo

Implementazione stub. Solo DataHandler (senza logica di salvataggio) e CleanCommands.

  • Da completare con saves.go, types.go, checks.go, UpdateHandler

Framework condiviso (internal/shared)

GenericDataHandler

Funzione generica parametrizzata con GenericDataHandlerParams[V, C]:

type GenericDataHandlerParams[V, C baseMachineDataInt] struct {
    W, R                                    // http writer/request
    MachineType                             // tipo macchina (P/L/M/R/I)
    SaveVariables    saveFunc[V]            // obbligatorio
    SaveConstants    saveFunc[C]            // obbligatorio
    GetCommands      getCommandsFunc        // obbligatorio
    CheckTurnOff     checkTurnOffFunc       // opzionale
    CheckNotifications checkNotificationsFunc // obbligatorio
    PreprocessInput  preprocessInputFunc    // opzionale
    PutSpace         bool                   // opzionale
}

BaseMachineData

Struct base embedded in tutti i tipi variables/constants:

  • MachineId, Timestamp, TimestampAck: identificazione e sincronizzazione
  • DigitalSaveError, DigitalSaveType: tracciamento salvataggio su storage digitale

Tipi di conversione

  • BoolStr: boolean <-> "0"/"1" (DB text, JSON 0/1)
  • StrBool: come BoolStr ma marshaled come boolean JSON
  • StringInt: int <-> "123" (JSON string o number -> int)
  • NumString: number <-> "123" (JSON string o number -> string)