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:
parent
c1e4727ee8
commit
5888e21090
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,6 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
// EthernetServer *setup(uint16_t port);
|
||||
EthernetServer *setup();
|
||||
|
||||
EthernetSetup();
|
||||
|
47
NetworkConfig.h
Normal file
47
NetworkConfig.h
Normal 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
|
@ -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(){}
|
@ -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();
|
||||
|
@ -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:
|
||||
|
@ -178,5 +178,4 @@ void StringFormatter::printPadded(Print* stream, long value, byte width, bool fo
|
||||
}
|
||||
if (!formatLeft) stream->print(value, DEC);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
@ -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
|
||||
*/
|
||||
|
||||
|
34
Transport.h
34
Transport.h
@ -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
|
@ -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
|
||||
}
|
||||
*/
|
||||
}
|
@ -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(){};
|
||||
|
@ -40,13 +40,11 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
// WiFiServer *setup(uint16_t port);
|
||||
bool setup();
|
||||
|
||||
WiFiUDP* getUdp() {
|
||||
return udp;
|
||||
}
|
||||
|
||||
WiFiServer* getServer() {
|
||||
return server;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user