Realizzare pagine web in Ajax con FB HTTPServer

  1. Home
  2. Knowledge Base
  3. PLC SlimLine/NetSyst Cortex M7/ARM7
  4. Programmazione LogicLab
  5. Esempi
  6. Realizzare pagine web in Ajax con FB HTTPServer

Scopo del programma

Il programma HTTPServerDemo trattato in questo articolo (Download programma) dimostra come utilizzare il FB HTTPServer per realizzare in modo semplice pagine HTML responsive che permettono di interagire con il programma PLC scambiando dati in formato JSON in tecnica Ajax con il modulo CPU Compact Ethernet da browser.

Il programma è fornito in un file zip che oltre al programma sorgente da aprire con LogicLab contiene la cartella WPages con la pagina Home.htm da trasferire nella cartella Storage dei sistemi Compact o nella cartella C:/WPages dei Cortex M7.

Come si vede dalla foto nella pagina web visualizzata dal browser è ritornato lo stato degli ingressi del modulo ed è possibile comandarne le uscite. Il programma si basa sul FB HTTPServer che permette di gestire fino a 2 connessioni contemporanee.

HTTPServer_Demo

Inizializzazione e gestione server

Nella inizializzazione viene configurato il server HTTP impostando il numero di connessioni contemporanee gestite, la porta in ascolto (Nel nostro esempio è la 2000), la directory in cui si trovano le pagine da visualizare "C/WPages/" e la pagina di default che viene ritornata "Home.htm" quando è richiesto il solo indirizzo IP del dispositivo. Viene impostata anche la dimensione del buffer TCP che determina la dimensione massima dei pacchetti TCP scambiati con il client e la dimensione del buffer HTTP che deve essere dimensionata per contenere oltre all'header di richiesta anche tutti i dati ricevuti dal client (Esempio in caso di richiesta POST).

Viene poi gestito il server che accetta le connessioni dai clients (Web browser) sulla porta 2000. Appena dopo l'esecuzione si resettano le variabili RAck e RNAck che sono da settare nella gestione dei dati ricevuti dal client per inviare la risposta al client. Alla ricezione della richiesta di una pagina la pagina viene automaticamente ritornata se presente nella cartella definita in HPath. Se non è presente viene attivato RRcvd che fà proseguire l'esecuzione nella gestione delle sequenze.

    // -------------------------------------------------------------------------
    // 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:=512; //HTTP buffer size        
        HTTPSv.HPath:=ADR('/Storage/'); //Home path (ARM 7)
        HTTPSv.HPath:=ADR('C:/WPages/'); //Home path (Cortex M7)
        HTTPSv.HPath:=ADR('/tmp/'); //Home path (Raspberry)
        HTTPSv.PDefault:=ADR('Home.htm'); //Default page
        HTTPSv.Timeout:=2.0; //Execution timeout (S)
    END_IF;

    // -------------------------------------------------------------------------
    // HTTP SERVER                                                           
    // -------------------------------------------------------------------------
    // Manage the server.

    HTTPSv(); //HTTP server
    HTTPSv.RAck:=FALSE; //Request acknowledge
    HTTPSv.RNAck:=FALSE; //Request not acknowledge
    IF NOT(HTTPSv.RRcvd) THEN CaseNr:=0; RETURN; END_IF;

Verifica pagina e ricezione comando output

La parte di programma che gestisce i dati ricevuti dal browser ed invia la risposta è eseguito a stati. Nel case 0 si trasferisce la pagina richiesta ed i dati ricevuti in GET o POST in due buffers per permetterne il debug.

Poi si controlla la pagina richiesta dal browser se è Read.cgi si passa al case 20 dove sarà gestita la risposta.

Se è Command.cgi si passa al case 10, questa pagina è richiesta dal browser in POST che invia anche una stringa JSON del tipo {"DOut":1}. DOut indica il comando di inversione stato uscita digitale e 1 è l'uscita da invertire. Con il FB JDecode si decodifica la stringa e se trovata variabile DOut in base al valore si inverte lo stato del relativo bit di uscita. Poi si passa al case 20 che ritornerà lo stato degli ingressi e delle uscite al browser per la visualizzazione.

    // -------------------------------------------------------------------------
    // CASES MANAGEMENT
    // -------------------------------------------------------------------------
    // Program management cases.

    CASE (CaseNr) OF

        // ---------------------------------------------------------------------
        // Copy to buffers the page and data received on request.
        // This is only to debug purposes.

        0:
        i:=Sysmemmove(ADR(RPage), HTTPSv.pPage, Sysstrlen(HTTPSv.pPage));
        i:=Sysmemmove(ADR(RData), HTTPSv.pRxData, Sysstrlen(HTTPSv.pRxData));

        // Check the requested page.

        IF (SysStrFind(HTTPSv.pPage, ADR('Read.cgi'), FIND_DEFAULT) = HTTPSv.pPage) THEN CaseNr:=20; RETURN; END_IF;
        IF (SysStrFind(HTTPSv.pPage, ADR('Command.cgi'), FIND_DEFAULT) = HTTPSv.pPage) THEN CaseNr:=10; RETURN; END_IF;
        HTTPSv.RNAck:=TRUE; //Request not acknowledge
        CaseNr:=0; //Program case

        // ---------------------------------------------------------------------
        // MANAGE THE "Command.cgi" PAGE
        // ---------------------------------------------------------------------
        // Acquire POST data.

        10:
        JDecode(Object:=HTTPSv.pRxData, Name:=ADR('DOut'), VType:=UDINT_TYPE, VAddress:=ADR(i));
        IF (JDecode.ECode <> 0) THEN Error:=10; CaseNr:=100; RETURN; END_IF;
        CASE (i) OF
            0: DOut.Value:=DOut.Value XOR 16#00000001;
            1: DOut.Value:=DOut.Value XOR 16#00000002;
            2: DOut.Value:=DOut.Value XOR 16#00000004;
            3: DOut.Value:=DOut.Value XOR 16#00000008;
        END_CASE;
        CaseNr:=20; //Program case

        // ---------------------------------------------------------------------
        // MANAGE THE "Read.cgi" PAGE
        // ---------------------------------------------------------------------
        // Acquires digital inputs and returns their status.
        
        20:
        i:=Sysmemset(HTTPSv.pTxData, 0, HTTPSv.HTTPBSize); //Tx buffer clear
        DInp(Address:=255, Mode:=DI_8_LL); //Digital inputs acquisistion

        FOR i:=0 TO 5 DO
            IF TO_BOOL(DInp.Value AND 16#00000001) THEN LEDStatus[i]:=ADR('red'); ELSE LEDStatus[i]:=ADR('#303030'); END_IF;
            DInp.Value:=DInp.Value/2;
        END_FOR;

        JEncode(Object:=HTTPSv.pTxData, OSize:=HTTPSv.HTTPBSize, Name:=ADR('Inputs'), VType:=STRING_TYPE, VAddress:=ADR(LEDStatus), Count:=6);

        // Sets digital outputs and returns their status.
        
        DOut(Address:=255, Mode:=DO_8_LL, Mask:=16#0000000F); // Digital inputs acquisistion
        j:=DOut.Value; //Auxiliary variable
        FOR i:=0 TO 3 DO
            IF TO_BOOL(j AND 16#00000001) THEN LEDStatus[i]:=ADR('red'); ELSE LEDStatus[i]:=ADR('#303030'); END_IF;
            j:=j/2; //Auxiliary variable
        END_FOR;

        JEncode(Object:=HTTPSv.pTxData, OSize:=HTTPSv.HTTPBSize, Name:=ADR('Outputs'), VType:=STRING_TYPE, VAddress:=ADR(LEDStatus), Count:=4);

        // Send data to client.

        HTTPSv.TxDSize:=Sysstrlen(HTTPSv.pTxData); //Answer data length
        HTTPSv.RAck:=TRUE; //Request acknowledge
        CaseNr:=0; //Program case

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

        100:
        HTTPSv.TxDSize:=SysVarsnprintf(HTTPSv.pTxData, HTTPSv.HTTPBSize, 'Error: %d, on request', USINT_TYPE, ADR(Error));
        HTTPSv.RAck:=TRUE; //Request acknowledge
        CaseNr:=0; //Program case
    END_CASE;

Pagina web

Per la visualizzazione nel browser è fornita la pagina Home.htm, questa è una pagina responsive realizzata con la libreria Skeleton. Non mi dilungo sulla gestione dei css per il responsive mentre è importante soffrmarci sulle funzioni javascript di aggiornamento pagina.

PRefresh: Eseguita al caricamento pagina ed ogni secondo invia una richiesta Ajax in GET della pagina Read.cgi che come visto nel programma farà saltare al case 20 dove viene preparata ed inviata la stringa JSON di risposta.

WCommand: Eseguita sulla pressione degli oggetti di visualizzazione uscite digitali, invia una richiesta Ajax in POST della pagina Command.cgi con l'indicazione dell'uscita comandata, che come visto nel programma farà saltare al case 10 dove viene gestita l'uscita e poi al case 20 dove viene preparata ed inviata la stringa JSON di risposta.

PUpdate: Richiamata dalle due funzioni precedenti riceve in PContent la stringa JSON ricevuta dal programma e provvede ad aggiornare il colore di background degli oggetti. Viene anche eseguita l'uscta sulla console del brower dei dati ricevuti per poterli visualizzare attivando la console del browser (Chrome o Firefox).

function PRefresh()
{
  GETAjax('Read.cgi', function(PContent){PUpdate(PContent);});
}

function WCommand(Do)
{
  POSTAjax('Command.cgi', '{"DOut":'+Do+'}', function(PContent){PUpdate(PContent);});
}

function PUpdate(PContent)
{
  console.warn(PContent);
  var Obj = JSON.parse(PContent);

  // Eseguo gestione LED I/O digitali.

  for (i=0; i<6; i++) document.getElementById("Di0"+i).style.backgroundColor = Obj.Inputs[i];
  for (i=0; i<4; i++) document.getElementById("Do0"+i).style.backgroundColor = Obj.Outputs[i];
}

Conclusioni

La disponibilità del FB HTTPServer permette di aggiungere uno o più server web nel proprio programma PLC permettendo di gestire scambio dati in JSON con il browser e/o di gestire servizi web come ad esempio richieste REST.

Ti è stato utile questo articolo ?

Ultimo aggiornamento: 14 Settembre 2019