1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2025-02-17 06:29:15 +01:00

Multiple NetworkInterface support

This commit is contained in:
Gregor Baues 2020-10-26 10:29:40 +01:00
parent c1e4727ee8
commit 5888e21090
14 changed files with 223 additions and 282 deletions

View File

@ -17,6 +17,11 @@
// to be issued from the USB serial console.
DCCEXParser serialParser;
// (0) Declare NetworkInterfaces
NetworkInterface wifi;
NetworkInterface eth;
// (0) Declared NetworkInterfaces
// (1) Start NetworkInterface - HTTP callback
void httpRequestHandler(ParsedRequest *req, Client* client) {
DIAG(F("\nParsed Request:"));
@ -64,12 +69,10 @@ void setup()
DIAG(F("\nFree RAM before network init: [%d]\n"),freeMemory());
DIAG(F("\nNetwork Setup In Progress ...\n"));
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
// NetworkInterface::setup(WIFI); // setup without port and protocol will use by default TCP on port 2560
// NetworkInterface::setup(); // all defaults ETHERNET, TCP on port 2560
wifi.setup(WIFI); // WIFI, TCP on Port 2560
eth.setup(ETHERNET, TCP, 8888); // ETHERNET, TCP on Port 8888
eth.setHttpCallback(httpRequestHandler); // HTTP callback
DIAG(F("\nNetwork Setup done ..."));
DIAG(F("\nFree RAM after network init: [%d]\n"),freeMemory());
@ -94,9 +97,14 @@ void loop()
#if WIFI_ON
WifiInterface::loop();
#endif
// (3) Start Loop NetworkInterface
NetworkInterface::loop();
// (3) Start Loop NetworkInterface
wifi.loop();
eth.loop();
// (3) End Loop NetworkInterface
LCDDisplay::loop(); // ignored if LCD not in use
// Optionally report any decrease in memory (will automatically trigger on first call)

View File

@ -54,7 +54,7 @@ EthernetServer* EthernetSetup::setup()
}
else if (Ethernet.hardwareStatus() == EthernetW5500)
{
DIAG(F("\nW5500 Ethernet controller detected."));
DIAG(F("W5500 Ethernet controller detected."));
maxConnections = 8;
}

View File

@ -30,7 +30,6 @@ private:
public:
// EthernetServer *setup(uint16_t port);
EthernetServer *setup();
EthernetSetup();

47
NetworkConfig.h Normal file
View File

@ -0,0 +1,47 @@
/*
* © 2020, Gregor Baues. All rights reserved.
*
* 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/>.
*/
/* some generated mac addresses as EthernetShields don't have one by default in HW.
* Sometimes they come on a sticker on the EthernetShield then use this address otherwise
* just choose one from below or generate one yourself. Only condition is that there is no
* other device on your network with the same Mac address.
*
* 52:b8:8a:8e:ce:21
* e3:e9:73:e1:db:0d
* 54:2b:13:52:ac:0c
* c2:d8:d4:7d:7c:cb
* 86:cf:fa:9f:07:79
*/
/**
* @brief Network operational configuration
*
*/
#define LISTEN_PORT 2560 // default listen port for the server
#define MAC_ADDRESS {0x52, 0xB8, 0x8A, 0x8E, 0xCE, 0x21} // MAC address of your networking card found on the sticker on your card or take one from above
#define IP_ADDRESS 10, 0, 0, 101 // Just in case we don't get an adress from DHCP try a static one;
/**
* @brief NetworkInterface configuration
*
*/
#define MAX_SOCK_NUM 8 // Maximum number of sockets allowed for any WizNet based EthernetShield. The W5100 only supports 4
#define MAX_WIFI_SOCK 5 // ESP8266 doesn't support more than 5 connections
#define MAX_ETH_BUFFER 64 // maximum length we read in one go from a TCP packet.
#define MAX_OVERFLOW MAX_ETH_BUFFER / 2 // length of the overflow buffer to be used for a given connection.
#define MAX_JMRI_CMD MAX_ETH_BUFFER / 2 // MAX Length of a JMRI Command

View File

@ -19,14 +19,12 @@
#include "DIAG.h"
#include "NetworkInterface.h"
#include "Transport.h"
#include "EthernetSetup.h"
#include "WifiSetup.h"
HttpCallback NetworkInterface::httpCallback = 0;
Transport<WiFiServer, WiFiClient, WiFiUDP> *NetworkInterface::wifiTransport;
Transport<EthernetServer, EthernetClient, EthernetUDP> *NetworkInterface::ethernetTransport;
transportType t;
Transport<WiFiServer, WiFiClient, WiFiUDP>* wifiTransport;
Transport<EthernetServer, EthernetClient, EthernetUDP>* ethernetTransport;
void NetworkInterface::setup(transportType transport, protocolType protocol, uint16_t port)
{
@ -43,31 +41,29 @@ void NetworkInterface::setup(transportType transport, protocolType protocol, uin
case WIFI:
{
WifiSetup wSetup(port, protocol);
wifiTransport = new Transport<WiFiServer, WiFiClient, WiFiUDP>();
ok = wSetup.setup();
if (ok)
{
wifiTransport = new Transport<WiFiServer, WiFiClient, WiFiUDP>;
wifiTransport->server = wSetup.getServer();
wifiTransport->port = port;
wifiTransport->protocol = protocol;
wifiTransport->transport = transport;
wifiTransport->maxConnections = wSetup.maxConnections;
ok = wifiTransport->setup();
ok = wifiTransport->setup(this);
}
break;
};
case ETHERNET:
{
EthernetSetup eSetup(port, protocol);
ethernetTransport = new Transport<EthernetServer, EthernetClient, EthernetUDP>();
ethernetTransport->server = eSetup.setup(); // returns (NULL) 0 if we run over UDP
ethernetTransport = new Transport<EthernetServer, EthernetClient, EthernetUDP>;
ethernetTransport->server = eSetup.setup(); // returns (NULL) 0 if we run over UDP; todo: error handling if something goes wrong in the init
ethernetTransport->port = port;
ethernetTransport->protocol = protocol;
ethernetTransport->transport = transport;
ethernetTransport->maxConnections = eSetup.maxConnections; // that has been determined during the ethernet/wifi setup
ok = ethernetTransport->setup(); // start the transport i.e. setup all the client connections; We don't need the setup object anymore from here on
ok = ethernetTransport->setup(this); // start the transport i.e. setup all the client connections; We don't need the setup object anymore from here on
break;
};
default:
@ -117,19 +113,13 @@ void NetworkInterface::loop()
void NetworkInterface::setHttpCallback(HttpCallback callback)
{
httpCallback = callback;
this->httpCallback = callback;
}
HttpCallback NetworkInterface::getHttpCallback()
{
return httpCallback;
return this->httpCallback;
}
NetworkInterface::NetworkInterface()
{
// DIAG(F("NetworkInterface created "));
}
NetworkInterface::~NetworkInterface()
{
// DIAG(F("NetworkInterface destroyed"));
}
NetworkInterface::NetworkInterface(){}
NetworkInterface::~NetworkInterface(){}

View File

@ -20,7 +20,7 @@
#include <Arduino.h>
#include "Transport.h"
// #include "Transport.h"
#include "HttpRequest.h"
typedef enum protocolType {
@ -32,29 +32,27 @@ typedef enum protocolType {
typedef enum transportType {
WIFI, // using an AT (Version >= V1.7) command enabled ESP8266 not to be used in conjunction with the WifiInterface though! not tested for conflicts
ETHERNET // using the EthernetShield
} transoprtType;
} transportType;
// typedef void (*HttpCallback)(ParsedRequest *req, Client *client);
using HttpCallback = void(*)(ParsedRequest *req, Client *client);
class NetworkInterface
{
private:
static Transport<WiFiServer,WiFiClient,WiFiUDP>* wifiTransport;
static Transport<EthernetServer,EthernetClient,EthernetUDP>* ethernetTransport;
static HttpCallback httpCallback;
HttpCallback httpCallback;
transportType t;
public:
static void setHttpCallback(HttpCallback callback);
static HttpCallback getHttpCallback();
static void setup(transportType t, protocolType p, uint16_t port); // specific port nummber
static void setup(transportType t, protocolType p); // uses default port number
static void setup(transportType t); // defaults for protocol/port
void setHttpCallback(HttpCallback callback);
HttpCallback getHttpCallback();
void setup(transportType t, protocolType p, uint16_t port); // specific port nummber
void setup(transportType t, protocolType p); // uses default port number
void setup(transportType t); // defaults for protocol/port
static void setup(); // defaults for all as above plus CABLE (i.e. using EthernetShield ) as default
static void loop();
void setup(); // defaults for all as above plus CABLE (i.e. using EthernetShield ) as default
void loop();
NetworkInterface();
~NetworkInterface();

View File

@ -17,31 +17,11 @@
#ifndef NetworkSetup_h
#define NetworkSetup_h
#include "Ethernet.h"
#include <Ethernet.h>
#include "NetworkConfig.h"
#include "NetworkInterface.h"
/* some generated mac addresses as EthernetShields don't have one by default in HW.
* Sometimes they come on a sticker on the EthernetShield then use this address otherwise
* just choose one from below or generate one yourself. Only condition is that there is no
* other device on your network with the same Mac address.
*
* 52:b8:8a:8e:ce:21
* e3:e9:73:e1:db:0d
* 54:2b:13:52:ac:0c
* c2:d8:d4:7d:7c:cb
* 86:cf:fa:9f:07:79
*/
/**
* @brief Network Configuration
*
*/
#define MAC_ADDRESS \
{ \
0x52, 0xB8, 0x8A, 0x8E, 0xCE, 0x21 \
} // MAC address of your networking card found on the sticker on your card or take one from above
#define IP_ADDRESS 10, 0, 0, 101 // Just in case we don't get an adress from DHCP try a static one;
class NetworkSetup
{
private:

View File

@ -178,5 +178,4 @@ void StringFormatter::printPadded(Print* stream, long value, byte width, bool fo
}
if (!formatLeft) stream->print(value, DEC);
}

View File

@ -54,8 +54,6 @@ class StringFormatter
static void printEscapes(char * input);
static void printEscape( char c);
static void setDiagOut(Connection *c) {
if ( c->client->connected() ) {
diagSerial = c->client;
@ -70,4 +68,4 @@ class StringFormatter
static void printPadded(Print* stream, long value, byte width, bool formatLeft);
};
#endif
#endif

View File

@ -25,11 +25,12 @@ extern bool diagNetwork;
extern uint8_t diagNetworkClient;
template<class S, class C, class U>
bool Transport<S,C,U>::setup() {
bool Transport<S,C,U>::setup(NetworkInterface *nw) {
if (protocol == TCP) {
connectionPool(server); // server should have started here so create the connection pool only for TCP though
connectionPool(server); // server should have started here so create the connection pool only for TCP though
}
t = new TransportProcessor();
t->nwi = nw; // The TransportProcesor needs to know which Interface he is connected to
connected = true; // server & clients which will recieve/send data have all e setup and are available
return true;
}
@ -45,8 +46,7 @@ void Transport<S,C,U>::loop() {
};
case TCP:
{
tcpSessionHandler(server); // for session oriented coms
break;
tcpSessionHandler(server);
};
case MQTT:
{
@ -64,7 +64,7 @@ void Transport<S, C, U>::connectionPool(S *server)
clients[i] = server->accept();
connections[i].client = &clients[i];
connections[i].id = i;
DIAG(F("\nConnection pool: [%d:%x]"), i, clients[i]);
DIAG(F("\nConnection pool: [%d:%x]"), i, connections[i].client);
}
}
@ -110,11 +110,11 @@ void Transport<S,C,U>::tcpSessionHandler(S* server)
{
// get client from the server
C client = server->accept();
// check for new client
// check for new client
if (client)
{
for (byte i = 0; i < Transport<S,C,U>::maxConnections; i++)
for (byte i = 0; i < maxConnections; i++)
{
if (!clients[i])
{
@ -126,23 +126,24 @@ void Transport<S,C,U>::tcpSessionHandler(S* server)
}
}
}
// check for incoming data from all possible clients
for (byte i = 0; i < Transport<S,C,U>::maxConnections; i++)
for (byte i = 0; i < maxConnections; i++)
{
if (clients[i] && clients[i].available() > 0)
{
// readStream(i);
t->readStream(&connections[i]);
}
// stop any clients which disconnect
for (byte i = 0; i < Transport<S,C,U>::maxConnections; i++)
for (byte i = 0; i < maxConnections; i++)
{
if (clients[i] && !clients[i].connected())
{
DIAG(F("\nDisconnect client #%d"), i);
clients[i].stop();
connections[i].isProtocolDefined = false;
if (diagNetworkClient == i && diagNetwork) {
if (diagNetworkClient == i && diagNetwork)
{
diagNetwork = false;
StringFormatter::resetDiagOut();
}
@ -152,22 +153,10 @@ void Transport<S,C,U>::tcpSessionHandler(S* server)
}
template<class S, class C, class U>
Transport<S,C,U>::Transport()
{
// DIAG(F("Transport created "));
}
Transport<S,C,U>::Transport(){}
template<class S, class C, class U>
Transport<S,C,U>::~Transport()
{
// DIAG(F("Transport destroyed"));
}
Transport<S,C,U>::~Transport(){}
// explicitly instatiate to get the relevant copies for ethernet / wifi build @compile time
template class Transport<EthernetServer,EthernetClient,EthernetUDP>;
template class Transport<WiFiServer, WiFiClient, WiFiUDP>;
/*
* Scratch pad Section
*/

View File

@ -23,37 +23,17 @@
#include <Ethernet.h>
#include <WiFiEspAT.h>
#include "NetworkConfig.h"
#include "NetworkInterface.h"
#include "TransportProcessor.h"
#define MAX_SOCK_NUM 8 // Maximum number of sockets allowed for any WizNet based EthernetShield. The W5100 only supports 4
#define MAX_WIFI_SOCK 5 // ESP8266 doesn't support more than 5 connections in //
#define LISTEN_PORT 2560 // default listen port for the server
#define MAC_ADDRESS \
{ \
0x52, 0xB8, 0x8A, 0x8E, 0xCE, 0x21 \
} // MAC address of your networking card found on the sticker on your card or take one from above
#define IP_ADDRESS 10, 0, 0, 101 // Just in case we don't get an adress from DHCP try a static one;
// Emulate Serial1 on pins 6/7 if not present
#if defined(ARDUINO_ARCH_AVR) && !defined(HAVE_HWSERIAL1)
#include <SoftwareSerial.h>
SoftwareSerial Serial1(6, 7); // RX, TX
#define AT_BAUD_RATE 9600
#else
#define AT_BAUD_RATE 115200
#endif
template <class S, class C, class U> class Transport
{
private:
C clients[MAX_SOCK_NUM]; // Client objects created by the connectionPool
Connection connections[MAX_SOCK_NUM]; // All the connections build by the connectionPool
bool connected = false;
C clients[MAX_SOCK_NUM]; // Client objects created by the connectionPool
Connection connections[MAX_SOCK_NUM]; // All the connections build by the connectionPool
bool connected = false;
TransportProcessor* t; // pointer to the object which handles the incomming flow
void udpHandler(); // Reads from a Udp socket - todo add incomming queue for processing when the flow is faster than we can process commands
@ -66,9 +46,9 @@ public:
uint8_t transport; // WIFI or ETHERNET
S* server; // WiFiServer or EthernetServer
U* udp; // UDP socket object
uint8_t maxConnections; // number of supported connections depending on the network equipment used
uint8_t maxConnections; // number of supported connections depending on the network equipment use
bool setup();
bool setup(NetworkInterface* nwi);
void loop();
bool isConnected() {
@ -80,4 +60,4 @@ public:
};
#endif // !Transport_h
#endif // !Transport_h

View File

@ -23,23 +23,19 @@
#include "TransportProcessor.h"
#ifdef DCCEX_ENABLED
#include "DCCEXParser.h"
#include "MemStream.h"
DCCEXParser ethParser;
#include "DCCEXParser.h"
#include "MemStream.h"
DCCEXParser ethParser;
#endif
static uint8_t buffer[MAX_ETH_BUFFER];
static char command[MAX_JMRI_CMD] = {0};
static uint8_t reply[MAX_ETH_BUFFER];
HttpRequest httpReq;
uint16_t _rseq[MAX_SOCK_NUM] = {0};
uint16_t _sseq[MAX_SOCK_NUM] = {0};
char protocolName[4][11] = {"JMRI", "WITHROTTLE", "HTTP", "UNKNOWN"}; // change for Progmem
char protocolName[4][11] = {"JMRI", "WITHROTTLE", "HTTP", "UNKNOWN"}; // change for Progmem
bool diagNetwork = false;
uint8_t diagNetworkClient = 0;
@ -50,33 +46,33 @@ uint8_t diagNetworkClient = 0;
* 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 t TransportProcessor used for accessing the buffers to be send
* @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)
void sendToDCC(Connection *c, TransportProcessor* t, bool blocking)
{
static MemStream* streamer = new MemStream((byte *)command, MAX_ETH_BUFFER, MAX_ETH_BUFFER, true);
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);
// 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
ethParser.parse(streamer, (byte *)t->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());
}
}
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"), t->command);
if (c->client->connected())
{
c->client->write((byte *)t->command, streamer->available());
}
}
}
#else
/**
@ -85,29 +81,41 @@ void sendToDCC(Connection* c ,char *command, bool blocking)
* @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)
{
void sendReply(Connection *c, TransportProcessor *t)
{
byte reply[MAX_ETH_BUFFER];
byte *response;
char *number;
char *command = t->command;
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++;
memset(reply, 0, MAX_ETH_BUFFER); // reset reply
// This expects messages to be send with a trailing sequence number <R 1 1 1:0>
// as of my stress test program to verify the arrival of messages
number = strrchr(command, ':'); // replace the int after the last ':' if number != 0
if (number != 0)
{
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, ">");
response = reply;
} else {
response = (byte *)command;
}
strcat((char *)reply, ":");
itoa(_sseq[c->id], seqNumber, 10);
strcat((char *)reply, seqNumber);
strcat((char *)reply, ">");
DIAG(F("Response: [%e]"), (char *)reply);
DIAG(F("Response: [%e]"), (char *)response);
if (c->client->connected())
{
c->client->write(reply, strlen((char *)reply));
c->client->write(response, strlen((char *)response));
_sseq[c->id]++;
DIAG(F(" send\n"));
}
@ -121,20 +129,21 @@ void sendReply(Connection* c, char *command)
* @param client Client object from whom we receievd the data
* @param c id of the Client object
*/
void httpProcessor(Connection* c)
void httpProcessor(Connection *c, TransportProcessor *t)
{
if (httpReq.callback == 0) return; // no callback i.e. nothing to do
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
* ev. send a 401 error back
*/
uint8_t i, l = 0;
ParsedRequest preq;
l = strlen((char *)buffer);
l = strlen((char *)t->buffer);
for (i = 0; i < l; i++)
{
httpReq.parseRequest((char)buffer[i]);
httpReq.parseRequest((char)t->buffer[i]);
}
if (httpReq.endOfRequest())
{
@ -216,7 +225,8 @@ appProtocol setAppProtocol(char a, char b, Connection *c)
p = DCCEX;
break;
}
case '#': {
case '#':
{
p = DCCEX;
DIAG(F("\nDiagnostics routed to network client\n"));
StringFormatter::setDiagOut(c);
@ -239,32 +249,31 @@ 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)
void processStream(Connection *c, TransportProcessor *t)
{
uint8_t i, j, k, l = 0;
uint8_t *_buffer = t->buffer;
DIAG(F("\nBuffer: [%e]\n"), _buffer);
memset(t->command, 0, MAX_JMRI_CMD); // clear out the command
memset(command, 0, MAX_JMRI_CMD); // clear out the command
DIAG(F("\nBuffer: [%e]\n"), buffer);
// copy overflow into the command
if ((i = strlen(c->overflow)) != 0)
{
// DIAG(F("\nCopy overflow to command: %e"), c->overflow);
strncpy(command, c->overflow, i);
strncpy(t->command, c->overflow, i);
k = i;
}
// reset the overflow
memset(c->overflow, 0, MAX_OVERFLOW);
// 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
if ((i = strlen((char *)_buffer)) == MAX_ETH_BUFFER - 1)
{
// 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]);
while (_buffer[i] != c->delimiter)
{
i--;
}
i++; // start of the buffer to copy
@ -273,32 +282,31 @@ void processStream(Connection *c)
for (j = 0; j < k; j++, i++)
{
c->overflow[j] = buffer[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 '>'
_buffer[l] = '\0'; // terminate buffer just after the last '>'
// DIAG(F("\nNew buffer: [%s] New overflow: [%s]\n"), (char*) buffer, c->overflow );
}
// breakup the buffer using its changed length
i = 0;
k = strlen(command); // current length of the command buffer telling us where to start copy in
l = strlen((char *)buffer);
k = strlen(t->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 );
while (i < l)
{
// DIAG(F("\nl: %d k: %d , i: %d"), l, k, i);
command[k] = buffer[i];
if (buffer[i] == c->delimiter)
t->command[k] = _buffer[i];
if (_buffer[i] == c->delimiter)
{ // closing bracket need to fix if there is none before an opening bracket ?
command[k+1] = '\0';
t->command[k + 1] = '\0';
DIAG(F("Command: [%d:%e]\n"),_rseq[c->id], command);
DIAG(F("Command: [%d:%e]\n"), _rseq[c->id], t->command);
#ifdef DCCEX_ENABLED
sendToDCC(c, command, true);
#else
sendReply(c, command);
sendReply(c, t);
#endif
_rseq[c->id]++;
j = 0;
@ -312,24 +320,26 @@ void processStream(Connection *c)
}
}
void echoProcessor(Connection *c)
void echoProcessor(Connection *c, TransportProcessor *t)
{
memset(reply, 0, MAX_ETH_BUFFER);
sprintf((char *)reply, "ERROR: malformed content in [%s]", buffer);
byte reply[MAX_ETH_BUFFER];
memset(reply, 0, MAX_ETH_BUFFER);
sprintf((char *)reply, "ERROR: malformed content in [%s]", t->buffer);
if (c->client->connected())
{
c->client->write(reply, strlen((char *)reply));
_sseq[c->id]++;
}
}
void jmriProcessor(Connection *c)
void jmriProcessor(Connection *c, TransportProcessor *t)
{
processStream(c);
DIAG(F("Processing JMRI ... \n"));
processStream(c, t);
}
void withrottleProcessor(Connection *c)
void withrottleProcessor(Connection *c, TransportProcessor *t)
{
processStream(c);
processStream(c, t);
}
/**
@ -340,6 +350,7 @@ void withrottleProcessor(Connection *c)
void TransportProcessor::readStream(Connection *c)
{
// read bytes from a client
int count = c->client->read(buffer, MAX_ETH_BUFFER - 1); // count is the amount of data ready for reading, -1 if there is no data, 0 is the connection has been closed
buffer[count] = 0;
@ -361,14 +372,14 @@ void TransportProcessor::readStream(Connection *c)
}
case WITHROTTLE:
{
c->delimiter = '\n';
c->delimiter = '\n';
c->appProtocolHandler = (appProtocolCallback)withrottleProcessor;
break;
}
case HTTP:
{
c->appProtocolHandler = (appProtocolCallback)httpProcessor;
httpReq.callback = NetworkInterface::getHttpCallback();
httpReq.callback = nwi->getHttpCallback();
break;
}
case UNKNOWN_PROTOCOL:
@ -380,17 +391,14 @@ void TransportProcessor::readStream(Connection *c)
}
}
// IPAddress remote = c->client->remoteIP(); // only available in my modified Client.h file
IPAddress remote = c->client->remoteIP();
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]\n"), count);
DIAG(F("\nReceived packet of size:[%d] from [%d.%d.%d.%d]\n"), count, remote[0], remote[1], remote[2], remote[3]);
DIAG(F("Client #: [%d]\n"), c->id);
DIAG(F("Packet: [%e]\n"), buffer);
// chop the buffer into CS / WiThrottle commands || assemble command across buffer read boundaries
c->appProtocolHandler(c);
c->appProtocolHandler(c, this);
}
/**
@ -407,64 +415,4 @@ void parse(Print *stream, byte *command, bool blocking)
DIAG(F("DCC parsing: [%e]\n"), command);
// echo back (as mock parser )
StringFormatter::send(stream, F("reply to: %s"), command);
}
/*
// 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
}
*/
}

View File

@ -23,12 +23,11 @@
#include <Ethernet.h>
#include <WiFiEspAT.h>
#define DCCEX_ENABLED
#include "NetworkConfig.h"
#include "NetworkInterface.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
@ -38,8 +37,11 @@ typedef enum
UNKNOWN_PROTOCOL
} appProtocol;
// Needed forward declarations
struct Connection;
using appProtocolCallback = void (*)(Connection *c);
class TransportProcessor;
using appProtocolCallback = void (*)(Connection* c, TransportProcessor* t);
struct Connection
{
@ -56,10 +58,15 @@ class TransportProcessor
{
private:
#ifdef DCCEX_ENABLED
void sendToDCC(Connection *c, char *command, bool blocking);
void sendToDCC(Connection *c, TransportProcessor* t, bool blocking);
#endif
public:
NetworkInterface *nwi;
uint8_t buffer[MAX_ETH_BUFFER];
char command[MAX_JMRI_CMD];
void readStream(Connection *c); // reads incomming packets and hands over to the commandHandle for taking the stream apart for commands
TransportProcessor(){};

View File

@ -40,13 +40,11 @@ private:
public:
// WiFiServer *setup(uint16_t port);
bool setup();
WiFiUDP* getUdp() {
return udp;
}
WiFiServer* getServer() {
return server;
}