RESTClient, connects to a REST web service

Home / Knowledge Base / Manualistica / Programmazione IEC 61131-3 / Gestione networking / RESTClient, connects to a REST web service

Questo blocco funzione gestisce la connessione verso un server REST con protocollo HTTP. Viene inviato il messaggio con i dati in POST (Lunghezza massima 12 Kbytes) e ritornati i dati ricevuti dal server (Lunghezza definita in BLength).

Per garantire il buffering dei dati su eventi molto rapidi, il servizio utilizza il FB di gestione FIFO per gestire uno stack FIFO su di un file, l'indirizzo di allocazione và passato in FIFOFile. In FIFOFilename occorre definire quale file utilizzare per appoggiare lo stack FIFO ed in FIFOSize che dimensione dare al file.  Se si desidera che la situazione dei dati nel FIFO sia mantenuta allo spegnimento del sistema in FIFOIDx occorre fornire l'indirizzo di un array di 2 UDINT che devono essere allocati in una memoria tampone.

In HTTPClient occorre indicare l'indirizzo di allocazione di un FB di gestione client HTTP per gestire la connessione al server REST. In HostAddress definire l'indirizzo IP o l'URL del server REST, è possibile definire anche l'HostName che sarà utilizzato nella richiesta (Di solito è uguale a HostAddress). In HostPort definire la porta del servizio REST (Di solito è la standard HTTP 80). In Page definire lo script di gestione del servizio REST sul server.

Attivando Enable ogni tempo definito in HBitTime viene inviato un messaggio di heartbeat al server REST, questo permette di controllare il corretto funzionamento del servizio anche in assenza di messaggi da inviare al server. L'uscita RSvcOn se attiva indica che la comunicazione con il server è attiva.

La comunicazione con il server è in formato JSON e quindi i dati che si desidera scambiare devono essere codiificati in JSON. Per inviare messaggi al server REST occorre eseguire il FB FIFOFile che trasferirà il messaggio nel buffer FIFO e successivamente al server. L'utilizzo di un buffer FIFO garantisce che tutti i messaggi siano inviati al server bufferizzandoli nel caso di mancata connessione.

La risposta ricevuta dal server è memorizzata in un buffer interno al FB (Di dimensione BLength il cui indirizzo è fornito in PBuffer), alla ricezione si attiva SvcOk ed il programma utente potrà utilizzare i dati presenti nel buffer. In caso di errore ricezione dal server si attiva SvcError ed in PBuffer vi è la pagina ricevuta dal server, sarà possibile trasferirla in un file per visualizzarla da browser per indagare sull'errore.

Function block
CODESYS: Non disponibile
LogicLab: eLLabNetworkLib

Enable (BOOL) Comando attivazione connessione al server REST.

SpyOn (BOOL) Se attivo permette di spiare il funzionamento della FB.

DDelete (BOOL) Attivandolo per un loop se errore RSvcError, rimuove il dato dal registro FIFO.

FIFOFile (@FIFOFile_v1) Indirizzo allocazione FB FIFOFile_v1 di supporto.

HTTPClient (@HTTPClient_v2) Indirizzo allocazione FB HTTPClient_v2 di supporto.

JEncode (@JSONEncode_v1) Indirizzo allocazione FB JSONEncode_v1 di supporto.

JDecode (@JSONDecode_v2) Indirizzo allocazione FB JSONDecode_v2 di supporto.

BLength (UDINT) Dimensione allocazione buffer risposta ricevuta server REST (SysRMalloc) (Default 1024).

Delay (REAL) Tempo pausa invio dati al server (S) (Default 1.0).

HBitTime (REAL) Tempo invio heartbeat al server (S). Ogni tempo definito viene inviato un messaggio di heartbeat al server (Default 5.0).

Enabled (BOOL) Blocco funzione abilitato.

Fault (BOOL) Attivo per un loop se errore esecuzione comando.

RSvcOn (BOOL) Servizio REST attivo. Si disattiva se si interrompe la comunicazione per errore su invio messaggi o heartbeat.

SvcSend (BOOL) Attivo su invio dati al server REST, sul fronte di attivazione è possibile acquisire i dati inviati al server nel buffer Request del FB HTTPClient.

SvcOk (BOOL) Ok ricezione da server REST, si attiva per un loop su ricezione risposta. Su attivazione è possibile acquisire i dati ricevuti dal server nel buffer PBuffer.

SvcError (BOOL) Errore ricezione da server REST, si attiva per un loop su ricezione risposta. Il messaggio viene automaticamente reinviato al server. E' possibile controllare la risposta del server presente in PBuffer e decidere se eliminare il messaggio dopo un numero di reinvii settando DDelete.

MIDNr (UINT) Identificativo messaggio inviato al server.

PktsOk (UDINT) Numero di pacchetti dati corretamente inviati al server (Non sono conteggiati i pacchetti di heartbeat).

PBuffer (@STRING) Pointer buffer risposta ricevuta da server REST. Il buffer è allocato nel FB tramite funzione SysRMAlloc. Valido per un solo loop, la lettura dei dati può essere fatta solo su attivazione di SvcOk o SvcError.

MLength (UDINT) Dimensione messaggio REST inviato al server.

RSvcTime (REAL) Tempo esecuzione servizio REST (S).

Resyncs (UDINT) Counter resincronizzazioni con il server REST. I messaggi scambiati con il server hanno un identificativo MIDNr che ne permette il controllo. In caso di disallineamento (Perdita di un messaggio) viene eseguita una resincronizzazione.

Retries (UDINT) Counter tentativi invio messaggio a server REST. In caso di errore di comunicazione il messaggio viene reinviato automaticamente. Il messaggio viene eliminato dal registro FIFO solo se comunicazione avvenuta correttamente.

Errors (UDINT) Counter errori comunicazione con il server REST.

Trigger di spy

Se SpyOn attivo è possibile utilizzare utilizzare la console di spionaggio per verificare il funzionamento della FB. Sono previsti vari livelli di triggers.

Errori

In caso di errore eseguendo immediatamente dopo la funzione SysGetLastError è possibile rilevare il codice di errore. Fare riferimento alla tabella seguente per la descrizione.

Esempi

Come utilizzare gli esempi.

Viene fornito il dimostrativo "RESTSvcDemo" che include il programma da installare su di un sistema SlimLine e lo script PHP da eseguire su di un qualsiasi server web. Per facilitare il test del programma abbiamo installato lo script PHP su di un server hostato presso Altervista, per testare il programma basterà eseguirlo su di un sistema SlimLine connesso ad Internet.

LogicLab (Ptp135)
PROGRAM ST_RESTClient
VAR
    i : UDINT; (* Auxiliary variable *)
    Fp : eFILEP; (* File pointer *)
    TimeBf : UDINT; (* Time buffer (uS) *)
    HTTP : HTTPClient_v2; (* HTTP client *)
    JDecode : JSONDecode_v2; (* JSON decode *)
    JEncode : JSONEncode_v1; (* JSON encode *)
    RESTAnswer : STRING[ 128 ]; (* REST answer *)
    RESTRequest : STRING[ 128 ]; (* REST request *)
    FIFO : FIFOFile_v1; (* FIFO on file *)
    RESTData : ARRAY[0..2] OF REAL; (* Data exchanged with REST server *)
    REST : RESTClient_v4; (* REST service client *)
END_VAR

// *****************************************************************************
// PROGRAM "ST_RESTClient"
// *****************************************************************************
// It connects to the REST server, sends data and manage answer.
// -----------------------------------------------------------------------------

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

    IF (SysFirstLoop) THEN
        TimeBf:=SysGetSysTime(TRUE); //Time buffer (uS)
        REST.SpyOn:=TRUE; //Spy On

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

//        FIFO.FIFOFilename:=ADR('C:/REST.bin'); //Path and name of FIFO file
//        FIFO.FIFOIDx:=ADR(FIFOIDx); //FIFO indexes
        FIFO.FIFOFilename:=eNULL; //Path and name of FIFO file
        FIFO.FIFOSize:=2048; //FIFO file size
        FIFO.FIFOIDx:=eNULL; //FIFO indexes

        // REST server address and request method.

        HTTP.SpyOn:=FALSE; //Spy On
        HTTP.RMethod:=1; //Request method (POST)
        HTTP.Header:=ADR('Content-Type:application/json$r$n');
        HTTP.HostAddress:=ADR('www.slimline.altervista.org'); //Host address server REST
        HTTP.HostName:=HTTP.HostAddress; //Host name server REST
        HTTP.Page:=ADR('Utilities/RESTSvcDemo/RESTSvc.php'); //REST server script
        HTTP.HostPort:=80; //REST server port
        HTTP.Timeout:=30000; //Timeout (mS)

        // REST auxiliary object references.
    
        REST.FIFOFile:=ADR(FIFO); //FIFO on file
        REST.HTTPClient:=ADR(HTTP); //HTTP Client
        REST.JDecode:=ADR(JDecode); //JSON decode
        REST.JEncode:=ADR(JEncode); //JSON encode

        // REST definitions.

        REST.SpyOn:=TRUE; //Spy On
        REST.Delay:=1.0; //Delayt time (S)
        REST.HBitTime:=5.0; //Heartbeat time (S)
        REST.BLength:=512; //REST Request/Answer buffers length
    END_IF;

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

    REST(Enable:=TRUE); //REST client management
    REST.DDelete:=FALSE; //Delete data from FIFO

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

    IF (REST.SvcError) THEN

        // If the server has sent an error the return is stored on a file.

        IF (REST.PBuffer <> NULL) THEN
            i:=SysFileRemove(ADR('D:/RESTEr.htm')); // Delete the old file if exists
            Fp:=SysFfopen(ADR('D:/RESTEr.htm'), ADR('a')); //Open the file
            IF (Fp <> NULL) THEN
                i:=Sysfwrite(REST.PBuffer, TO_INT(Sysstrlen(REST.PBuffer)), 1, Fp);
                i:=Sysfclose(Fp); //Close file
            END_IF;
        END_IF;

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

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

    // -------------------------------------------------------------------------
    // SEND DATA TO SERVER
    // -------------------------------------------------------------------------
    // Request string, preparation and sending, it will be of the type:
    // {"MID":0,"ST":1,"UID":10878977,"MV":"1.0"}
    // {"Dividend":502.987671,"Divisor":584.047363}

    IF ((SysGetSysTime(TRUE)-TimeBf) > 1000000) THEN
        TimeBf:=SysGetSysTime(TRUE); //Time buffer (uS)
        RESTData[0]:=SysGetRandom(TRUE)*1000.0; //Data exchanged with REST server
        RESTData[1]:=SysGetRandom(TRUE)*1000.0; //Data exchanged with REST server

        i:=Sysmemset(ADR(RESTRequest), 0, SIZEOF(RESTRequest)); //REST request
        JEncode(Object:=ADR(RESTRequest), OSize:=SIZEOF(RESTRequest), Name:=ADR('Dividend'), VType:=REAL_TYPE, VAddress:=ADR(RESTData[0]), Count:=1);
        JEncode(Object:=ADR(RESTRequest), OSize:=SIZEOF(RESTRequest), Name:=ADR('Divisor'), VType:=REAL_TYPE, VAddress:=ADR(RESTData[1]), Count:=1);
        FIFO(In:=TRUE, Dp:=ADR(RESTRequest), Dls:=LEN(RESTRequest)); //Write record on FIFO
    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 <> NULL)) 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;
        i:=Sysmemmove(ADR(RESTAnswer), REST.PBuffer, i); 

        // Decodes the value of "Result" and stores it to RESTData[2].

        JDecode(Object:=REST.PBuffer, Name:=ADR('Result'), VType:=REAL_TYPE, VAddress:=ADR(RESTData[2]), Count:=1);
    END_IF;
    
// [End of file]
PHP (Ptp135)

Ti è stato utile questo articolo ?