HTTPServer, HTTP server management

  1. Home
  2. Knowledge Base
  3. Manualistica
  4. Programmazione IEC 61131-3
  5. Libreria comunicazione HTTP
  6. HTTPServer, HTTP server management

Questo blocco funzione esegue la gestione di un server HTTP, è un blocco funzione protetto per utilizzarlo occorre richiedere il codice di protezione. E' comunque possibile utilizzarlo liberamente in modo test per 30 Min, terminato il tempo il FB continuerà a funzionare ma non gestirà più le variabili pHeader e pRxData.

Attivando Enable è messo in ascolto sulla porta indicata in Port un server HTTP e sono accettate il numero di connessioni definito in AConnections. In TCPBSize occorre definire la dimensione dei buffers allocati 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.

In HTTPBSize occorre definire la dimensione del buffer allocato con SysRMalloc per contenere i dati ricevuti in richiesta (Header e dati) ed il contenuto della pagina di risposta da inviare al client. In HPath occorre definire il  percorso della cartella dove si trovano i files gestiti dal server. In PDefault occorre definire la pagina di default da visualizzare su richiesta del solo indirizzo IP del server.

Alla ricezione di una richiesta dal client si attiva RRcvd e sono valorizzati pPage con indirizzo stringa di definizione pagina richiesta, pHeader con indirizzo buffer memorizzazione header ricevuto, pRxData con indirizzo buffer dati di pagina ricevuti e RxDSize con la dimensione dei dati ricevuti. Se la pagina richiesta è presente nel percorso indicato in HPath, si attiverà anche RFFound.

Sulla attivazione di RRcvd il programma utente può controllare i dati ricevuti in richiesta e preparare i dati in risposta l'indirizzo và indicato in pTxData e la dimensione in TxDSize. Il programma utente deve definire l'header da inviare in risposta definendone l'indirizzo in Header (Vedi definizione). Attivando RAck verranno inviati sia l'header che i dati al client. Attivando RNAck verrà inviato al client solo il campo header.

Function block
CODESYS: Non disponibile
LogicLab: eLLabHTTPLib

Enable (BOOL) Comando abilitazione server.

SpyOn (BOOL) Se attivo permette di spiare il funzionamento del FB (Vedi articolo).

RAck (BOOL) Settato da programma forza invio dell'header e dai dati di pagina.

RNack (BOOL) Settato da programma forza invio del solo header, non viene inviato nessun dato.

Port (UINT) Porta TCP su cui viene posto in ascolto il server, verificare che la porta non sia già utilizzata da un'altro servizio.

AConnections (USINT) Numero di connessioni HTTP contemporanee accettate.

TCPBSize (UINT) Dimensione buffer TCP. Determina la dimensione massima dei pacchetti TCP in ricezione e trasmissione

HTTPBSize (UINT) Dimensione buffer HTTP. Determina la dimensione massima dei dati HTTP, deve avere dimensione sufficente a contenere l'header ed i dati ricevuti.

TxDSize (UDINT) Dimensione dati da inviare al client e caricati in buffer pTxData.

HPath (@STRING) Percorso della cartella dove si trovano i files gestiti dal server. Su richiesta pagina viene ricercato il file in questa cartella, se presente viene ritornato al client, se non presente si attiva RRcvd per poter gestire la richiesta da programma.

PDefault (@STRING) Definizione pagina di default ritornata su richiesta del solo indirizzo IP.

Header (@STRING) Indirizzo stringa header, se NULL viene inviato l'header standard. E' possibile definire parametri da inviare con l'header della risposta.

pTxData (@BYTE) Puntatore al buffer dati da trasmettere al client Alla attivazione di RAck viene inviato l'header seguito dai dati indicati.

TxDSize (UDINT) Dimensione dati da inviare al dient e caricati in buffer pTxData.

Timeout (REAL) Timeout esecuzione (S).

RRcvd (BOOL) Si attiva su ricezione richiesta HTTP, alla attivazione sono validi i dati in pPage, pHeader, pRxData e RxDSize. Si resetta alla attivazione di RAck o RNAck da programma utente.

Fault (BOOL) Attivo per un loop di programma se errore gestione.

pPage (@STRING) Puntatore al buffer che contiene pagina richiesta ricevuta dal client. Il dato è valido alla attivazione di RRcvd, rimane valido fino alla attivazione di RAck o RNAck.

pHeader (@STRING) Puntatore al buffer che contiene header richiesta ricevuto dal client. Il dato è valido alla attivazione di RRcvd, rimane valido fino alla attivazione di RAck o RNAck.

pRxData (@BYTE) Puntatore al buffer dati ricevuti dal client su richieste GET, PUT, POST. Il dato è valido alla attivazione di RRcvd, rimane valido fino alla attivazione di RAck o RNAck.

RxDSize (UDINT) Dimensione dati ricevuti dal client.

CCounter (ARRAY[0..3]OF UDINT) Numero connessioni gestite su ogni connessione HTTP (Utilizzato per debug).

ECounter (UDINT) Numero di errori.

Spionaggio funzionamento

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

Errori

In caso di errore si attiva l'uscita Fault, con la funzione SysGetLastError è possibile rilevare il codice di errore. Fare riferimento alla tabella seguente per la descrizione.

Esempi

Come utilizzare gli esempi.
Viene istanziato un server HTTP sulla porta 2000, eseguendo una richiesta in GET della pagina Division.cgi sono acquisiti i valori di dividendo e divisore e viene ritornato il risultato della divisione. Per testare il programma da un browser usare il link xxx.xxx.xxx.xxx:2000/Division.cgi?Dividend=500&Divisor=10, sostituendo alle x l'indirizzo IP del sistema. Nell'esempio è riportata anche la pagina AjaxPage.html che gestisce l'aggiornamento dei valori in Ajax. Nel Tab HTML Files sono riportati i files da copiare nella cartella definita in HPath.

LogicLab (Ptp156)
PROGRAM ST_HTTPServer
VAR
    i : UDINT; (* Auxiliary variable *)
    CaseNr : USINT; (* Program case *)
    Error : USINT; (* Error number *)
    Ptr : @STRING; (* Auxiliary pointer *)
    Dividend : REAL := 10.0; (* Dividend value *)
    Divisor : REAL := 1.0; (* Divisor value *)
    Result : REAL; (* Result value *)
    RPage : STRING[ 64 ]; (* Page requested *)
    RxData : STRING[ 64 ]; (* Rx data *)
    HTTPSv : HTTPServer_v1; (* HTTP server *)
    TxData : STRING[ 128 ]; (* Tx data *)
    TxHeader : STRING[ 128 ]; (* Tx header buffer *)
    JDecode : JSONDecode_v2; (* JSON decode *)
    JEncode : JSONEncode_v1; (* JSON encode *)
    Counter : UDINT; (* Counter *)
END_VAR

// *****************************************************************************
// PROGRAM "ST_HTTPServer"
// *****************************************************************************
// The program instantiates a HTTP server om port 2000 that accepts up 2
// simultaneus connections.
//
// If the client (ie Web browser) connects to it and requests a page with GET
// parameters it reads the parameters and returns the page. The program can
// be tested with the example of the HTTPClient.
//
// It's possible use a browser to connect to the device IP and request:
// xxx.xxx.xxx.xxx:2000/Division.cgi?Dividend=500&Divisor=10
// it will return a message with the division result.
// -----------------------------------------------------------------------------

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

    IF (SysFirstLoop) THEN
        HTTPSv.Enable:=TRUE; //Server enable
        HTTPSv.SpyOn:=TRUE; //Spy active
        HTTPSv.Port:=2000; //TCP Port
        HTTPSv.AConnections:=2; //Accepted connections
        HTTPSv.TCPBSize:=1024; //TCP buffer size
        HTTPSv.HTTPBSize:=1024; //HTTP buffer size
        HTTPSv.pTxData:=ADR(TxData); //Pointer to Tx data
        HTTPSv.HPath:=ADR('/Storage'); //Home path (ARM 7)
        HTTPSv.HPath:=ADR('/home/pi'); //Home path (Raspberry)
        HTTPSv.HPath:=ADR('C:/WPages'); //Home path (Cortex M7)
        HTTPSv.PDefault:=ADR('Home.htm'); //Default page
        HTTPSv.Timeout:=2.0; //Execution timeout (S)
    END_IF;

    // -------------------------------------------------------------------------
    // HTTP SERVER                                                           
    // -------------------------------------------------------------------------
    // Manage the server and wait until a request is received.

    HTTPSv(); //HTTP server
    HTTPSv.RAck:=FALSE; //Request acknowledge
    HTTPSv.RNAck:=FALSE; //Request not acknowledge

    // -------------------------------------------------------------------------
    // MANAGE THE REQUEST
    // -------------------------------------------------------------------------
    // The request is managed in a batch program.

    CASE (CaseNr) OF

        // ---------------------------------------------------------------------
        // WAIT FOR THE REQUEST
        // ---------------------------------------------------------------------
        // Waits until a request is received..

        0:
        IF NOT(HTTPSv.RRcvd) THEN CaseNr:=0; RETURN; END_IF;
        
        // Copy the request page with the complete path to a buffer.

        IF (Sysstrlen(HTTPSv.pPage) <= SIZEOF(RPage)) THEN
            i:=Sysmemmove(ADR(RPage), HTTPSv.pPage, Sysstrlen(HTTPSv.pPage));
        END_IF;

        // A request has been received, is managed. Copy received data if any.

        IF ((HTTPSv.RxDSize <> 0) AND (HTTPSv.RxDSize <= SIZEOF(RxData))) THEN
            i:=Sysmemmove(ADR(RxData), HTTPSv.pRxData, HTTPSv.RxDSize);
        END_IF;

        // Check if requested a file present in the root directory,So the FB
        // sends back the content by itself. Here define the type of content.
        // Please refer to https://www.sitepoint.com/mime-types-complete-list/

        IF (HTTPSv.RFFound) THEN
               TxHeader:='HTTP/1.1 200 OK$r$nContent-Type: ';
            IF (SysStrFind(HTTPSv.pPage, ADR('.htm'), FIND_DEFAULT) <> eNULL) THEN TxHeader:=CONCAT(TxHeader, 'text/html$r$n'); END_IF;
            IF (SysStrFind(HTTPSv.pPage, ADR('.html'), FIND_DEFAULT) <> eNULL) THEN TxHeader:=CONCAT(TxHeader, 'text/html$r$n'); END_IF;
            IF (SysStrFind(HTTPSv.pPage, ADR('.txt'), FIND_DEFAULT) <> eNULL) THEN TxHeader:=CONCAT(TxHeader, 'text/plain$r$n'); END_IF;
            IF (SysStrFind(HTTPSv.pPage, ADR('.json'), FIND_DEFAULT) <> eNULL) THEN TxHeader:=CONCAT(TxHeader, 'application/json$r$n'); END_IF;
            HTTPSv.Header:=ADR(TxHeader); //Header definition
            HTTPSv.RAck:=TRUE; //Request acknowledge
            RETURN;
        END_IF;

        // Has been request a file that is not present in the root directory.
        // Check if it's a supported script. Otherwise redirect the browser.

        IF (SysStrFind(HTTPSv.pPage, ADR('Division.cgi'), FIND_DEFAULT) <> eNULL) THEN CaseNr:=10; RETURN; END_IF;
        IF (SysStrFind(HTTPSv.pPage, ADR('AjaxPage.cgi'), FIND_DEFAULT) <> eNULL) THEN CaseNr:=20; RETURN; END_IF;
        HTTPSv.Header:=ADR('HTTP/1.1 303 See Other$r$nLocation: Redirect.htm$r$n'); HTTPSv.RNAck:=TRUE; RETURN;

        // ---------------------------------------------------------------------
        // "Division.cgi" SCRIPT
        // ---------------------------------------------------------------------
        // Manage the script.

        10:
        Ptr:=SysStrFind(HTTPSv.pRxData, ADR('Dividend='), FIND_GET_END); //Auxiliary pointer
        IF (Ptr = NULL) THEN CaseNr:=100; RETURN; END_IF;
        IF NOT(SysVsscanf(Ptr, ADR('%f'), REAL_TYPE, ADR(Dividend))) THEN Error:=11; CaseNr:=100; RETURN; END_IF;

        Ptr:=SysStrFind(HTTPSv.pRxData, ADR('Divisor='), FIND_GET_END); //Auxiliary pointer
        IF (Ptr = NULL) THEN CaseNr:=100; RETURN; END_IF;
        IF NOT(SysVsscanf(Ptr, ADR('%f'), REAL_TYPE, ADR(Divisor))) THEN Error:=21; CaseNr:=100; RETURN; END_IF;

        Result:=Dividend/Divisor; //Result value

        HTTPSv.TxDSize:=SysVsnprintf(HTTPSv.pTxData, HTTPSv.HTTPBSize, ADR('Result=%.3f'), REAL_TYPE, ADR(Result));
        HTTPSv.Header:=ADR('HTTP/1.1 200 OK$r$nContent-Type: text/html$r$n');
        HTTPSv.RAck:=TRUE; //Request acknowledge
        CaseNr:=0; //Program case

        // ---------------------------------------------------------------------
        // "AjaxPage.cgi" SCRIPT
        // ---------------------------------------------------------------------
        // Manage the script.

        20:
        JDecode(Object:=HTTPSv.pRxData, Name:=ADR('Dividend'), VType:=REAL_TYPE, VAddress:=ADR(Dividend), Count:=1);
        JDecode(Object:=HTTPSv.pRxData, Name:=ADR('Divisor'), VType:=REAL_TYPE, VAddress:=ADR(Divisor), Count:=1);

        // Calculate and increase counter.

        Counter:=Counter+1; //Counter
        Result:=Dividend/Divisor; //Result value

        // Return values to the browser.

        i:=Sysmemset(ADR(TxData), 0, SIZEOF(TxData)); //Initialize the Tx data
        JEncode(Object:=ADR(TxData), OSize:=SIZEOF(TxData), Name:=ADR('Counter'), VType:=UDINT_TYPE, VAddress:=ADR(Counter), Count:=1);
        JEncode(Object:=ADR(TxData), OSize:=SIZEOF(TxData), Name:=ADR('Dividend'), VType:=REAL_TYPE, VAddress:=ADR(Dividend), Count:=1);
        JEncode(Object:=ADR(TxData), OSize:=SIZEOF(TxData), Name:=ADR('Divisor'), VType:=REAL_TYPE, VAddress:=ADR(Divisor), Count:=1);
        JEncode(Object:=ADR(TxData), OSize:=SIZEOF(TxData), Name:=ADR('Result'), VType:=REAL_TYPE, VAddress:=ADR(Result), Count:=1);
        HTTPSv.TxDSize:=Sysstrlen(ADR(TxData)); //Tx data size

        HTTPSv.Header:=ADR('HTTP/1.1 200 OK$r$nContent-Type: application/json$r$n');
        HTTPSv.RAck:=TRUE; //Request acknowledge
         CaseNr:=0; //Program case

        // ---------------------------------------------------------------------
        // Arrive on request error.

        100:
        HTTPSv.TxDSize:=SysVsnprintf(HTTPSv.pTxData, HTTPSv.HTTPBSize, ADR('Error: %d, on request'), USINT_TYPE, ADR(Error));
        HTTPSv.Header:=ADR('HTTP/1.1 200 OK$r$n');
        HTTPSv.RAck:=TRUE; //Request acknowledge
    END_CASE;

// [End of file]
HTML Files

Ti è stato utile questo articolo ?

Ultimo aggiornamento: 23 Febbraio 2021