HTTPClient, HTTP client

List

Questa pagina fa parte del Manuale Programmazione IEC 61131-3. Vai all indice.

L’HTTP è un protocollo che lavora con un’architettura di tipo client/server, il client esegue una richiesta ed il server restituisce la risposta mandata da un altro host. Questo blocco funzione da eseguire in task Back, esegue la richiesta in modalità client (Proprio come il browser), per gestire la connessione con il protocollo HTTP utilizzare il FB SysTCPClient collegando tra di loro gli I/O File. Per gestire la connessione con il protocollo HTTPS occorre inserire tra la connessione degli I/O File il FB SysTLSClient.

Attivando Enable viene attivata l’uscita Connect che comanda la connessione al server, a connessione avvenuta viene inviata la richiesta HTTP della pagina definita in Page all’HostName. La pagina viene richiesta con i parametri definiti nel buffer Request passati secondo la definizione di RMethod (GET, POST, PUT).

In DBSize occorre definire la dimensione del buffers che l’FB alloca (Con SysRMalloc) per la gestione dei pacchetti TCP in trasmissione e ricezione. La dimensione minima è 256 bytes, aumentando la dimensione si velocizza il trasferimento (Sono effettuati meno frazionamenti). E’ inutile definire lunghezze superiori al limite del pacchetto TCP 1500 bytes.

L’header e la pagina sono ricevuti in frammenti successivi in base al tipo di trasferimento del server, ad ogni ricezione di un frammento DBChars viene valorizzato (Per un solo loop di esecuzione) con il numero di bytes ricevuti che possono essere letti dal buffer all’indirizzo DBAddress. (HPSelector indica il tipo di dati ricevuti, FALSE dati header, TRUE dati pagina). In questo modo è possibile ricevere qualsiasi dimensione di dati, sarà cura del programma utente trasferire i dati ricevuti in una stringa o in un file.

Al termine dell’invio della richiesta si attiva per un loop di programma l’uscita RSent, su ricezione pagina si attiva per un loop di programma l’uscita PLoad, in HLength e PLength sono ritornate le lunghezze dell’header e della pagina ricevuti, mentre in PLTime il tempo necessario per l’intera richiesta.

In caso di errore esecuzione o tempo di esecuzione comando superiore al tempo definito in Timeout, viene attivata per un loop di programma l’uscita Fault. L’uscita Done si attiva al termine della esecuzione della richiesta e su errore. Per acquisire nuovamente la pagina occorre disabilitare e poi riabilitare l’ingresso Enable.

Upgrade list

HTTPClient_v4

Viene gestita la connessione HTTP Keep Alive nativa dell’HTTP 1.1, per gestirla si è aggiunto il comando Send. Attivando Enable viene comandata la sola connessione al server HTTP, per inviare la richiesta occorre attivare Send. Ricevuta la risposta la connessione rimane attiva per la prossima richiesta, per chiudere la connessione disabilitare Enable.

HTTPClient_v5

Modificata gestione connessione HTTP Keep Alive, aggiunto parametri KeepAlive e KAAccepted, attivando permanentemente Enable, il comando Send, gestisce la connessione automaticamente. Modificato parametro RMethod ora è di tipo HTTP_REQUEST. Modificato tipo parametri Timeout e PLTime ora sono di tipo TIME.

Information Circle

Blocco funzione

CODESYS: Non disponibile

LogicLab: eLLabHTTPLib

Descrizione

Enable (BOOL) Comando abilitazione esecuzione, la connessione al server si attiva sul comando Send.
Send (BOOL) Comando richiesta pagina. Per eseguire una nuova richiesta occorre disattivare e riattivare l’ingresso.
KeepAlive (BOOL) Se attivo, terminata la richiesta viene mantenuta attiva la connessione al server. Verificare che nel tempo di LifeTme del FB TCPClient vengano eseguite richieste.
SpyOn (BOOL) Se attivo permette di spiare il funzionamento del FB (Vedi articolo).
File (eFILE) Stream di I/O utilizzato per la connessione.
RMethod (HTTP_REQUEST) Metodo gestione richiesta (Definizione).
HostName (@STRING) Nome del server utilizzato nella richiesta.
Page (@STRING) Stringa di definizione pagina richiesta.
Request (@STRING) Indirizzo buffer dati da inviare con la richiesta.
Header (@STRING) Indirizzo stringa header, se eNULL viene inviato l’header standard. Se definito occorre indicare l’header completo da inviare con la richiesta al server i campi vanno sempre terminati con $r$n.
DBSize (UINT) Dimensione buffers Rx/Tx allocati da FB (SysRMalloc). Aumentando la dimensione del buffer si riduce la frammentazione dei dati, minimo 256 massimo 1500.
Timeout (TIME) Timeout esecuzione richiesta pagina.
Connect (BOOL) Comando di connessione al server, su attivazione occorre gestire la connessione.
Done (BOOL) Attivo a fine esecuzione, si attiva anche in caso di Fault. L’uscita rimane attiva fino a quando non viene settato Enable:=FALSE, o Send:=FALSE.
Fault (BOOL) Attivo per un loop di programma se errore gestione.
KAAccepted (BOOL) Si attiva se il server gestisce la connessione HTTP Keep Alive.
RSent (BOOL) Attivo per un loop di programma al termine dell’invio richiesta HTTP.
HPSelector (BOOL) FALSE: Si stanno ricevendo dati di header. TRUE: Si stanno ricevendo dati di pagina.
PLoad (BOOL) Attivo per un loop di programma su fine ricezione pagina.
HTTPStatus (UINT) Al termine della ricezione pagina ritorna il codice di stato HTTP ricevuto dal server.
CCounter (UDINT) Contatore connessioni al server HTTP. Se la connessione è in keep alive, se non vi sono errori o timeout di comunicazione si ha una sola connessione al server.
PCounter (UDINT) Contatore pagine ricevute.
DBAddress (PVOID) Indirizzo buffer dati ricevuti allocato da FB (Con SysRMalloc) di dimensione DBSize.
DBChars (UDINT) Bytes di pagina ricevuti, viene valorizzato per un loop. Ad ogni valorizzazione occorre estrarre i dati (Header/Pagina) da DBAddress. Nella ricezione dell’header (HPSelector=FALSE) sono ritornate di volta in volta le singole righe di header terminate con <CR><LF>.
HLength (UDINT) Dimensione header ricevuto.
PLength (UDINT) Dimensione pagina ricevuta.
PLTime (TIME) Tempo impiegato per richiesta pagina.
Errors (UDINT) Counter errori di esecuzione.

Immagine FB HTTPClient_v5

Spionaggio funzionamento

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

Trigger di spionaggio
16#00000001‘Tx’ Invio dati verso server HTTP
16#00000002‘Rx’ Ricezione dati da server HTTP
16#00000004‘Rq’ Stringa richiesta
16#10000000‘Lg’ Log dati di esecuzione
16#40000000‘Er’ Errori di esecuzione

Header richiesta

Nel parametro Header è possibile definire un campo header personalizzato da inviare al server, se definito eNULL il FB invia l’header con i campi evidenziati nella tabella seguente. Occorre far seguire alla definizione i caratteri $r$n, è possibile definire più headers semplicemente accodandoli. Da notare che se viene definito un header uguale ad uno definito di default dal FB, il campo di default è sostituito da quello definito.

Ecco alcuni esempi, definendo il campo Accept: seguito dalla definizione application/json il campo di default Accept: text/plain, text/html non verrà più inviato.

HTTPRq.Header:=ADR('Accept: application/json$r$n');
HTTPRq.Header:=ADR('Accept: application/json$r$nAccept-Charset: ascii$r$nConnection: Close$r$n');
Elenco headers
Accept-Language: it-ITIndica al server quali lingue sono accettate. In questo caso italiano.
Accept: text/plain, text/htmlIndica al server che è accettato testo.
Accept-Charset: asciiIndica al server che sono accettati tutti i caratteri ascii.
Accept-Encoding: *;q=0Indica al server il tipo di codifica accettata.
Connection: Close
Connection: Keep-alive
Indica al server che terminata la richiesta deve chiudere la connessione.
Indica al server di mantenere attiva la connessione.
Accept: application/jsonIndica al server che sono accettate risposte in JSON
Autenticazione

Molti servizi REST API richiedono l’autenticazione, esistono diversi tipi di autenticazione i più utilizzati sono il Basic e Digest utilizzati per le autenticazioni basate su credenziali di accesso (Nome utete e password) ed il Bearer utilizzato per autenticazione con token. L’autenticazione deve essere passata nell’header di accesso all’API, i valori di autenticazione sono passati codificati in Base64, esistono molti codificatori on-line che provvedono alla codifica/decodifica.

Particolare attenzione và posta nella codifica delle credenziali definendo username e password separate da “:”. Quindi ad esempio per l’utente Root con password MyPassword codificando la stringa Root:MyPassword, avremo: HTTPRq.Header:=ADR(‘Authorization: Basic Um9vdDpNeVBhc3N3b3Jk$r$n’);

Da notare che dopo la definizione della stringa Base64 ho inserito il <CR><LF> ($r$n) di terminazione campo.

Esempi

Come utilizzare gli esempi.

ST_HTTPClient: Ogni 5 secondi viene eseguito uno script php sul cloud che esegue la divisione tra i valori passati in GET alla richiesta, sono passati i valori di Dividend e Divisor. E’ possibile testare il funzionamento dello script a questo indirizzo. Se lo script è attivo viene ritornata una pagina con: The result is: 50, nelle variabili Header e Page è possibile visualizzare in debug i valori ritornati dal server.

ST_HTTPClient_ToFile: Attivando da debug la variabile Execute viene eseguito uno script php sul cloud che ritorna un file di testo da 4Kb (4110 bytes). E’ possibile testare il funzionamento dello script a questo indirizzo. Se lo script è attivo vengono creati 2 files uno che contiene l’header e l’altro la pagina con i valori ricevuti dal server.

LogicLab (Ptp156, ST_HTTPClient)
PROGRAM ST_HTTPClient
VAR
    i : UDINT; (* Auxiliary variable *)
    CaseNr : USINT; (* Program case *)
    Errors : UDINT; (* Execution errors *)
    TimeBf : UDINT; (* Time buffer (mS) *)
    Page : STRING[ 64 ]; (* Page string *)
    Header : STRING[ 256 ]; (* Header string *)
    TCPClient : SysTCPClient; (* TCP client management *)
    HTTPRq : HTTPClient_v5; (* HTTP client *)
END_VAR

// *****************************************************************************
// PROGRAM "ST_HTTPClient"
// *****************************************************************************
// The program connects to a server om which is running a PHP script like:
//
// <?php echo "The result is: ".($_REQUEST["Dividend"]/$_REQUEST["Divisor"]); ?>
//
// The program sends the "Dividend" and "Divisor" values and receives back from
// the server the result. If the server answers correctly the following result
// is expected.
//
// Header: 'HTTP/1.1 200 OK...
// Page: 'The result is: 50'
// -----------------------------------------------------------------------------

    // -------------------------------------------------------------------------
    // INITIALIZATIONS
    // -------------------------------------------------------------------------
    // Program initializations.

    IF (SysFirstLoop) THEN

        // Set TCPClient parameters.

        TCPClient.PeerAdd:=ADR('demos.elsist.biz'); //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:=TRUE; //Activate the spy
        HTTPRq.KeepAlive:=FALSE; //HTTP keep-alive
        HTTPRq.RMethod:=HTTP_REQUEST#HTTP_GET; //HTTP request method
        HTTPRq.HostName:=TCPClient.PeerAdd; // Hostname
        HTTPRq.Page:=ADR('eLLabHTTPLib/HTTPClient/ST_HTTPClient.php'); //Web page
        HTTPRq.Request:=ADR('Dividend=500$26Divisor=10'); //Request string
        HTTPRq.Header:=eNULL; //HTTP header
        HTTPRq.DBSize:=512; //Data buffer size
        HTTPRq.Timeout:=T#10s; //Execution timeout

        // Set local variables.

        TimeBf:=SysTimeGetMs(); //Time buffer (mS)
    END_IF;

    // -------------------------------------------------------------------------
    // MANAGE CONNECTION
    // -------------------------------------------------------------------------
    // Manage the connection.

    TCPClient(Connect:=HTTPRq.Connect); //TCPClient management
    HTTPRq(Enable:=TRUE, File:=TCPClient.File); //HTTP client
    IF (HTTPRq.Fault) THEN CaseNr:=0; END_IF;

    // -------------------------------------------------------------------------
    // PROGRAM SEQUENCIES
    // -------------------------------------------------------------------------
    // Program sequencies.

    CASE (CaseNr) OF

        // ---------------------------------------------------------------------
        // Initialize the time to manage acquisition delay.

        0:
        HTTPRq.Send:=FALSE; //Send request
        TimeBf:=SysTimeGetMs(); //Time buffer (mS)
        CaseNr:=CaseNr+1; //Program case

        // ---------------------------------------------------------------------
        // Wait delay time then initialize buffers and enable client.

        1:
        IF ((SysTimeGetMs()-TimeBf) < TO_UDINT(T#5s)) THEN RETURN; END_IF;
        i:=Sysmemset(ADR(Header), 0, SIZEOF(Header)); //Empty header string
        i:=Sysmemset(ADR(Page), 0, SIZEOF(Page)); //Empty page string
        HTTPRq.Send:=TRUE; //Send request
        CaseNr:=CaseNr+1; //Program case

        // ---------------------------------------------------------------------
        // Header/Page data are received from server by chunked. When a chunk
        // has been received DBChars returns its length.

        2:
        IF (HTTPRq.DBChars <> 0) THEN

            // Transfer the received chunk on the proper buffer.
            
            IF NOT(HTTPRq.HPSelector) THEN
                IF ((Sysstrlen(ADR(Header))+HTTPRq.DBChars) < SIZEOF(Header)) THEN
                    i:=Sysmemmove(ADR(Header)+Sysstrlen(ADR(Header)), HTTPRq.DBAddress, HTTPRq.DBChars);
                END_IF;
            ELSE
                IF ((Sysstrlen(ADR(Page))+HTTPRq.DBChars) < SIZEOF(Page)) THEN
                    i:=Sysmemmove(ADR(Page)+Sysstrlen(ADR(Page)), HTTPRq.DBAddress, HTTPRq.DBChars);
                END_IF;
            END_IF;
        END_IF;

        // On Done it's possible to test if page has been loaded.

        IF (HTTPRq.Done) THEN
            IF NOT(HTTPRq.PLoad) THEN Errors:=Errors+1; END_IF;
            CaseNr:=0; //Program case
        END_IF;
    END_CASE;

// [End of file]
LogicLab (Ptp156, ST_HTTPClient_ToFile)
PROGRAM ST_HTTPClient_ToFile
VAR
    i : INT; (* Auxiliary variable *)
    Execute : BOOL; (* Execute command *)
    CaseNr : USINT; (* Program case *)
    Fp : eFILEP; (* File pointer *)
    Error : USINT; (* Execution error *)
    HFile : STRING[ 32 ]; (* Header storage file definition *)
    PFile : STRING[ 32 ]; (* Page storage file definition *)
    TCPClient : SysTCPClient; (* TCP client management *)
    HTTPRq : HTTPClient_v4; (* HTTP client *)
END_VAR

// *****************************************************************************
// PROGRAM "ST_HTTPClient_ToFile"
// *****************************************************************************
// The program connects to a server om which is running a PHP script that
// returns 4Kb of text.
// THe header and the page returned are stored into a files.
// -----------------------------------------------------------------------------

    // -------------------------------------------------------------------------
    // INITIALIZATIONS
    // -------------------------------------------------------------------------
    // Program initializations.

    IF (SysFirstLoop) THEN

        // Set TCPClient parameters.

        TCPClient.PeerAdd:=ADR('demos.elsist.biz'); //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.RMethod:=0; //Request method, GET
        HTTPRq.SpyOn:=TRUE; //Activate the spy
        HTTPRq.HostName:=TCPClient.PeerAdd; // Hostname
        HTTPRq.Page:=ADR('eLLabHTTPLib/HTTPClient/ST_HTTPClient_ToFile.php'); //Web page
        HTTPRq.Request:=eNULL; //Request string
        HTTPRq.Header:=eNULL; //HTTP header
        HTTPRq.DBSize:=512; //Data buffer size
        HTTPRq.Timeout:=10.0; //Execution timeout

        // Set local variables.
        // Store data file definition, change according your requirements.

        HFile:='D:/Header.txt'; //Header file definition    
        PFile:='D:/Page.txt'; //Page file definition    
    END_IF;

    // -------------------------------------------------------------------------
    // MANAGE CONNECTION
    // -------------------------------------------------------------------------
    // Manage the connection.

    TCPClient(Connect:=HTTPRq.Connect); //TCPClient management
    HTTPRq(File:=TCPClient.File); //HTTP client

    // -------------------------------------------------------------------------
    // PROGRAM SEQUENCIES
    // -------------------------------------------------------------------------
    // Program sequencies.

    CASE (CaseNr) OF

        // ---------------------------------------------------------------------
        // Wait for the command.

        0:
        HTTPRq.Enable:=FALSE; //HTTP enable
        HTTPRq.Send:=FALSE; //Send request

        IF NOT(Execute) THEN RETURN; END_IF;
        Execute:=FALSE; //Execute command
        Error:=0; //Execution error

        // Initialize buffers and enable client.

        i:=SysFileRemove(ADR(HFile)); //Delete the header storage file
        i:=SysFileRemove(ADR(PFile)); //Delete the pge storage file
        HTTPRq.Enable:=TRUE; //HTTP enable
        HTTPRq.Send:=TRUE; //Send request
        CaseNr:=CaseNr+1; //Program case

        // ---------------------------------------------------------------------
        // Header/Page data are received from server by chunked. When a chunk
        // has been received DBChars returns its length.

        1:
        IF (HTTPRq.DBChars <> 0) THEN

            // Transfer the received chunk on the proper file.

            IF NOT(HTTPRq.HPSelector) THEN
                Fp:=SysFfopen(ADR(HFile), ADR('a')); //File pointer
            ELSE
                Fp:=SysFfopen(ADR(PFile), ADR('a')); //File pointer
            END_IF;

            // Open the file in append mode, if it doesn't exist i'll be created.

            IF (Fp = eNULL) THEN Error:=10; CaseNr:=0; RETURN; END_IF;

            // Transfer received data to file.

            IF (Sysfwrite(HTTPRq.DBAddress, TO_INT(HTTPRq.DBChars), 1, Fp) <> TO_INT(HTTPRq.DBChars)) THEN
                i:=Sysfclose(Fp); //Close the file
                Error:=20; //Execution error
                CaseNr:=0; //Program case
                RETURN;
            END_IF;

            i:=Sysfclose(Fp); //Close the file
        END_IF;

        // Check if request done.
        // On Done it's possible to test if page has been loaded.

        IF (HTTPRq.Done) THEN
            IF NOT(HTTPRq.PLoad) THEN Error:=30; END_IF;
            CaseNr:=0; //Program case
        END_IF;
    END_CASE;

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