1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2025-02-21 08:16:04 +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. // to be issued from the USB serial console.
DCCEXParser serialParser; DCCEXParser serialParser;
// (0) Declare NetworkInterfaces
NetworkInterface wifi;
NetworkInterface eth;
// (0) Declared NetworkInterfaces
// (1) Start NetworkInterface - HTTP callback // (1) Start NetworkInterface - HTTP callback
void httpRequestHandler(ParsedRequest *req, Client* client) { void httpRequestHandler(ParsedRequest *req, Client* client) {
DIAG(F("\nParsed Request:")); DIAG(F("\nParsed Request:"));
@ -64,12 +69,10 @@ void setup()
DIAG(F("\nFree RAM before network init: [%d]\n"),freeMemory()); DIAG(F("\nFree RAM before network init: [%d]\n"),freeMemory());
DIAG(F("\nNetwork Setup In Progress ...\n")); 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 wifi.setup(WIFI); // WIFI, TCP on Port 2560
// NetworkInterface::setup(WIFI); // setup without port and protocol will use by default TCP on port 2560 eth.setup(ETHERNET, TCP, 8888); // ETHERNET, TCP on Port 8888
// NetworkInterface::setup(); // all defaults ETHERNET, TCP on port 2560 eth.setHttpCallback(httpRequestHandler); // HTTP callback
DIAG(F("\nNetwork Setup done ...")); DIAG(F("\nNetwork Setup done ..."));
DIAG(F("\nFree RAM after network init: [%d]\n"),freeMemory()); DIAG(F("\nFree RAM after network init: [%d]\n"),freeMemory());
@ -94,9 +97,14 @@ void loop()
#if WIFI_ON #if WIFI_ON
WifiInterface::loop(); WifiInterface::loop();
#endif #endif
// (3) Start Loop NetworkInterface // (3) Start Loop NetworkInterface
NetworkInterface::loop(); wifi.loop();
eth.loop();
// (3) End Loop NetworkInterface // (3) End Loop NetworkInterface
LCDDisplay::loop(); // ignored if LCD not in use LCDDisplay::loop(); // ignored if LCD not in use
// Optionally report any decrease in memory (will automatically trigger on first call) // 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) else if (Ethernet.hardwareStatus() == EthernetW5500)
{ {
DIAG(F("\nW5500 Ethernet controller detected.")); DIAG(F("W5500 Ethernet controller detected."));
maxConnections = 8; maxConnections = 8;
} }

View File

@ -30,7 +30,6 @@ private:
public: public:
// EthernetServer *setup(uint16_t port);
EthernetServer *setup(); EthernetServer *setup();
EthernetSetup(); 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 "DIAG.h"
#include "NetworkInterface.h" #include "NetworkInterface.h"
#include "Transport.h"
#include "EthernetSetup.h" #include "EthernetSetup.h"
#include "WifiSetup.h" #include "WifiSetup.h"
HttpCallback NetworkInterface::httpCallback = 0; Transport<WiFiServer, WiFiClient, WiFiUDP>* wifiTransport;
Transport<EthernetServer, EthernetClient, EthernetUDP>* ethernetTransport;
Transport<WiFiServer, WiFiClient, WiFiUDP> *NetworkInterface::wifiTransport;
Transport<EthernetServer, EthernetClient, EthernetUDP> *NetworkInterface::ethernetTransport;
transportType t;
void NetworkInterface::setup(transportType transport, protocolType protocol, uint16_t port) void NetworkInterface::setup(transportType transport, protocolType protocol, uint16_t port)
{ {
@ -43,31 +41,29 @@ void NetworkInterface::setup(transportType transport, protocolType protocol, uin
case WIFI: case WIFI:
{ {
WifiSetup wSetup(port, protocol); WifiSetup wSetup(port, protocol);
wifiTransport = new Transport<WiFiServer, WiFiClient, WiFiUDP>();
ok = wSetup.setup(); ok = wSetup.setup();
if (ok) if (ok)
{ {
wifiTransport = new Transport<WiFiServer, WiFiClient, WiFiUDP>;
wifiTransport->server = wSetup.getServer(); wifiTransport->server = wSetup.getServer();
wifiTransport->port = port; wifiTransport->port = port;
wifiTransport->protocol = protocol; wifiTransport->protocol = protocol;
wifiTransport->transport = transport; wifiTransport->transport = transport;
wifiTransport->maxConnections = wSetup.maxConnections; wifiTransport->maxConnections = wSetup.maxConnections;
ok = wifiTransport->setup(); ok = wifiTransport->setup(this);
} }
break; break;
}; };
case ETHERNET: case ETHERNET:
{ {
EthernetSetup eSetup(port, protocol); EthernetSetup eSetup(port, protocol);
ethernetTransport = new Transport<EthernetServer, EthernetClient, EthernetUDP>;
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->server = eSetup.setup(); // returns (NULL) 0 if we run over UDP
ethernetTransport->port = port; ethernetTransport->port = port;
ethernetTransport->protocol = protocol; ethernetTransport->protocol = protocol;
ethernetTransport->transport = transport; ethernetTransport->transport = transport;
ethernetTransport->maxConnections = eSetup.maxConnections; // that has been determined during the ethernet/wifi setup 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; break;
}; };
default: default:
@ -117,19 +113,13 @@ void NetworkInterface::loop()
void NetworkInterface::setHttpCallback(HttpCallback callback) void NetworkInterface::setHttpCallback(HttpCallback callback)
{ {
httpCallback = callback; this->httpCallback = callback;
} }
HttpCallback NetworkInterface::getHttpCallback() HttpCallback NetworkInterface::getHttpCallback()
{ {
return httpCallback; return this->httpCallback;
} }
NetworkInterface::NetworkInterface() NetworkInterface::NetworkInterface(){}
{ NetworkInterface::~NetworkInterface(){}
// DIAG(F("NetworkInterface created "));
}
NetworkInterface::~NetworkInterface()
{
// DIAG(F("NetworkInterface destroyed"));
}

View File

@ -20,7 +20,7 @@
#include <Arduino.h> #include <Arduino.h>
#include "Transport.h" // #include "Transport.h"
#include "HttpRequest.h" #include "HttpRequest.h"
typedef enum protocolType { typedef enum protocolType {
@ -32,29 +32,27 @@ typedef enum protocolType {
typedef enum transportType { 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 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 ETHERNET // using the EthernetShield
} transoprtType; } transportType;
// typedef void (*HttpCallback)(ParsedRequest *req, Client *client);
using HttpCallback = void(*)(ParsedRequest *req, Client *client); using HttpCallback = void(*)(ParsedRequest *req, Client *client);
class NetworkInterface class NetworkInterface
{ {
private: private:
static Transport<WiFiServer,WiFiClient,WiFiUDP>* wifiTransport; HttpCallback httpCallback;
static Transport<EthernetServer,EthernetClient,EthernetUDP>* ethernetTransport; transportType t;
static HttpCallback httpCallback;
public: public:
static void setHttpCallback(HttpCallback callback); void setHttpCallback(HttpCallback callback);
static HttpCallback getHttpCallback(); HttpCallback getHttpCallback();
static void setup(transportType t, protocolType p, uint16_t port); // specific port nummber void setup(transportType t, protocolType p, uint16_t port); // specific port nummber
static void setup(transportType t, protocolType p); // uses default port number void setup(transportType t, protocolType p); // uses default port number
static void setup(transportType t); // defaults for protocol/port void setup(transportType t); // defaults for protocol/port
static void setup(); // defaults for all as above plus CABLE (i.e. using EthernetShield ) as default void setup(); // defaults for all as above plus CABLE (i.e. using EthernetShield ) as default
static void loop(); void loop();
NetworkInterface(); NetworkInterface();
~NetworkInterface(); ~NetworkInterface();

View File

@ -17,31 +17,11 @@
#ifndef NetworkSetup_h #ifndef NetworkSetup_h
#define NetworkSetup_h #define NetworkSetup_h
#include "Ethernet.h" #include <Ethernet.h>
#include "NetworkConfig.h"
#include "NetworkInterface.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 class NetworkSetup
{ {
private: private:

View File

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

View File

@ -54,8 +54,6 @@ class StringFormatter
static void printEscapes(char * input); static void printEscapes(char * input);
static void printEscape( char c); static void printEscape( char c);
static void setDiagOut(Connection *c) { static void setDiagOut(Connection *c) {
if ( c->client->connected() ) { if ( c->client->connected() ) {
diagSerial = c->client; diagSerial = c->client;

View File

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

View File

@ -23,30 +23,10 @@
#include <Ethernet.h> #include <Ethernet.h>
#include <WiFiEspAT.h> #include <WiFiEspAT.h>
#include "NetworkConfig.h"
#include "NetworkInterface.h" #include "NetworkInterface.h"
#include "TransportProcessor.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 template <class S, class C, class U> class Transport
{ {
@ -66,9 +46,9 @@ public:
uint8_t transport; // WIFI or ETHERNET uint8_t transport; // WIFI or ETHERNET
S* server; // WiFiServer or EthernetServer S* server; // WiFiServer or EthernetServer
U* udp; // UDP socket object 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(); void loop();
bool isConnected() { bool isConnected() {

View File

@ -24,17 +24,13 @@
#ifdef DCCEX_ENABLED #ifdef DCCEX_ENABLED
#include "DCCEXParser.h" #include "DCCEXParser.h"
#include "MemStream.h" #include "MemStream.h"
DCCEXParser ethParser; DCCEXParser ethParser;
#endif #endif
static uint8_t buffer[MAX_ETH_BUFFER];
static char command[MAX_JMRI_CMD] = {0};
static uint8_t reply[MAX_ETH_BUFFER];
HttpRequest httpReq; HttpRequest httpReq;
uint16_t _rseq[MAX_SOCK_NUM] = {0}; uint16_t _rseq[MAX_SOCK_NUM] = {0};
uint16_t _sseq[MAX_SOCK_NUM] = {0}; uint16_t _sseq[MAX_SOCK_NUM] = {0};
@ -50,19 +46,19 @@ uint8_t diagNetworkClient = 0;
* before ending it. * before ending it.
* *
* @param stream Actually the Client to whom to send the reply. As Clients implement Print this is working * @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 * @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); DIAG(F("DCC parsing: [%e]\n"), command);
// as we use buffer for recv and send we have to reset the write position // as we use buffer for recv and send we have to reset the write position
streamer->setBufferContentPosition(0, 0); 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) if (streamer->available() == 0)
{ {
@ -71,10 +67,10 @@ void sendToDCC(Connection* c ,char *command, bool blocking)
else else
{ {
command[streamer->available()] = '\0'; // mark end of buffer, so it can be used as a string later command[streamer->available()] = '\0'; // mark end of buffer, so it can be used as a string later
DIAG(F("Response: %s\n"), command); DIAG(F("Response: %s\n"), t->command);
if (c->client->connected()) if (c->client->connected())
{ {
c->client->write((byte *)command, streamer->available()); c->client->write((byte *)t->command, streamer->available());
} }
} }
} }
@ -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 client Client who send the command to which the reply shall be send
* @param command Command initaliy recieved to be echoed back * @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 *number;
char *command = t->command;
char seqNumber[6]; char seqNumber[6];
int i = 0; int i = 0;
memset(reply, 0, MAX_ETH_BUFFER); // reset reply memset(reply, 0, MAX_ETH_BUFFER); // reset reply
number = strrchr(command, ':'); // replace the int after the last ':' // This expects messages to be send with a trailing sequence number <R 1 1 1:0>
while( &command[i] != number ) { // copy command into the reply upto the last ':' // 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]; reply[i] = command[i];
i++; i++;
} }
strcat((char *)reply, ":"); strcat((char *)reply, ":");
itoa(_sseq[c->id], seqNumber, 10); itoa(_sseq[c->id], seqNumber, 10);
strcat((char *)reply, seqNumber); strcat((char *)reply, seqNumber);
strcat((char *)reply, ">"); strcat((char *)reply, ">");
response = reply;
} else {
response = (byte *)command;
}
DIAG(F("Response: [%e]"), (char *)reply); DIAG(F("Response: [%e]"), (char *)response);
if (c->client->connected()) if (c->client->connected())
{ {
c->client->write(reply, strlen((char *)reply)); c->client->write(response, strlen((char *)response));
_sseq[c->id]++; _sseq[c->id]++;
DIAG(F(" send\n")); 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 client Client object from whom we receievd the data
* @param c id of the Client object * @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 * @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 * ev. send a 401 error back
*/ */
uint8_t i, l = 0; uint8_t i, l = 0;
ParsedRequest preq; ParsedRequest preq;
l = strlen((char *)buffer); l = strlen((char *)t->buffer);
for (i = 0; i < l; i++) for (i = 0; i < l; i++)
{ {
httpReq.parseRequest((char)buffer[i]); httpReq.parseRequest((char)t->buffer[i]);
} }
if (httpReq.endOfRequest()) if (httpReq.endOfRequest())
{ {
@ -216,7 +225,8 @@ appProtocol setAppProtocol(char a, char b, Connection *c)
p = DCCEX; p = DCCEX;
break; break;
} }
case '#': { case '#':
{
p = DCCEX; p = DCCEX;
DIAG(F("\nDiagnostics routed to network client\n")); DIAG(F("\nDiagnostics routed to network client\n"));
StringFormatter::setDiagOut(c); 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 * @brief Parses the buffer to extract commands to be executed
* *
*/ */
void processStream(Connection *c, TransportProcessor *t)
// void TransportProcessor::processStream(Connection *c)
void processStream(Connection *c)
{ {
uint8_t i, j, k, l = 0; 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 // copy overflow into the command
if ((i = strlen(c->overflow)) != 0) if ((i = strlen(c->overflow)) != 0)
{ {
// DIAG(F("\nCopy overflow to command: %e"), c->overflow); // DIAG(F("\nCopy overflow to command: %e"), c->overflow);
strncpy(command, c->overflow, i); strncpy(t->command, c->overflow, i);
k = i; k = i;
} }
// reset the overflow // reset the overflow
memset(c->overflow, 0, MAX_OVERFLOW); memset(c->overflow, 0, MAX_OVERFLOW);
// check if there is again an overflow and copy if needed // check if there is again an overflow and copy if needed
if ((i = strlen((char *)buffer)) == MAX_ETH_BUFFER - 1) 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); // DIAG(F("\nPossible overflow situation detected: %d "), i);
j = i; j = i;
while (buffer[i] != c->delimiter) while (_buffer[i] != c->delimiter)
{ // what if there is none: ? {
// DIAG(F("%c"),(char) buffer[i]);
i--; i--;
} }
i++; // start of the buffer to copy i++; // start of the buffer to copy
@ -273,32 +282,31 @@ void processStream(Connection *c)
for (j = 0; j < k; j++, i++) 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]); // 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 ); // DIAG(F("\nNew buffer: [%s] New overflow: [%s]\n"), (char*) buffer, c->overflow );
} }
// breakup the buffer using its changed length // breakup the buffer using its changed length
i = 0; i = 0;
k = strlen(command); // current length of the command buffer telling us where to start copy in k = strlen(t->command); // current length of the command buffer telling us where to start copy in
l = strlen((char *)buffer); 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) while (i < l)
{ {
// DIAG(F("\nl: %d k: %d , i: %d"), l, k, i); // DIAG(F("\nl: %d k: %d , i: %d"), l, k, i);
command[k] = buffer[i]; t->command[k] = _buffer[i];
if (buffer[i] == c->delimiter) if (_buffer[i] == c->delimiter)
{ // closing bracket need to fix if there is none before an opening bracket ? { // 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 #ifdef DCCEX_ENABLED
sendToDCC(c, command, true); sendToDCC(c, command, true);
#else #else
sendReply(c, command); sendReply(c, t);
#endif #endif
_rseq[c->id]++; _rseq[c->id]++;
j = 0; j = 0;
@ -312,24 +320,26 @@ void processStream(Connection *c)
} }
} }
void echoProcessor(Connection *c) void echoProcessor(Connection *c, TransportProcessor *t)
{ {
byte reply[MAX_ETH_BUFFER];
memset(reply, 0, MAX_ETH_BUFFER); memset(reply, 0, MAX_ETH_BUFFER);
sprintf((char *)reply, "ERROR: malformed content in [%s]", buffer); sprintf((char *)reply, "ERROR: malformed content in [%s]", t->buffer);
if (c->client->connected()) if (c->client->connected())
{ {
c->client->write(reply, strlen((char *)reply)); c->client->write(reply, strlen((char *)reply));
_sseq[c->id]++; _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) void TransportProcessor::readStream(Connection *c)
{ {
// read bytes from a client // 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 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; buffer[count] = 0;
@ -368,7 +379,7 @@ void TransportProcessor::readStream(Connection *c)
case HTTP: case HTTP:
{ {
c->appProtocolHandler = (appProtocolCallback)httpProcessor; c->appProtocolHandler = (appProtocolCallback)httpProcessor;
httpReq.callback = NetworkInterface::getHttpCallback(); httpReq.callback = nwi->getHttpCallback();
break; break;
} }
case UNKNOWN_PROTOCOL: 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 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("Client #: [%d]\n"), c->id);
DIAG(F("Packet: [%e]\n"), buffer); DIAG(F("Packet: [%e]\n"), buffer);
// chop the buffer into CS / WiThrottle commands || assemble command across buffer read boundaries // chop the buffer into CS / WiThrottle commands || assemble command across buffer read boundaries
c->appProtocolHandler(c); c->appProtocolHandler(c, this);
} }
/** /**
@ -408,63 +416,3 @@ void parse(Print *stream, byte *command, bool blocking)
// echo back (as mock parser ) // echo back (as mock parser )
StringFormatter::send(stream, F("reply to: %s"), command); 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 <Ethernet.h>
#include <WiFiEspAT.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 typedef enum
{ {
DCCEX, // if char[0] = < opening bracket the client should be a JMRI / DCC EX client_h DCCEX, // if char[0] = < opening bracket the client should be a JMRI / DCC EX client_h
@ -38,8 +37,11 @@ typedef enum
UNKNOWN_PROTOCOL UNKNOWN_PROTOCOL
} appProtocol; } appProtocol;
// Needed forward declarations
struct Connection; struct Connection;
using appProtocolCallback = void (*)(Connection *c); class TransportProcessor;
using appProtocolCallback = void (*)(Connection* c, TransportProcessor* t);
struct Connection struct Connection
{ {
@ -56,10 +58,15 @@ class TransportProcessor
{ {
private: private:
#ifdef DCCEX_ENABLED #ifdef DCCEX_ENABLED
void sendToDCC(Connection *c, char *command, bool blocking); void sendToDCC(Connection *c, TransportProcessor* t, bool blocking);
#endif #endif
public: 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 void readStream(Connection *c); // reads incomming packets and hands over to the commandHandle for taking the stream apart for commands
TransportProcessor(){}; TransportProcessor(){};

View File

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