Questo blocco funzione da eseguire in task Back, implementai 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 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.
In caso di errore si attiva per un loop l’uscita Fault, con la funzione SysGetLastError è possibile rilevare il codice di errore. Fare riferimento alla tabella elenco errori per la descrizione.
Approfondimenti
- In questo topic un programma per gestire allarmi su richieste GET da telecamera.
Descrizione
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.
HTTPBSize (UDINT) Dimensione buffer HTTP. Determina la dimensione massima dei dati HTTP, deve avere dimensione sufficente a contenere l’header ed i dati ricevuti.
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.
CORSHeader (@STRING) Definizione header richieste Cross-Origin.
StatusCode (@STRING) Definizione codice stato ritornato al client.
pTxData (@BYTE) Puntatore al buffer dati da trasmettere al client, non può essere allocato con SysRMAlloc. 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.
RFFound (BOOL) Attvo se pagina richiesta è stata trovata in HPath ed è stata inviata al client.
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.
pRMethod (@STRING) Indirizzo buffer che riporta metodo richiesta ricevuta dal client.
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.
Trigger di spionaggio
16#00000001 | Rx: Ricezione dati da client HTTP. |
16#00000002 | Tx: Trasmissione dati al client HTTP. |
16#10000000 | Lg: Messaggi di log funzionamento. |
16#40000000 | Er: Errori di esecuzione. |
Esempi
Gestione server con autenticazione (Ptp194)
Il programma dimostrativo Ptp194 (Download) realizza la gestione di un server HTTP con autenticazione per l’accesso. Digitando da un browser l’IP del dispositivo seguito dal numero di porta su cui il server è attivo (Esempio http://xxx.xxx.xxx.xxx:2000) verrà visualizzata la pagina di richiesta credenziali di accesso (Default Admin:Admin). Eseguito l’accesso sarà possibile navigare tra le pagine del sito.
Un pulsante di Logout dalla pagina Home.html permetterà di eseguire il logout, nel caso in cui non si eseguano accessi per un tempo superiore a 120 secondi l’utente verrà automaticamente sloggato dal sistema.
L’esempio può essere eseguito su qualunque sistema SlimLine occorre trasferire le pagine presenti nella cartella WebPages che và trasferita sul disco D: del sistema (Se non si dispone della SDCard è possibile trasferire la cartella sul disco C:, in tal caso occorre modificare la directory root nel progetto).
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.
HTML
LogicLab (PTP156, ST_HTTPServer)
PROGRAM ST_HTTPServer
VAR
CaseNr : USINT; (* Program case *)
Error : USINT; (* Error number *)
Counter : UDINT; (* Counter *)
Dividend : REAL := 10.0; (* Dividend value *)
Divisor : REAL := 1.0; (* Divisor value *)
Result : REAL; (* Result value *)
Ptr : @STRING; (* Auxiliary pointer *)
RPage : STRING[ 64 ]; (* Page requested *)
RxData : STRING[ 64 ]; (* Rx data *)
TxData : STRING[ 128 ]; (* Tx data *)
TxHeader : STRING[ 128 ]; (* Tx header buffer *)
HTTPSv : HTTPServer_v2; (* HTTP server *)
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.HTTPBSize:=1024; //HTTP buffer size
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.html'); //Default page
HTTPSv.Timeout:=2.0; //Execution timeout (S)
HTTPSv.CORSHeader:=ADR('Access-Control-Allow-Origin: *$r$nAccess-Control-Allow-Methods: GET, POST, OPTIONS$r$nAccess-Control-Allow-Headers: Content-Type$r$n');
HTTPSv.pTxData:=ADR(TxData); //Pointer to Tx data
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
IF NOT(HTTPSv.RRcvd) THEN CaseNr:=0; RETURN; END_IF;
HTTPSv.StatusCode:=eNULL; //Status code
HTTPSv.TxDSize:=0; //Data to send size
// -------------------------------------------------------------------------
// MANAGE THE REQUEST
// -------------------------------------------------------------------------
// The request is managed in a batch program.
CASE (CaseNr) OF
// ---------------------------------------------------------------------
// WAIT FOR THE REQUEST
// ---------------------------------------------------------------------
// Copy the request page with the complete path to a buffer.
0:
IF (Sysstrlen(HTTPSv.pPage) <= SIZEOF(RPage)) THEN
eTO_JUNK(Sysmemmove(ADR(RPage), HTTPSv.pPage, Sysstrlen(HTTPSv.pPage)+1));
END_IF;
// Check if a "OPTIONS" request has been received.
IF (SysStrFind(HTTPSv.pRMethod, ADR('OPTIONS'), FIND_DEFAULT) <> eNULL) THEN
HTTPSv.RAck:=TRUE; //Request acknowledge
CaseNr:=0; //Program case
RETURN;
END_IF;
// A request has been received, is managed. Copy received data if any.
IF ((HTTPSv.RxDSize <> 0) AND (HTTPSv.RxDSize <= SIZEOF(RxData))) THEN
eTO_JUNK(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.
IF (HTTPSv.RFFound) THEN HTTPSv.Header:=eNULL; HTTPSv.RAck:=TRUE; 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.StatusCode:=ADR('HTTP/1.1 303 See Other$r$n');
HTTPSv.Header:=ADR('Location: Redirect.html$r$n');
HTTPSv.RNAck:=TRUE; //Request not acknowledge
RETURN;
// ---------------------------------------------------------------------
// "Division.cgi" SCRIPT
// ---------------------------------------------------------------------
// Manage the script.
10:
Ptr:=SysStrFind(HTTPSv.pRxData, ADR('Dividend='), FIND_GET_END); //Auxiliary pointer
IF (Ptr = eNULL) THEN Error:=10; 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 = eNULL) THEN Error:=20; 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:=TO_UDINT(SysVsnprintf(HTTPSv.pTxData, HTTPSv.HTTPBSize, ADR('Result=%.3f'), REAL_TYPE, ADR(Result)));
HTTPSv.Header:=ADR('Content-Type: text/html$r$n');
HTTPSv.RAck:=TRUE; //Request acknowledge
CaseNr:=0; //Program case
// ---------------------------------------------------------------------
// "AjaxPage.cgi" SCRIPT
// ---------------------------------------------------------------------
// Manage the script.
20:
eTO_JUNK(JSONDecoder(HTTPSv.pRxData, ADR('Dividend'), REAL_TYPE, ADR(Dividend), 1, 0));
eTO_JUNK(JSONDecoder(HTTPSv.pRxData, ADR('Divisor'), REAL_TYPE, ADR(Divisor), 1, 0));
// Calculate and increase counter.
Counter:=Counter+1; //Counter
Result:=Dividend/Divisor; //Result value
// Return values to the browser.
eTO_JUNK(Sysmemset(ADR(TxData), 0, SIZEOF(TxData))); //Initialize the Tx data
eTO_JUNK(JSONEncoder(ADR(TxData), SIZEOF(TxData), ADR('Counter'), UDINT_TYPE, ADR(Counter), 1));
eTO_JUNK(JSONEncoder(ADR(TxData), SIZEOF(TxData), ADR('Dividend'), REAL_TYPE, ADR(Dividend), 1));
eTO_JUNK(JSONEncoder(ADR(TxData), SIZEOF(TxData), ADR('Divisor'), REAL_TYPE, ADR(Divisor), 1));
eTO_JUNK(JSONEncoder(ADR(TxData), SIZEOF(TxData), ADR('Result'), REAL_TYPE, ADR(Result), 1));
HTTPSv.TxDSize:=Sysstrlen(ADR(TxData)); //Tx data size
HTTPSv.Header:=ADR('Content-Type: application/json$r$n');
HTTPSv.RAck:=TRUE; //Request acknowledge
CaseNr:=0; //Program case
// ---------------------------------------------------------------------
// Arrive on request error.
100:
HTTPSv.TxDSize:=TO_UDINT(SysVsnprintf(HTTPSv.pTxData, HTTPSv.HTTPBSize, ADR('Error: %d, on request'), USINT_TYPE, ADR(Error)));
HTTPSv.Header:=ADR('Content-Type: text/html$r$n');
HTTPSv.RAck:=TRUE; //Request acknowledge
END_CASE;
// [End of file]