mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2025-02-17 06:29:15 +01:00
NetworkInterface integrated
This commit is contained in:
parent
c9c5f6a67b
commit
d00864cbf4
@ -64,7 +64,7 @@ void setup()
|
||||
|
||||
DIAG(F("\nFree RAM before network init: [%d]\n"),freeMemory());
|
||||
DIAG(F("\nNetwork Setup In Progress ...\n"));
|
||||
NetworkInterface::setup(WIFI, TCP, 8888); // specify WIFI or ETHERNET depending on if you have Wifi or an EthernetShield; Wifi has to be on Serial1 UDP or TCP for the protocol
|
||||
NetworkInterface::setup(ETHERNET, TCP, 8888); // specify WIFI or ETHERNET depending on if you have Wifi or an EthernetShield; Wifi has to be on Serial1 UDP or TCP for the protocol
|
||||
NetworkInterface::setHttpCallback(httpRequestHandler); // The network interface will provide and HTTP request object which can be used as well to send the reply. cf. example above
|
||||
|
||||
// NetworkInterface::setup(WIFI, UDP, 8888); // Setup without port will use the by default port 2560 :: Wifi+UDP IS NOT YET SUPPORTED
|
||||
|
23
DIAG copy.h
23
DIAG copy.h
@ -1,23 +0,0 @@
|
||||
/*
|
||||
* © 2020, Chris Harlow. All rights reserved.
|
||||
*
|
||||
* This file is part of Asbelos DCC API
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* It is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef DIAG_h
|
||||
#define DIAG_h
|
||||
#include "StringFormatter.h"
|
||||
#define DIAG StringFormatter::diag
|
||||
#endif
|
@ -54,7 +54,7 @@ EthernetServer* EthernetSetup::setup()
|
||||
}
|
||||
else if (Ethernet.hardwareStatus() == EthernetW5500)
|
||||
{
|
||||
DIAG(F("W5500 Ethernet controller detected."));
|
||||
DIAG(F("\nW5500 Ethernet controller detected."));
|
||||
maxConnections = 8;
|
||||
}
|
||||
|
||||
|
@ -19,8 +19,6 @@
|
||||
|
||||
#include "DIAG.h"
|
||||
#include "NetworkInterface.h"
|
||||
// #include "Singelton.h"
|
||||
// #include "WifiTransport.h"
|
||||
#include "EthernetSetup.h"
|
||||
#include "WifiSetup.h"
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include <Ethernet.h>
|
||||
#include <WiFiEspAT.h>
|
||||
|
||||
#include <NetworkInterface.h>
|
||||
#include "NetworkInterface.h"
|
||||
#include "TransportProcessor.h"
|
||||
|
||||
|
||||
@ -80,4 +80,4 @@ public:
|
||||
|
||||
};
|
||||
|
||||
#endif // !Transport_h
|
||||
#endif // !Transport_h
|
||||
|
@ -16,15 +16,20 @@
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "DIAG.h"
|
||||
// #include "DCCEXParser.h"
|
||||
|
||||
#include "NetworkInterface.h"
|
||||
#include "HttpRequest.h"
|
||||
#include "TransportProcessor.h"
|
||||
|
||||
// DCCEXParser ethParser;
|
||||
#ifdef DCCEX_ENABLED
|
||||
|
||||
#include "DCCEXParser.h"
|
||||
#include "MemStream.h"
|
||||
|
||||
DCCEXParser ethParser;
|
||||
|
||||
#endif
|
||||
|
||||
static uint8_t buffer[MAX_ETH_BUFFER];
|
||||
static char command[MAX_JMRI_CMD] = {0};
|
||||
@ -38,6 +43,76 @@ char protocolName[4][11] = {"JMRI", "WITHROTTLE", "HTTP", "UNKNOWN"}; // change
|
||||
bool diagNetwork = false;
|
||||
uint8_t diagNetworkClient = 0;
|
||||
|
||||
#ifdef DCCEX_ENABLED
|
||||
/**
|
||||
* @brief Sending a reply by using the StringFormatter (this will result in every byte send individually which may/will create an important Network overhead).
|
||||
* Here we hook back into the DCC code for actually processing the command using a DCCParser. Alternatively we could use MemeStream in order to build the entiere reply
|
||||
* before ending it.
|
||||
*
|
||||
* @param stream Actually the Client to whom to send the reply. As Clients implement Print this is working
|
||||
* @param command The reply to be send ( echo as in sendReply() )
|
||||
* @param blocking if set to true will instruct the DCC code to not use the async callback functions
|
||||
*/
|
||||
|
||||
void sendToDCC(Connection* c ,char *command, bool blocking)
|
||||
{
|
||||
static MemStream* streamer = new MemStream((byte *)command, MAX_ETH_BUFFER, MAX_ETH_BUFFER, true);
|
||||
|
||||
DIAG(F("DCC parsing: [%e]\n"), command);
|
||||
// as we use buffer for recv and send we have to reset the write position
|
||||
streamer->setBufferContentPosition(0, 0);
|
||||
|
||||
ethParser.parse(streamer, (byte *)command, true); // set to true to that the execution in DCC is sync
|
||||
|
||||
if (streamer->available() == 0)
|
||||
{
|
||||
DIAG(F("No response\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
command[streamer->available()] = '\0'; // mark end of buffer, so it can be used as a string later
|
||||
DIAG(F("Response: %s\n"), command);
|
||||
if (c->client->connected())
|
||||
{
|
||||
c->client->write((byte *)command, streamer->available());
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @brief Sending a reply without going through the StringFormatter. Sends the repy in one go
|
||||
*
|
||||
* @param client Client who send the command to which the reply shall be send
|
||||
* @param command Command initaliy recieved to be echoed back
|
||||
*/
|
||||
void sendReply(Connection* c, char *command)
|
||||
{
|
||||
char *number;
|
||||
char seqNumber[6];
|
||||
int i = 0;
|
||||
|
||||
memset(reply, 0, MAX_ETH_BUFFER); // reset reply
|
||||
|
||||
number = strrchr(command, ':'); // replace the int after the last ':'
|
||||
while( &command[i] != number ) { // copy command into the reply upto the last ':'
|
||||
reply[i] = command[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
strcat((char *)reply, ":");
|
||||
itoa(_sseq[c->id], seqNumber, 10);
|
||||
strcat((char *)reply, seqNumber);
|
||||
strcat((char *)reply, ">");
|
||||
|
||||
DIAG(F("Response: [%e]"), (char *)reply);
|
||||
if (c->client->connected())
|
||||
{
|
||||
c->client->write(reply, strlen((char *)reply));
|
||||
_sseq[c->id]++;
|
||||
DIAG(F(" send\n"));
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief creates a HttpRequest object for the user callback. Some conditions apply esp reagrding the length of the items in the Request
|
||||
@ -48,6 +123,7 @@ uint8_t diagNetworkClient = 0;
|
||||
*/
|
||||
void httpProcessor(Connection* c)
|
||||
{
|
||||
|
||||
if (httpReq.callback == 0) return; // no callback i.e. nothing to do
|
||||
/**
|
||||
* @todo look for jmri formatted uris and execute those if there is no callback. If no command found ignore and
|
||||
@ -163,6 +239,8 @@ appProtocol setAppProtocol(char a, char b, Connection *c)
|
||||
* @brief Parses the buffer to extract commands to be executed
|
||||
*
|
||||
*/
|
||||
|
||||
// void TransportProcessor::processStream(Connection *c)
|
||||
void processStream(Connection *c)
|
||||
{
|
||||
uint8_t i, j, k, l = 0;
|
||||
@ -181,18 +259,22 @@ void processStream(Connection *c)
|
||||
|
||||
// check if there is again an overflow and copy if needed
|
||||
if ((i = strlen((char *)buffer)) == MAX_ETH_BUFFER - 1)
|
||||
{
|
||||
{ // only then we shall be in an overflow situation
|
||||
// DIAG(F("\nPossible overflow situation detected: %d "), i);
|
||||
j = i;
|
||||
while (buffer[i] != c->delimiter)
|
||||
{
|
||||
{ // what if there is none: ?
|
||||
// DIAG(F("%c"),(char) buffer[i]);
|
||||
i--;
|
||||
}
|
||||
i++; // start of the buffer to copy
|
||||
i++; // start of the buffer to copy
|
||||
l = i;
|
||||
k = j - i; // length to copy
|
||||
k = j - i; // length to copy
|
||||
|
||||
for (j = 0; j < k; j++, i++)
|
||||
{
|
||||
c->overflow[j] = buffer[i];
|
||||
// DIAG(F("\n%d %d %d %c"),k,j,i, buffer[i]); // c->overflow[j]);
|
||||
}
|
||||
buffer[l] = '\0'; // terminate buffer just after the last '>'
|
||||
// DIAG(F("\nNew buffer: [%s] New overflow: [%s]\n"), (char*) buffer, c->overflow );
|
||||
@ -200,19 +282,24 @@ void processStream(Connection *c)
|
||||
|
||||
// breakup the buffer using its changed length
|
||||
i = 0;
|
||||
k = strlen(command);
|
||||
k = strlen(command); // current length of the command buffer telling us where to start copy in
|
||||
l = strlen((char *)buffer);
|
||||
// DIAG(F("\nCommand buffer: [%s]:[%d:%d:%d]\n"), command, i, l, k );
|
||||
DIAG(F("\nCommand buffer: [%s]:[%d:%d:%d]\n"), command, i, l, k );
|
||||
while (i < l)
|
||||
{
|
||||
// DIAG(F("\nl: %d k: %d , i: %d"), l, k, i);
|
||||
command[k] = buffer[i];
|
||||
if (buffer[i] == c->delimiter)
|
||||
{
|
||||
command[k+1] = '\0';
|
||||
DIAG(F("Command: [%d:%e]\n"),_rseq[c->id], command);
|
||||
{ // closing bracket need to fix if there is none before an opening bracket ?
|
||||
|
||||
parse(c, (byte *)command, true);
|
||||
// sendReply(c->client, command, c);
|
||||
command[k+1] = '\0';
|
||||
|
||||
DIAG(F("Command: [%d:%e]\n"),_rseq[c->id], command);
|
||||
#ifdef DCCEX_ENABLED
|
||||
sendToDCC(c, command, true);
|
||||
#else
|
||||
sendReply(c, command);
|
||||
#endif
|
||||
_rseq[c->id]++;
|
||||
j = 0;
|
||||
k = 0;
|
||||
@ -248,7 +335,7 @@ void withrottleProcessor(Connection *c)
|
||||
/**
|
||||
* @brief Reads what is available on the incomming TCP stream and hands it over to the protocol handler.
|
||||
*
|
||||
* @param c Pointer to the connection contining relevant information handling the data from that connection
|
||||
* @param c Pointer to the connection struct contining relevant information handling the data from that connection
|
||||
*/
|
||||
|
||||
void TransportProcessor::readStream(Connection *c)
|
||||
@ -293,10 +380,12 @@ void TransportProcessor::readStream(Connection *c)
|
||||
}
|
||||
}
|
||||
|
||||
IPAddress remote = c->client->remoteIP();
|
||||
// IPAddress remote = c->client->remoteIP(); // only available in my modified Client.h file
|
||||
|
||||
buffer[count] = '\0'; // terminate the string properly
|
||||
DIAG(F("\nReceived packet of size:[%d] from [%d.%d.%d.%d]\n"), count, remote[0], remote[1], remote[2], remote[3]);
|
||||
|
||||
// DIAG(F("\nReceived packet of size:[%d] from [%d.%d.%d.%d]\n"), count, remote[0], remote[1], remote[2], remote[3]);
|
||||
DIAG(F("\nReceived packet of size:[%d]\n"), count);
|
||||
DIAG(F("Client #: [%d]\n"), c->id);
|
||||
DIAG(F("Packet: [%e]\n"), buffer);
|
||||
|
||||
@ -307,49 +396,75 @@ void TransportProcessor::readStream(Connection *c)
|
||||
/**
|
||||
* @brief Sending a reply by using the StringFormatter (this will result in every byte send individually which may/will create an important Network overhead).
|
||||
* Here we hook back into the DCC code for actually processing the command using a DCCParser. Alternatively we could use MemeStream in order to build the entiere reply
|
||||
* before ending it.
|
||||
* before ending it (cf. Scratch pad below)
|
||||
*
|
||||
* @param stream Actually the Client to whom to send the reply. As Clients implement Print this is working
|
||||
* @param command The reply to be send ( echo as in sendReply() )
|
||||
* @param blocking if set to true will instruct the DCC code to not use the async callback functions
|
||||
*/
|
||||
void parse(Connection* c , byte *command, bool blocking)
|
||||
void parse(Print *stream, byte *command, bool blocking)
|
||||
{
|
||||
DIAG(F("DCC parsing: [%e]\n"), command);
|
||||
// echo back (as mock parser )
|
||||
StringFormatter::send(c->client, F("reply to: %s"), command);
|
||||
StringFormatter::send(stream, F("reply to: %s"), command);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sending a reply without going through the StringFormatter. Sends the repy in one go; For testing purposes; Adds sequence numbers to the reply
|
||||
*
|
||||
* @param client Client who send the command to which the reply shall be send
|
||||
* @param command Command initaliy recieved to be echoed back
|
||||
*/
|
||||
void sendReply(Client *client, char *command, uint8_t c)
|
||||
{
|
||||
char *number;
|
||||
char seqNumber[6];
|
||||
int i = 0;
|
||||
|
||||
memset(reply, 0, MAX_ETH_BUFFER); // reset reply
|
||||
|
||||
number = strrchr(command, ':'); // replace the int after the last ':'
|
||||
while( &command[i] != number ) { // copy command into the reply upto the last ':'
|
||||
reply[i] = command[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
strcat((char *)reply, ":");
|
||||
itoa(_sseq[c], seqNumber, 10);
|
||||
strcat((char *)reply, seqNumber);
|
||||
strcat((char *)reply, ">");
|
||||
|
||||
DIAG(F("Response: [%e]"), (char *)reply);
|
||||
if (client->connected())
|
||||
{
|
||||
client->write(reply, strlen((char *)reply));
|
||||
_sseq[c]++;
|
||||
DIAG(F(" send\n"));
|
||||
}
|
||||
};
|
||||
/*
|
||||
// Alternative reply mechanism using MemStream thus allowing to send all in one go using the parser
|
||||
streamer.setBufferContentPosition(0, 0);
|
||||
|
||||
// Parse via MemBuffer to be replaced by DCCEXparser.parse later
|
||||
|
||||
parse(&streamer, buffer, true); // set to true to that the execution in DCC is sync
|
||||
|
||||
if (streamer.available() == 0)
|
||||
{
|
||||
DIAG(F("No response\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[streamer.available()] = '\0'; // mark end of buffer, so it can be used as a string later
|
||||
DIAG(F("Response: [%s]\n"), (char *)reply);
|
||||
if (clients[i]->connected())
|
||||
{
|
||||
clients[i]->write(reply, streamer.available());
|
||||
}
|
||||
}
|
||||
*/
|
||||
/* This should work but creates a segmentation fault ??
|
||||
|
||||
// check if we have one parameter with name 'jmri' then send the payload directly and don't call the callback
|
||||
preq = httpReq.getParsedRequest();
|
||||
DIAG(F("Check parameter count\n"));
|
||||
if (*preq.paramCount == 1)
|
||||
{
|
||||
Params *p;
|
||||
int cmp;
|
||||
p = httpReq.getParam(1);
|
||||
|
||||
DIAG(F("Parameter name[%s]\n"), p->name);
|
||||
DIAG(F("Parameter value[%s]\n"), p->value);
|
||||
|
||||
cmp = strcmp("jmri", p->name);
|
||||
if ( cmp == 0 ) {
|
||||
memset(buffer, 0, MAX_ETH_BUFFER); // reset PacktBuffer
|
||||
strncpy((char *)buffer, p->value, strlen(p->value));
|
||||
jmriHandler(client, c);
|
||||
} else {
|
||||
DIAG(F("Callback 1\n"));
|
||||
httpReq.callback(&preq, client);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DIAG(F("Callback 2\n"));
|
||||
httpReq.callback(&preq, client);
|
||||
}
|
||||
DIAG(F("ResetRequest\n"));
|
||||
httpReq.resetRequest();
|
||||
|
||||
} // else do nothing and wait for the next packet
|
||||
}
|
||||
*/
|
@ -23,44 +23,47 @@
|
||||
#include <Ethernet.h>
|
||||
#include <WiFiEspAT.h>
|
||||
|
||||
#define DCCEX_ENABLED
|
||||
|
||||
#define MAX_ETH_BUFFER 64 // maximum length we read in one go from a TCP packet. Anything longer in one go send to the Arduino may result in unpredictable behaviour.
|
||||
// idealy the windowsize should be set accordingly so that the sender knows to produce only max 250 size packets.
|
||||
#define MAX_OVERFLOW MAX_ETH_BUFFER/2 // length of the overflow buffer to be used for a given connection.
|
||||
#define MAX_JMRI_CMD 32 // MAX Length of a JMRI Command
|
||||
typedef enum {
|
||||
DCCEX, // if char[0] = < opening bracket the client should be a JMRI / DCC EX client_h
|
||||
WITHROTTLE, //
|
||||
HTTP, // If char[0] = G || P || D; if P then char [1] = U || O || A
|
||||
N_DIAG, // '#' send form a telnet client as FIRST message will then reroute all DIAG messages to that client whilst being able to send jmri type commands
|
||||
#define MAX_ETH_BUFFER 64 // maximum length we read in one go from a TCP packet. Anything longer in one go send to the Arduino may result in unpredictable behaviour.
|
||||
// idealy the windowsize should be set accordingly so that the sender knows to produce only max 250 size packets.
|
||||
#define MAX_OVERFLOW MAX_ETH_BUFFER / 2 // length of the overflow buffer to be used for a given connection.
|
||||
#define MAX_JMRI_CMD 32 // MAX Length of a JMRI Command
|
||||
typedef enum
|
||||
{
|
||||
DCCEX, // if char[0] = < opening bracket the client should be a JMRI / DCC EX client_h
|
||||
WITHROTTLE, //
|
||||
HTTP, // If char[0] = G || P || D; if P then char [1] = U || O || A
|
||||
N_DIAG, // '#' send form a telnet client as FIRST message will then reroute all DIAG messages to that client whilst being able to send jmri type commands
|
||||
UNKNOWN_PROTOCOL
|
||||
} appProtocol;
|
||||
|
||||
struct Connection;
|
||||
using appProtocolCallback = void(*)(Connection* c);
|
||||
using appProtocolCallback = void (*)(Connection *c);
|
||||
|
||||
struct Connection {
|
||||
uint8_t id;
|
||||
Client* client;
|
||||
char overflow[MAX_OVERFLOW];
|
||||
appProtocol p;
|
||||
char delimiter = '\0';
|
||||
bool isProtocolDefined = false;
|
||||
struct Connection
|
||||
{
|
||||
uint8_t id;
|
||||
Client *client;
|
||||
char overflow[MAX_OVERFLOW];
|
||||
appProtocol p;
|
||||
char delimiter = '\0';
|
||||
bool isProtocolDefined = false;
|
||||
appProtocolCallback appProtocolHandler;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class TransportProcessor
|
||||
class TransportProcessor
|
||||
{
|
||||
private:
|
||||
#ifdef DCCEX_ENABLED
|
||||
void sendToDCC(Connection *c, char *command, bool blocking);
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
void readStream(Connection *c); // reads incomming packets and hands over to the commandHandle for taking the stream apart for commands
|
||||
void readStream(Connection *c); // reads incomming packets and hands over to the commandHandle for taking the stream apart for commands
|
||||
|
||||
TransportProcessor(){};
|
||||
~TransportProcessor(){};
|
||||
|
||||
};
|
||||
|
||||
#endif // !Transport_h
|
Loading…
Reference in New Issue
Block a user