Vai al contenuto

Energy logger su cloud in FTP, MQTT o REST API

Vai all indice del manuale di programmazione

In questo articolo vediamo come realizzare in modo semplice un energy logger utilizzando i nostri prodotti in grado di memorizzare i dati su file locali CSV e trasferirli su server FTP in cloud o inviarli ad un broker MQTT o ad un API REST. Un energy logger è un dispositivo elettronico progettato per misurare, registrare e analizzare i consumi energetici di impianti, macchinari o edifici. È uno strumento fondamentale per il monitoraggio energetico, consentendo di raccogliere dati precisi e continui sull’uso dell’energia, al fine di ottimizzare l’efficienza e ridurre gli sprechi. Gli energy logger trovano impiego in diversi contesti:

  • Industria: per monitorare il consumo energetico di macchinari e processi produttivi.
  • Edifici commerciali e residenziali: per analizzare i consumi e identificare aree di inefficienza.
  • Impianti fotovoltaici e di cogenerazione: per verificare la produzione e l’autoconsumo di energia.
  • Audit energetici: come strumento di diagnosi per valutare le prestazioni energetiche e proporre interventi di miglioramento.

Vantaggi dell’utilizzo

  • Riduzione dei costi: identificando sprechi e ottimizzando l’uso dell’energia.
  • Conformità normativa: supportando le aziende nel rispetto delle normative sull’efficienza energetica.
  • Sostenibilità ambientale: contribuendo alla diminuzione delle emissioni di CO₂.
  • Decisioni informate: fornendo dati concreti per pianificare interventi e investimenti.
Immagine Energy logge

Realizzazione pratica

Vediamo come realizzare un energy logger basato sui nostri sistemi programmabili SlimLine che si interfacciano con i meter di energia Eastron tramite interfaccia RS485 Modbus. La connessione con ii meters può avvenire tramite seriale RS485 diretta oppure via TCP/IP utilizzando un convertitore Ethernet/Seriale o WiFi/Seriale. I sistemi sono liberamente programmabili con i linguaggi standard PLC secondo la normativa IEC61131-3 con il tool gratuito LogicLab.Per l’acquisizione dei valori forniamo appositi blocchi funzione da istanziare nel programma (EastronSDM120, EastronSDM630). Tramite i FB è possibile acquisire parametri come:

  • Potenza attiva e reattiva
  • Corrente e tensione
  • Frequenza
  • Fattore di potenza
  • Energia consumata nel tempo

I dati raccolti possono essere memorizzati nel file system del sistema su file in formato CSV con il FB StringToLogFile, successivamente i files potranno essere scaricati da locale e/o da remoto con un cient FTP oppure è il sistema stesso che invia i file CSV direttamente su di un server FTP nel cloud con il FB FTPClient, ad un broker MQTT con il FB MQTTClient (Esempio), o ad una API REST con il FB RESTClient (Esempio).

Programma PTP211, FTPEnergyLogger

In questo programma (Distribuito con LogicLab) i dati acquisiti da due meters Eastron SDM630 sono salvati ogni 15 minuti in un file CSV. Il file è salvato nel file system del sistema SlimLine ed poi inviato ad un server FTP nel cloud. Vediamo come è suddiviso il programma.

MetersAcquisition: Esegue l’acquisizione di due meters Eastron SDM630.

DateTimeMng: Gestisce data/ora di sistema, sincronizza il real time clock con un server NTP su Internet e gestisce lo strobe di log ogni 15 minuti.

CSVDataStore: Su strobe tempo crea un file CSV con un nome del tipo “HHQDDMM.csv” dove MM (2 cifre): Mese dell’anno, DD (2 cifre): Giorno del mese, HH (2 cifre): Ora del giorno, MM (2 cifre): Minuti del giorno.Nella prima riga del file inserisce il nome delle colonne e nella seconda riga i dati acquisiti dai meters.

LogicLab (Ptp211, CSVDataStore)
PROGRAM CSVDataStore
VAR CONSTANT
    BSize : UINT := 512; (* Buffer size (SysRMalloc) *)
END_VAR

VAR
    i : UDINT; (* Auxiliary variable *)
    DBuffer : @STRING; (* Data buffer (SysRMalloc) *)
    Filename : STRING[ 32 ]; (* Log file name *)
    Logger : StringToLogFile_v3; (* Log manager *)
END_VAR

// *****************************************************************************
// PROGRAM "CSVDataStore"
// *****************************************************************************
// Esegue la compilazione del file CSV con i dati in log.
// -----------------------------------------------------------------------------

    // -------------------------------------------------------------------------
    // INITIALIZATION
    // -------------------------------------------------------------------------
    // Execute program initialization.

    IF (SysFirstLoop) THEN
        Logger.Mode:=1; //AsciiHex mode
        Logger.LDLength:=0; //Log data length
        Logger.Header:=eNULL; //Header string
        Logger.Filename:=ADR(Filename); //Log file
    END_IF;

    // -------------------------------------------------------------------------
    // ATTESA STROBE ESECUZIONE
    // -------------------------------------------------------------------------
    // Attesa strobe esecuzione log dati.

    IF (DBuffer <> eNULL) THEN eTO_JUNK(SysRMFree(ADR(DBuffer))); END_IF;
    IF NOT(GBVars.LDStrobe) THEN RETURN; END_IF;

    // Allocate the log string buffer.

    IF NOT(SysRMAlloc(BSize, ADR(DBuffer))) THEN RETURN; END_IF;
    GBVars.LDStrobe:=FALSE; //Log data strobe

    // -------------------------------------------------------------------------
    // MANAGE THE DISK FILE
    // -------------------------------------------------------------------------
    // Creo nome file di log "HHQDDMM.csv"
    // MM (2 cifre): Mese dell'anno
    // DD (2 cifre): Giorno del mese
    // HH (2 cifre): Ora del giorno
    // MM (2 cifre): Minuti del giorno

    eTO_JUNK(SysVsnprintf(ADR(Filename), SIZEOF(Filename), ADR('%s\'), STRING_TYPE, ADR(GBPars.DFilePath)));
    eTO_JUNK(DateTimeFormat(GBVars.LDTime, ADR('mdHi\."csv"'), ADR(Filename), SIZEOF(Filename)));

    // Se file non esiste lo creo definendo i nomi delle colonne.

    IF (SysGetFileLen(ADR(Filename)) = eEOF) THEN
        eTO_JUNK(SysVsnprintf(DBuffer, BSize, ADR('%s;'), STRING_TYPE, ADR('TIMESTAMP')));

        eTO_JUNK(SysCVsnprintf(DBuffer, BSize, ADR('%s;'), STRING_TYPE, ADR('Meter_1_Voltage')));
        eTO_JUNK(SysCVsnprintf(DBuffer, BSize, ADR('%s;'), STRING_TYPE, ADR('Meter_1_Current')));
        eTO_JUNK(SysCVsnprintf(DBuffer, BSize, ADR('%s;'), STRING_TYPE, ADR('Meter_1_Energy')));

        eTO_JUNK(SysCVsnprintf(DBuffer, BSize, ADR('%s;'), STRING_TYPE, ADR('Meter_1_Voltage')));
        eTO_JUNK(SysCVsnprintf(DBuffer, BSize, ADR('%s;'), STRING_TYPE, ADR('Meter_1_Current')));
        eTO_JUNK(SysCVsnprintf(DBuffer, BSize, ADR('%s;'), STRING_TYPE, ADR('Meter_1_Energy')));
        Logger(LData:=DBuffer); //FB gestione log
    END_IF;

    // -------------------------------------------------------------------------
    // WRITE THE LOG RECORD
    // -------------------------------------------------------------------------
    // Initialize it with date and time.

    eTO_JUNK(DateTimeFormat(GBVars.LDTime, ADR('^d/m/Y H\:i\:s\;'), DBuffer, BSize)); //TIMESTAMP 22/09/2022 16:21:08

    // Insert column values separated by a ",".

    eTO_JUNK(SysCVsnprintf(DBuffer, BSize, ADR('%.1f;'), REAL_TYPE, ADR(Meter[0].Voltage[3])));
    eTO_JUNK(SysCVsnprintf(DBuffer, BSize, ADR('%.1f;'), REAL_TYPE, ADR(Meter[0].Current[4])));
    eTO_JUNK(SysCVsnprintf(DBuffer, BSize, ADR('%.1f;'), REAL_TYPE, ADR(Meter[0].TAcEnergy)));

    eTO_JUNK(SysCVsnprintf(DBuffer, BSize, ADR('%.1f;'), REAL_TYPE, ADR(Meter[1].Voltage[3])));
    eTO_JUNK(SysCVsnprintf(DBuffer, BSize, ADR('%.1f;'), REAL_TYPE, ADR(Meter[1].Current[4])));
    eTO_JUNK(SysCVsnprintf(DBuffer, BSize, ADR('%.1f;'), REAL_TYPE, ADR(Meter[1].TAcEnergy)));

    // Separatore decimale richiesto deve essere "," sostituisco ".".

    FOR i:=0 TO BSize DO
        IF (eGetBYTE(DBuffer+i) = 16#00) THEN EXIT; END_IF; //Fine stringa
        IF (eGetBYTE(DBuffer+i) = 16#2E) THEN eTO_JUNK(eSetBYTE(DBuffer+i, 16#2C)); END_IF;
    END_FOR;

    // Eseguo scrittura record di log.

    Logger(LData:=DBuffer); //Store record on disk

// [End of file]

FTPDataSend: Esegue la ricerca nella cartella dove sono memorizzati i files CSV generati dal programma CSVDataStore. Per permettere la gestione dell’invio al server dei nuovi file memorizzati si antepone al nome del file un carattere dal significato:

  • (#) Corrente: File corrente su cui sono memorizzati idati. Si utilizza nel caso il file contenga più registrazioni. In questo esempio il file ha sempre solo una registrazione quindi non si utilizza.
  • (!) Archiviato: File già inviato al server FTP. Il file è comunque mantenuto nella cartella del sistema per sicurezza.
  • (_) In errore: Questo file non è stato possibile inviarlo al server FTP.

Nel programma si itera su tutti i files presenti nella cartella e i file nuovi sono inviati al server FTP. In caso di errore invio si ritenta per un numero di tentativi e se sempre in errore il file viene segnato in errore. I files gia inviati al server con data antecedente il perido definito vengono cancellati dalla cartella.

LogicLab (Ptp211, FTPDataSend)
PROGRAM FTPDataSend
VAR
    CaseNr : USINT; (* Program case *)
    FTPErrors : USINT; (* FTP error counter *)
    TimeBf : UDINT; (* Time buffer (mS) *)
    DirWithFilter : STRING[ 32 ]; (* Directory name with filter *)
    LFilename : STRING[ 64 ]; (* Local filename *)
    RFilename : STRING[ 64 ]; (* Remote filename *)
    DList : SysGetFileInfos; (* Directory listing *)
    FTP : FTPClient_v4; (* FTP client *)
END_VAR

// *****************************************************************************
// PROGRAM "FTPDataSend"
// *****************************************************************************
// Esegue trasferimento file dati su server FTP.    
// -----------------------------------------------------------------------------

    // -------------------------------------------------------------------------
    // INITIALIZAZTION
    // -------------------------------------------------------------------------
    // Program initialization.

    IF (SysFirstLoop) THEN

        // Definizione filtro di ricerca in directory.

        DList.PathName:=ADR(DirWithFilter); //Directory to list
        eTO_JUNK(SysVsnprintf(ADR(DirWithFilter), SIZEOF(DirWithFilter), ADR('%s\*.csv'), STRING_TYPE, ADR(GBPars.DFilePath)));

        // Configurazione client FTP.

        FTP.SpyOn:=TRUE; //Spy active
        FTP.FTPServer:=ADR(GBPars.FTPServer); //Server FTP address
        FTP.FTPPort:=GBPars.FTPPort; //Server FTP port
        FTP.User:=ADR(GBPars.FTPUser); //User
        FTP.Password:=ADR(GBPars.FTPPassword); //Password
        FTP.LocalFile:=ADR(LFilename); //Local file
        FTP.RemoteFile:=ADR(RFilename); //Remote file
    END_IF;

    // -------------------------------------------------------------------------
    // FTP CLIENT MANAGEMENT
    // -------------------------------------------------------------------------
    // On FTP error set the parking sequence in waiting to retry the
    // connection. In this phase the log records are saved in the FIFO.

    FTP(); //FTP client
    IF (FTP.Fault) THEN CaseNr:=200; END_IF;

    // -------------------------------------------------------------------------
    // DIRECTORY LISTING MANAGEMENT
    // -------------------------------------------------------------------------
    // Manage the directory listing.

    DList(); //Directory listing
    DList.Init:=FALSE; //Init listing
    DList.Next:=FALSE; //Next command

    // -------------------------------------------------------------------------
    // PROGRAM CASES
    // -------------------------------------------------------------------------
    // Program cases.

    CASE (CaseNr) OF

        // ---------------------------------------------------------------------
        // INIZIALIZZAZIONE
        // ---------------------------------------------------------------------
        // Inizializzo trasferimento in FTP.

        0:
        TimeBf:=SysTimeGetMs(); //Time buffer (mS)
        CaseNr:=CaseNr+1; //Program case

        // ---------------------------------------------------------------------
        // Attendo tempo di pausa su ricerca file da inviare.

        1:
        IF ((SysTimeGetMs()-TimeBf) < TO_UDINT(GBPars.FTPDelay)) THEN RETURN; END_IF;
        CaseNr:=CaseNr+1; //Program case

        // ---------------------------------------------------------------------
        // Inizializzo listing directory.

        2:
        DList.Init:=TRUE; //Init listing
        CaseNr:=10; //Program case

        // ---------------------------------------------------------------------
        // CONTROLLO SE FILE PRESENTE
        // ---------------------------------------------------------------------
        // Se nessun file presente termino.

        10:
        IF NOT(DList.Found) THEN CaseNr:=0; RETURN; END_IF;

        // Controllo se directory.

        IF (DList.IsDir) THEN DList.Next:=TRUE; RETURN; END_IF;

        // Definisco percorso e nome file locale.

        eTO_JUNK(SysVsnprintf(ADR(LFilename), SIZEOF(LFilename), ADR('%s/'), STRING_TYPE, ADR(GBPars.DFilePath)));
        eTO_JUNK(SysCVsnprintf(ADR(LFilename), SIZEOF(LFilename), ADR('%s'), STRING_TYPE, DList.Name));

        // Controllo se file da cancellare.

        IF (DList.FTime < (SysDateTime-(TO_UDINT(GBPars.FileLTime)/1000))) THEN
            eTO_JUNK(SysFileRemove(ADR(LFilename))); //File remove
            DList.Next:=TRUE; //Next command
            RETURN;
        END_IF;

        // Scarto il files corrente, se archiviato ed in errore.

        IF (eGetBYTE(DList.Name) = 16#21) THEN DList.Next:=TRUE; RETURN; END_IF; //(!) Archiviato
        IF (eGetBYTE(DList.Name) = 16#23) THEN DList.Next:=TRUE; RETURN; END_IF; //(#) Corrente
        IF (eGetBYTE(DList.Name) = 16#5F) THEN DList.Next:=TRUE; RETURN; END_IF; //(_) In errore

        // Definisco percorso e nome file remoto.
        // Antepongo anno davanti al nome del file per generare anno a 4 cifre.

        eTO_JUNK(DateTimeFormat(TO_LDATE_AND_TIME(SysDateGetNs()), ADR('^Y'), ADR(RFilename), SIZEOF(RFilename)));
        eTO_JUNK(SysCVsnprintf(ADR(RFilename), SIZEOF(RFilename), ADR('%s'), STRING_TYPE, DList.Name));
        FTP.Store:=TRUE; //Store command
        CaseNr:=100; //Program case

        // ---------------------------------------------------------------------
        // ESEGUO TRASFERIMENTO
        // ---------------------------------------------------------------------
        // Controllo se fine trasferimento.

        100:
        IF NOT(FTP.Done) THEN RETURN; END_IF;
        FTP.Store:=FALSE; //Store command
        FTPErrors:=0; //FTP error counter

        // Rinomino file locale anteponendo "!" per identificarlo come inviato.
        // Uso "RFilename" come buffer di appoggio nome file.

        eTO_JUNK(SysVsnprintf(ADR(RFilename), SIZEOF(RFilename), ADR('%s/'), STRING_TYPE, ADR(GBPars.DFilePath)));
        eTO_JUNK(SysCVsnprintf(ADR(RFilename), SIZEOF(RFilename), ADR('!%s'), STRING_TYPE, DList.Name));
        eTO_JUNK(SysFileRename(ADR(LFilename), ADR(RFilename)));
        CaseNr:=0; //Program case

        // ---------------------------------------------------------------------
        // FTP SERVER ERROR                        
        // ---------------------------------------------------------------------
        // Arrivo se trasferimento FTP in errore.

        200:
        FTP.Store:=FALSE; //Store command
        FTPErrors:=FTPErrors+1; //FTP error counter

        // Se superato numero errori rinomino file locale.
        // Rinomino file locale anteponendo "_" per identificarlo come errore.
        // Uso "RFilename" come buffer di appoggio nome file.

        IF (FTPErrors > 3) THEN
            eTO_JUNK(SysVsnprintf(ADR(RFilename), SIZEOF(RFilename), ADR('%s/'), STRING_TYPE, ADR(GBPars.DFilePath)));
            eTO_JUNK(SysCVsnprintf(ADR(RFilename), SIZEOF(RFilename), ADR('_%s'), STRING_TYPE, DList.Name));
            eTO_JUNK(SysFileRename(ADR(LFilename), ADR(RFilename)));
            CaseNr:=0; //Program case
            RETURN;
        END_IF;

        // Inizializzo temporizzazione per retry.

        TimeBf:=SysTimeGetMs(); //Time buffer (mS)
        CaseNr:=CaseNr+1; //Program case

        // ---------------------------------------------------------------------
        // Wait a while before to retry.

        201:
        IF ((SysTimeGetMs()-TimeBf) < TO_UDINT(T#5m)) THEN RETURN; END_IF;
        FTP.Store:=TRUE; //Store command
        CaseNr:=100; // Program case
    END_CASE;

// [End of file]

Programma PTP212, RESTEnergyLogger

In questo programma (Distribuito con LogicLab) i dati acquisiti da due meters Eastron SDM630 sono inviati ogni 15 minuti ad una API REST sul cloud, utilizzando il FB RESTClient. In realtà i dati sono memorizzati in un registro FIFO su file ed inviati al server solo se possibile. Questo permette nel caso di problemi di cominicazione con il server di mantenere tutte le acquisizioni con il relativo timestamp. Vediamo come è suddiviso il programma.

MetersAcquisition: Esegue l’acquisizione di due meters Eastron SDM630.

DateTimeMng: Gestisce data/ora di sistema, sincronizza il real time clock con un server NTP su Internet e gestisce lo strobe di log ogni 15 minuti.

JSONDataStore: Su strobe tempo crea un record JSON con tutte le misure evvettuate e memorizza il record nel registro FIFO.

LogicLab (Ptp212, JSONDataStore)
PROGRAM JSONDataStore
VAR CONSTANT
    BSize : UINT := 1024; (* Buffer size (SysRMalloc) *)
END_VAR

VAR
    DBuffer : @STRING; (* Data buffer (SysRMalloc) *)
END_VAR

// *****************************************************************************
// PROGRAM "JSONDataStore"
// *****************************************************************************
// Creo record JSON e lo memorizza nel registro FIFO.
// -----------------------------------------------------------------------------

    // -------------------------------------------------------------------------
    // ATTESA STROBE ESECUZIONE
    // -------------------------------------------------------------------------
    // Attesa strobe esecuzione log dati.

    IF (DBuffer <> eNULL) THEN eTO_JUNK(SysRMFree(ADR(DBuffer))); END_IF;
    IF NOT(GBVars.LDStrobe) THEN RETURN; END_IF;

    // Allocate the log string buffer.

    IF NOT(SysRMAlloc(BSize, ADR(DBuffer))) THEN RETURN; END_IF;
    GBVars.LDStrobe:=FALSE; //Log data strobe

    // -------------------------------------------------------------------------
    // RECORD DATI ENERGETICI
    // -------------------------------------------------------------------------
    // Compilo record JSON con dati energetici.

    eTO_JUNK(JSONEncoder(DBuffer, BSize, ADR('Meter_1_Voltage'), REAL_TYPE, ADR(Meter[0].Voltage[3]), 1));
    eTO_JUNK(JSONEncoder(DBuffer, BSize, ADR('Meter_1_Current'), REAL_TYPE, ADR(Meter[0].Current[4]), 1));
    eTO_JUNK(JSONEncoder(DBuffer, BSize, ADR('Meter_1_Energy'), REAL_TYPE, ADR(Meter[0].TAcEnergy), 1));

    eTO_JUNK(JSONEncoder(DBuffer, BSize, ADR('Meter_2_Voltage'), REAL_TYPE, ADR(Meter[1].Voltage[3]), 1));
    eTO_JUNK(JSONEncoder(DBuffer, BSize, ADR('Meter_2_Current'), REAL_TYPE, ADR(Meter[1].Current[4]), 1));
    eTO_JUNK(JSONEncoder(DBuffer, BSize, ADR('Meter_2_Energy'), REAL_TYPE, ADR(Meter[1].TAcEnergy), 1));

    RDFIFO(In:=TRUE, Dp:=DBuffer, Dls:=Sysstrlen(DBuffer)); //Write record on RDFIFO
    eTO_JUNK(SysWrSpyData(SPY_ASCII, 0, 16#00000001, ADR('JStore[3]'), DBuffer));

// [End of file]

RESTDataSend: Controlla se dati nel registro FIFO e li invia al server REST. Se invio correttamente eseguito elimina dal registro FIFO il record inviato:

LogicLab (Ptp212, RESTDataSend)
PROGRAM RESTDataSend

VAR
    i : UDINT; (* Auxiliary variable *)
    RESTAnswer : STRING[ 128 ]; (* REST answer *)
    TCPClient : SysTCPClient; (* TCP client management *)
    HTTPRq : HTTPClient_v5; (* HTTP client *)
    REST : RESTClient_v7; (* REST service client *)
END_VAR

// *****************************************************************************
// PROGRAM "RESTDataSend"
// *****************************************************************************
// Invio dati ad API REST.
// -----------------------------------------------------------------------------

    // -------------------------------------------------------------------------
    // PROGRAM INIT
    // -------------------------------------------------------------------------
    // Executed at first program execution, all variables are initialized.

    IF (SysFirstLoop) THEN

        // If "RDFIFOFilename" it's not defined the data are stored in memory 
        // instead of a file on disk. Stored data are lost on system power off.
        // If "RDFIFOIDx" it's not defined the index are stored internally of the
        // FB. Stored data are lost on power off.

        RDFIFO.FIFOFilename:=ADR('D:/REST.bin'); //Path and name of RDFIFO file
        RDFIFO.FIFOSize:=24000; //RDFIFO file size
        RDFIFO.FIFOIDx:=ADR(FIFOIDx); //RDFIFO indexes

        // Set TCPClient parameters.

        TCPClient.PeerAdd:=ADR('server.com'); //Peer address
        TCPClient.PeerPort:=80; //Peer port
        TCPClient.LocalAdd:=ADR('0.0.0.0'); //Local address
        TCPClient.LocalPort:=0; //Local port
        TCPClient.FlushTm:=50; //Flush time (mS)
        TCPClient.LifeTm:=20; //Life time (S)
        TCPClient.RxSize:=512; //Rx buffer size
        TCPClient.TxSize:=512; //Tx buffer size

        // Set HTTPClient parameters.

        HTTPRq.SpyOn:=FALSE; //Activate the spy
        HTTPRq.KeepAlive:=FALSE; //HTTP keep-alive
        HTTPRq.RMethod:=HTTP_REQUEST#HTTP_POST; //Request method
        HTTPRq.HostName:=ADR('server.com'); // Hostname
        HTTPRq.Page:=ADR('page'); //Web page
        HTTPRq.Header:=ADR('Content-Type:application/json$r$n'); //HTTP header
        HTTPRq.DBSize:=512; //Data buffer size
        HTTPRq.Timeout:=T#10s; //Execution timeout

        // REST definitions.

        REST.SpyOn:=TRUE; //Spy On
        REST.BLength:=512; //REST Request/Answer buffers length
        REST.Delay:=T#2s; //Delay time
        REST.HBitTime:=T#30s; //Heartbeat time

        // REST auxiliary object references.

        REST.FIFOFile:=ADR(RDFIFO); //RDFIFO on file
        REST.HTTPClient:=ADR(HTTPRq); //HTTP Client
    END_IF;

    // -------------------------------------------------------------------------
    // REST CLIENT MANAGEMENT
    // -------------------------------------------------------------------------
    // Here the REST client is managed, it's always enabled.

    TCPClient(Connect:=HTTPRq.Connect); //TCPClient management
    HTTPRq(File:=TCPClient.File); //HTTP client
    REST(Enable:=TRUE); //REST client management
    REST.DDelete:=FALSE; //Delete data from RDFIFO

    // -------------------------------------------------------------------------
    // ERROR MANAGEMENT                                       
    // -------------------------------------------------------------------------
    // On error it's possible to count the retries and then decide what to do.

    IF (REST.SvcError) THEN

        // After 3 retries the message to be sent to the REST server is removed
        // from the RDFIFO register. Before to remove the message it's returned
        // to the spy console.

        IF (REST.Retries > 3) THEN
            RDFIFO(Out:=TRUE, Dp:=eNULL, Dls:=0); //Read record length
            IF NOT(SysRMAlloc(RDFIFO.Dl+1, ADR(RDFIFO.Dp))) THEN RETURN; END_IF;
            RDFIFO(Out:=TRUE, Dls:=RDFIFO.Dl); //Read record data
            i:=SysWrSpyData(SPY_ASCII, 0, 16#00000001, ADR('-Delete-'), RDFIFO.Dp);
            i:=SysRMFree(ADR(RDFIFO.Dp)); //Free malloc memory
            REST.DDelete:=TRUE; //Delete data from RDFIFO
         END_IF;
    END_IF;

    // -------------------------------------------------------------------------
    // RECEIVED VARIABLES ACQUISITION                                        
    // -------------------------------------------------------------------------
    // When answer is received decodes it's value and store it on variable.
    // The REST server answer will be {"Result":"xxxx"}

    IF (REST.SvcOk AND (REST.PBuffer <> eNULL)) THEN

        // Copy the received data to buffer.

        i:=Sysmemset(ADR(RESTAnswer), 0, SIZEOF(RESTAnswer));
        i:=Sysstrlen(REST.PBuffer);
        IF (i > SIZEOF(RESTAnswer)) THEN i:=SIZEOF(RESTAnswer); END_IF;
        eTO_JUNK(Sysmemmove(ADR(RESTAnswer), REST.PBuffer, i)); 
    END_IF;

// [End of file]
Was this article helpful?