mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-23 08:06:13 +01:00
Diag output over MQTT added
This commit is contained in:
parent
e51279b202
commit
9fcc69d273
7
DIAG.h
7
DIAG.h
|
@ -16,14 +16,15 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef DIAG_h
|
#ifndef DIAG_h
|
||||||
#define DIAG_h
|
#define DIAG_h
|
||||||
|
|
||||||
#include "StringFormatter.h"
|
#include "StringFormatter.h"
|
||||||
#include "StringLogger.h"
|
#include "DiagLogger.h"
|
||||||
|
|
||||||
// #define DIAG StringFormatter::diag
|
// #define DIAG StringFormatter::diag // Std logging to serial only
|
||||||
#define DIAG StringLogger::get().diag // allows to add other log writers
|
#define DIAG DiagLogger::get().diag // allows to add other log writers
|
||||||
#define LCD StringFormatter::lcd
|
#define LCD StringFormatter::lcd
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
63
DiagLogger.cpp
Normal file
63
DiagLogger.cpp
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* © 2021, Gregor Baues, All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is part of DCC-EX/CommandStation-EX
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "DiagLogger.h"
|
||||||
|
|
||||||
|
// DIAG.h the #define DIAG points to here ...
|
||||||
|
// EthernetSetup , Wifisetup, etc can register a function to be called allowing the channel
|
||||||
|
// to publish the diag info to
|
||||||
|
// serial is default end enabled all the time
|
||||||
|
|
||||||
|
DiagLogger DiagLogger::singleton; // static instantiation;
|
||||||
|
|
||||||
|
void DiagLogger::addDiagWriter(DiagWriter l) {
|
||||||
|
if ( registered == MAXWRITERS ) {
|
||||||
|
Serial.println("Error: Max amount of writers exceeded.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
writers[registered] = l;
|
||||||
|
registered++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DiagLogger::diag(const FSH *input,...)
|
||||||
|
{
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, input);
|
||||||
|
|
||||||
|
int len = 0;
|
||||||
|
len += sprintf(&b1[len], "<* ");
|
||||||
|
len += vsprintf_P(&b1[len], (const char *)input, args);
|
||||||
|
len += sprintf(&b1[len], " *>\n");
|
||||||
|
|
||||||
|
if ( len >= 256 ) { Serial.print("ERROR : Diag Buffer overflow"); return; }
|
||||||
|
// allways print to Serial
|
||||||
|
Serial.print(b1);
|
||||||
|
|
||||||
|
// callback the other registered diag writers
|
||||||
|
for (size_t i = 0; i < (size_t) registered; i++)
|
||||||
|
{
|
||||||
|
writers[i](b1, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
}
|
59
DiagLogger.h
Normal file
59
DiagLogger.h
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* © 2021, Gregor Baues, All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is part of DCC-EX/CommandStation-EX
|
||||||
|
*
|
||||||
|
* This is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* It is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DiagLogger_h
|
||||||
|
#define DiagLogger_h
|
||||||
|
|
||||||
|
#include "StringFormatter.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define MAXWRITERS 10
|
||||||
|
typedef void (*DiagWriter)(const char *msg, const int length);
|
||||||
|
|
||||||
|
class DiagLogger
|
||||||
|
{
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Methods
|
||||||
|
DiagLogger() = default;
|
||||||
|
DiagLogger(const DiagLogger &); // non construction-copyable
|
||||||
|
DiagLogger &operator=(const DiagLogger &); // non copyable
|
||||||
|
|
||||||
|
// Members
|
||||||
|
static DiagLogger singleton; // unique instance of the MQTTInterface object
|
||||||
|
DiagWriter writers[MAXWRITERS];
|
||||||
|
int registered = 0; // number of registered writers ( Serial is not counted as always used )
|
||||||
|
char b1[256];
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Methods
|
||||||
|
static DiagLogger &get() noexcept
|
||||||
|
{ // return a reference to the unique instance
|
||||||
|
return singleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
void diag(const FSH *input...);
|
||||||
|
void addDiagWriter(DiagWriter l);
|
||||||
|
~DiagLogger() = default;
|
||||||
|
|
||||||
|
// Members
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -75,14 +75,17 @@ void mqttDiag(const char *msg, const int length)
|
||||||
{
|
{
|
||||||
sprintf(topic, "%s/%ld/diag", MQTTInterface::get()->getClientID(), MQTTInterface::get()->getClients()[mqSocket].topic);
|
sprintf(topic, "%s/%ld/diag", MQTTInterface::get()->getClientID(), MQTTInterface::get()->getClients()[mqSocket].topic);
|
||||||
}
|
}
|
||||||
// Serial.print(" ---- MQTT pub to: "); Serial.print(topic); Serial.print(" Msg: "); Serial.print(msg);
|
// Serial.print(" ---- MQTT pub to: ");
|
||||||
|
// Serial.print(topic);
|
||||||
|
// Serial.print(" Msg: ");
|
||||||
|
// Serial.print(msg);
|
||||||
MQTTInterface::get()->publish(topic, msg);
|
MQTTInterface::get()->publish(topic, msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MQTTInterface::setup()
|
void MQTTInterface::setup()
|
||||||
{
|
{
|
||||||
StringLogger::get().addDiagWriter(mqttDiag);
|
DiagLogger::get().addDiagWriter(mqttDiag);
|
||||||
singleton = new MQTTInterface();
|
singleton = new MQTTInterface();
|
||||||
|
|
||||||
if (!singleton->connected)
|
if (!singleton->connected)
|
||||||
|
@ -108,6 +111,42 @@ MQTTInterface::MQTTInterface()
|
||||||
this->outboundRing = new RingStream(OUT_BOUND_SIZE);
|
this->outboundRing = new RingStream(OUT_BOUND_SIZE);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief determine the mqsocket from a topic
|
||||||
|
*
|
||||||
|
* @return byte the mqsocketid for the message recieved
|
||||||
|
*/
|
||||||
|
byte senderMqSocket(MQTTInterface *mqtt, char *topic)
|
||||||
|
{
|
||||||
|
// list of all available clients from which we can determine the mqsocket
|
||||||
|
auto clients = mqtt->getClients();
|
||||||
|
const char s[2] = "/"; // topic delimiter is /
|
||||||
|
char *token;
|
||||||
|
byte mqsocket = 0;
|
||||||
|
|
||||||
|
/* get the first token = ClientID */
|
||||||
|
token = strtok(topic, s);
|
||||||
|
/* get the second token = topicID */
|
||||||
|
token = strtok(NULL, s);
|
||||||
|
if (token != NULL) // topic didn't contain any topicID
|
||||||
|
{
|
||||||
|
auto topicid = atoi(token);
|
||||||
|
// verify that there is a MQTT client with that topic id connected
|
||||||
|
// check in the array of clients if we have one with the topicid
|
||||||
|
// start at 1 as 0 is not allocated as mqsocket
|
||||||
|
for (int i = 1; i <= mqtt->getClientSize(); i++)
|
||||||
|
{
|
||||||
|
if (clients[i].topic == topicid)
|
||||||
|
{
|
||||||
|
mqsocket = i;
|
||||||
|
break; // we are done
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if we get here we have a topic but no associated client
|
||||||
|
}
|
||||||
|
// if mqsocket == 0 here we haven't got any Id in the topic string
|
||||||
|
return mqsocket;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* @brief MQTT Interface callback recieving all incomming messages from the PubSubClient
|
* @brief MQTT Interface callback recieving all incomming messages from the PubSubClient
|
||||||
*
|
*
|
||||||
|
@ -115,96 +154,68 @@ MQTTInterface::MQTTInterface()
|
||||||
* @param payload
|
* @param payload
|
||||||
* @param length
|
* @param length
|
||||||
*/
|
*/
|
||||||
void mqttCallback(char *topic, byte *payload, unsigned int length)
|
void mqttCallback(char *topic, byte *pld, unsigned int length)
|
||||||
{
|
{
|
||||||
|
// it's a bounced diag message ignore in all cases
|
||||||
|
// but it should not be necessary here .. that means the active mqsocket is wrong when sending to diag message
|
||||||
|
if ( (pld[0] == '<') && (pld[1] == '*'))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// ignore anything above the PAYLOAD limit of 64 char which should be enough
|
||||||
|
// in general things rejected here is the bounce of the inital messages setting up the chnanel etc
|
||||||
|
if (length >= MAXPAYLOAD)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
MQTTInterface *mqtt = MQTTInterface::get();
|
MQTTInterface *mqtt = MQTTInterface::get();
|
||||||
auto clients = mqtt->getClients();
|
auto clients = mqtt->getClients();
|
||||||
|
|
||||||
errno = 0;
|
errno = 0;
|
||||||
payload[length] = '\0'; // make sure we have the string terminator in place
|
csmsg_t tm; // topic message
|
||||||
if (Diag::MQTT)
|
|
||||||
DIAG(F("MQTT Callback:[%s] [%s] [%d] on interface [%x]"), topic, (char *)payload, length, mqtt);
|
// FOR DIAGS and MQTT ON in the callback we need to copy the payload buffer
|
||||||
switch (payload[0])
|
// as during the publish of the diag messages the original payload gets destroyed
|
||||||
|
// so we setup the csmsg_t now to save yet another buffer
|
||||||
|
// if tm not used it will just be discarded at the end of the function call
|
||||||
|
|
||||||
|
memset(tm.cmd, 0, MAXPAYLOAD); // Clean up the cmd buffer - should not be necessary
|
||||||
|
strlcpy(tm.cmd, (char *)pld, length + 1); // Message payload
|
||||||
|
tm.mqsocket = senderMqSocket(mqtt,topic); // On which socket did we recieve the mq message
|
||||||
|
mqtt->setActive(tm.mqsocket); // connection from where we recieved the command is active now
|
||||||
|
|
||||||
|
if (Diag::MQTT) DIAG(F("MQTT Callback:[%s/%d] [%s] [%d] on interface [%x]"), topic, tm.mqsocket, tm.cmd, length, mqtt);
|
||||||
|
|
||||||
|
switch (tm.cmd[0])
|
||||||
{
|
{
|
||||||
case '<': // Recieved a DCC-EX Command
|
case '<': // Recieved a DCC-EX Command
|
||||||
{
|
{
|
||||||
if (payload[1] == '*') { return;} // it's a bounced diag message
|
if(!tm.mqsocket) {
|
||||||
const char s[2] = "/"; // topic delimiter is /
|
DIAG(F("MQTT Can't identify sender; command send on wrong topic"));
|
||||||
char *token;
|
|
||||||
byte mqsocket;
|
|
||||||
|
|
||||||
/* get the first token = ClientID */
|
|
||||||
token = strtok(topic, s);
|
|
||||||
/* get the second token = topicID */
|
|
||||||
token = strtok(NULL, s);
|
|
||||||
if (token == NULL)
|
|
||||||
{
|
|
||||||
DIAG(F("MQTT Can't identify sender #1; command send on wrong topic"));
|
|
||||||
return;
|
|
||||||
// don't do anything as we wont know where to send the results
|
|
||||||
// normally the topicid shall be valid as we only have subscribed to that one and nothing else
|
|
||||||
// comes here; The only issue is when recieveing on the open csid channel ( which stays open in order to
|
|
||||||
// able to accept other connections )
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto topicid = atoi(token);
|
|
||||||
// verify that there is a MQTT client with that topic id connected
|
|
||||||
bool isClient = false;
|
|
||||||
// check in the array of clients if we have one with the topicid
|
|
||||||
// start at 1 as 0 is not allocated as mqsocket
|
|
||||||
for (int i = 1; i <= mqtt->getClientSize(); i++)
|
|
||||||
// for (int i = 1; i <= subscriberid; i++)
|
|
||||||
{
|
|
||||||
if (clients[i].topic == topicid)
|
|
||||||
{
|
|
||||||
isClient = true;
|
|
||||||
mqsocket = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!isClient)
|
|
||||||
{
|
|
||||||
// no such client connected
|
|
||||||
DIAG(F("MQTT Can't identify sender #2; command send on wrong topic"));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// if we make it until here we dont even need to test the last "cmd" element from the topic as there is no
|
|
||||||
// subscription for anything else
|
|
||||||
|
|
||||||
// Prepare the DCC-EX command
|
|
||||||
csmsg_t tm; // topic message
|
|
||||||
|
|
||||||
if (length >= MAXPAYLOAD)
|
|
||||||
{
|
|
||||||
DIAG(F("MQTT Command too long (> [%d] characters)"), MAXPAYLOAD);
|
|
||||||
}
|
|
||||||
memset(tm.cmd, 0, MAXPAYLOAD); // Clean up the cmd buffer - should not be necessary
|
|
||||||
strlcpy(tm.cmd, (char *)payload, length + 1); // Message payload
|
|
||||||
tm.mqsocket = mqsocket; // On which socket did we recieve the mq message
|
|
||||||
int idx = mqtt->getPool()->setItem(tm); // Add the recieved command to the pool
|
int idx = mqtt->getPool()->setItem(tm); // Add the recieved command to the pool
|
||||||
|
|
||||||
if (idx == -1)
|
if (idx == -1)
|
||||||
{
|
{
|
||||||
DIAG(F("MQTT Command pool full. Could not handle recieved command."));
|
DIAG(F("MQTT Command pool full. Could not handle recieved command."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mqtt->getIncomming()->push(idx); // Add the index of the pool item to the incomming queue
|
mqtt->getIncomming()->push(idx); // Add the index of the pool item to the incomming queue
|
||||||
|
|
||||||
|
// don't show the topic as we would have to save it also just like the payload
|
||||||
if (Diag::MQTT)
|
if (Diag::MQTT)
|
||||||
DIAG(F("MQTT Message arrived [%s]: [%s]"), topic, tm.cmd);
|
DIAG(F("MQTT Message arrived: [%s]"), tm.cmd);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'm': // Recieved an MQTT Connection management message
|
case 'm': // Recieved an MQTT Connection management message
|
||||||
{
|
{
|
||||||
switch (payload[1])
|
switch (tm.cmd[1])
|
||||||
{
|
{
|
||||||
case 'i': // Inital handshake message to create the tunnel
|
case 'i': // Inital handshake message to create the tunnel
|
||||||
{
|
{
|
||||||
char buffer[MAXPAYLOAD];
|
char buffer[MAXPAYLOAD];
|
||||||
char *tmp = (char *)payload + 3;
|
char *tmp = tm.cmd + 3;
|
||||||
strlcpy(buffer, tmp, length);
|
strlcpy(buffer, tmp, length);
|
||||||
buffer[length - 4] = '\0';
|
buffer[length - 4] = '\0';
|
||||||
|
|
||||||
|
@ -244,18 +255,14 @@ void mqttCallback(char *topic, byte *payload, unsigned int length)
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
default: // Invalid message
|
default:
|
||||||
{
|
{
|
||||||
// ignore
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default: // invalid command / message
|
default:
|
||||||
{
|
{
|
||||||
// this may be the echo comming back on the main channel to which we are also subscribed
|
|
||||||
// si just ignore for now
|
|
||||||
// DIAG(F("MQTT Invalid DCC-EX command: %s"), (char *)payload);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -457,6 +464,7 @@ void inLoop(Queue<int> &in, ObjectPool<csmsg_t, MAXPOOLSIZE> &pool, RingStream *
|
||||||
int idx = in.pop();
|
int idx = in.pop();
|
||||||
csmsg_t *c = pool.getItem(idx, &state);
|
csmsg_t *c = pool.getItem(idx, &state);
|
||||||
|
|
||||||
|
MQTTInterface::get()->setActive(c->mqsocket); // connection from where we recieved the command is active now
|
||||||
// execute the command and collect results
|
// execute the command and collect results
|
||||||
outboundRing->mark((uint8_t)c->mqsocket);
|
outboundRing->mark((uint8_t)c->mqsocket);
|
||||||
CommandDistributor::parse(c->mqsocket, (byte *)c->cmd, outboundRing);
|
CommandDistributor::parse(c->mqsocket, (byte *)c->cmd, outboundRing);
|
||||||
|
@ -547,8 +555,7 @@ void checkSubscribers(Queue<int> &sq, csmqttclient_t *clients)
|
||||||
mqtt->getClientID(),
|
mqtt->getClientID(),
|
||||||
clients[s].topic,
|
clients[s].topic,
|
||||||
mqtt->getClientID(),
|
mqtt->getClientID(),
|
||||||
clients[s].topic
|
clients[s].topic);
|
||||||
);
|
|
||||||
|
|
||||||
if (Diag::MQTT)
|
if (Diag::MQTT)
|
||||||
DIAG(F("MQTT channel setup message: [%s]"), buffer);
|
DIAG(F("MQTT channel setup message: [%s]"), buffer);
|
||||||
|
@ -562,15 +569,14 @@ void checkSubscribers(Queue<int> &sq, csmqttclient_t *clients)
|
||||||
|
|
||||||
void MQTTInterface::loop()
|
void MQTTInterface::loop()
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!singleton)
|
if (!singleton)
|
||||||
return;
|
return;
|
||||||
singleton->loop2();
|
singleton->loop2();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool showonce = false;
|
bool showonce = false;
|
||||||
auto s = millis();
|
auto s = millis();
|
||||||
|
|
||||||
void loopPing(int interval)
|
void loopPing(int interval)
|
||||||
{
|
{
|
||||||
auto c = millis();
|
auto c = millis();
|
||||||
|
@ -581,15 +587,15 @@ void loopPing(int interval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MQTTInterface::loop2()
|
void MQTTInterface::loop2()
|
||||||
{
|
{
|
||||||
|
|
||||||
loopPing(2000); // ping every 2 sec
|
// loopPing(2000); // ping every 2 sec
|
||||||
// Connection impossible so just don't do anything
|
// Connection impossible so just don't do anything
|
||||||
if (singleton->mqState == CONNECTION_FAILED)
|
if (singleton->mqState == CONNECTION_FAILED)
|
||||||
{
|
{
|
||||||
if(!showonce) {
|
if (!showonce)
|
||||||
|
{
|
||||||
DIAG(F("MQTT connection failed..."));
|
DIAG(F("MQTT connection failed..."));
|
||||||
showonce = true;
|
showonce = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,23 +41,17 @@
|
||||||
#define MAXPAYLOAD 64 // max length of a payload recieved
|
#define MAXPAYLOAD 64 // max length of a payload recieved
|
||||||
#define MAXDOMAINLENGTH 32 // domain name length for the broker e.g. test.mosquitto.org
|
#define MAXDOMAINLENGTH 32 // domain name length for the broker e.g. test.mosquitto.org
|
||||||
|
|
||||||
#define MAXTBUF 50 //!< max length of the buffer for building the topic name ;to be checked
|
#define MAXTBUF 64 //!< max length of the buffer for building the topic name ;to be checked
|
||||||
#define MAXTMSG 120 //!< max length of the messages for a topic ;to be checked PROGMEM ?
|
#define MAXTMSG 64 //!< max length of the messages for a topic ;to be checked PROGMEM ?
|
||||||
#define MAXTSTR 30 //!< max length of a topic string
|
#define MAXTSTR 32 //!< max length of a topic string
|
||||||
#define MAXCONNECTID 40 // broker connection id length incl possible prefixes
|
#define MAXCONNECTID 32 // broker connection id length incl possible prefixes
|
||||||
#define CLIENTIDSIZE 6 // max length of the clientid used for connection to the broker
|
#define CLIENTIDSIZE 6 // max length of the clientid used for connection to the broker
|
||||||
#define MAXRECONNECT 5 // reconnection tries before final failure
|
#define MAXRECONNECT 5 // reconnection tries before final failure
|
||||||
#define MAXMQTTCONNECTIONS 20 // maximum number of unique tpoics available for subscribers
|
#define MAXMQTTCONNECTIONS 20 // maximum number of unique tpoics available for subscribers
|
||||||
#define OUT_BOUND_SIZE 256 // Size of the RingStream used to provide results from the parser and publish
|
#define OUT_BOUND_SIZE 128 // Size of the RingStream used to provide results from the parser and publish
|
||||||
#define MAX_POOL_SIZE 32 // recieved command store size
|
#define MAX_POOL_SIZE 16 // recieved command store size
|
||||||
|
#define MAX_CALLBACKS 10
|
||||||
|
|
||||||
// Define Broker configurations; Values are provided in the following order
|
|
||||||
// MQTT_BROKER_PORT 9883
|
|
||||||
// MQTT_BROKER_DOMAIN "dcclms.modelrailroad.ovh"
|
|
||||||
// MQTT_BROKER_ADDRESS 51, 210, 151, 143
|
|
||||||
// MQTT_BROKER_USER "dcccs"
|
|
||||||
// MQTT_BROKER_PASSWD "dcccs$3020"
|
|
||||||
// MQTT_BROKER_CLIENTID_PREFIX "dcc$lms-"
|
|
||||||
struct MQTTBroker
|
struct MQTTBroker
|
||||||
{
|
{
|
||||||
int port;
|
int port;
|
||||||
|
|
|
@ -1,98 +0,0 @@
|
||||||
/*
|
|
||||||
|
|
||||||
(c) 2015 Ingo Fischer
|
|
||||||
buffer serial device
|
|
||||||
based on Arduino SoftwareSerial
|
|
||||||
|
|
||||||
Constructor warning messages fixed by Chris Harlow.
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "MemStream.h"
|
|
||||||
|
|
||||||
MemStream::MemStream(uint8_t *buffer, const uint16_t len, uint16_t content_len, bool allowWrite)
|
|
||||||
:_buffer(buffer),_len(len), _buffer_overflow(false), _pos_read(0), _allowWrite(allowWrite)
|
|
||||||
{
|
|
||||||
if (content_len==0) memset(_buffer, 0, _len);
|
|
||||||
_pos_write=(content_len>len)? len: content_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t MemStream::write(uint8_t byte) {
|
|
||||||
if (! _allowWrite) return -1;
|
|
||||||
if (_pos_write >= _len) {
|
|
||||||
_buffer_overflow = true;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
_buffer[_pos_write] = byte;
|
|
||||||
++_pos_write;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemStream::flush() {
|
|
||||||
memset(_buffer, 0, _len);
|
|
||||||
_pos_write = 0;
|
|
||||||
_pos_read = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int MemStream::read() {
|
|
||||||
if (_pos_read >= _len) {
|
|
||||||
_buffer_overflow = true;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (_pos_read >= _pos_write) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return _buffer[_pos_read++];
|
|
||||||
}
|
|
||||||
|
|
||||||
int MemStream::peek() {
|
|
||||||
if (_pos_read >= _len) {
|
|
||||||
_buffer_overflow = true;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (_pos_read >= _pos_write) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return _buffer[_pos_read+1];
|
|
||||||
}
|
|
||||||
|
|
||||||
int MemStream::available() {
|
|
||||||
int ret=_pos_write-_pos_read;
|
|
||||||
if (ret<0) ret=0;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemStream::setBufferContent(uint8_t *buffer, uint16_t content_len) {
|
|
||||||
memset(_buffer, 0, _len);
|
|
||||||
memcpy(_buffer, buffer, content_len);
|
|
||||||
_buffer_overflow=false;
|
|
||||||
_pos_write=content_len;
|
|
||||||
_pos_read=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemStream::setBufferContentFromProgmem(uint8_t *buffer, uint16_t content_len) {
|
|
||||||
memset(_buffer, 0, _len);
|
|
||||||
memcpy_P(_buffer, buffer, content_len);
|
|
||||||
_buffer_overflow=false;
|
|
||||||
_pos_write=content_len;
|
|
||||||
_pos_read=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemStream::setBufferContentPosition(uint16_t read_pos, uint16_t write_pos) {
|
|
||||||
_pos_write=write_pos;
|
|
||||||
_pos_read=read_pos;
|
|
||||||
}
|
|
78
MemStream.h
78
MemStream.h
|
@ -1,78 +0,0 @@
|
||||||
/*
|
|
||||||
|
|
||||||
(c) 2015 Ingo FIscher
|
|
||||||
buffer serial device
|
|
||||||
based on Arduino SoftwareSerial
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef MemStream_h
|
|
||||||
#define MemStream_h
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#if defined(ARDUINO_ARCH_MEGAAVR)
|
|
||||||
#include <Arduino.h>
|
|
||||||
#else
|
|
||||||
#include <Stream.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <avr/pgmspace.h>
|
|
||||||
|
|
||||||
class MemStream : public Stream
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
uint8_t *_buffer;
|
|
||||||
const uint16_t _len;
|
|
||||||
bool _buffer_overflow;
|
|
||||||
uint16_t _pos_read;
|
|
||||||
uint16_t _pos_write;
|
|
||||||
bool _allowWrite;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// public methods
|
|
||||||
MemStream(uint8_t *buffer, const uint16_t len, uint16_t content_len = 0, bool allowWrite = true);
|
|
||||||
~MemStream() {}
|
|
||||||
|
|
||||||
operator const uint8_t *() const { return _buffer; }
|
|
||||||
operator const char *() const { return (const char *)_buffer; }
|
|
||||||
|
|
||||||
uint16_t current_length() const { return _pos_write; }
|
|
||||||
|
|
||||||
bool listen() { return true; }
|
|
||||||
void end() {}
|
|
||||||
bool isListening() { return true; }
|
|
||||||
bool overflow()
|
|
||||||
{
|
|
||||||
bool ret = _buffer_overflow;
|
|
||||||
_buffer_overflow = false;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
int peek();
|
|
||||||
|
|
||||||
virtual size_t write(uint8_t byte);
|
|
||||||
virtual int read();
|
|
||||||
virtual int available();
|
|
||||||
virtual void flush();
|
|
||||||
|
|
||||||
void setBufferContent(uint8_t *buffer, uint16_t content_len);
|
|
||||||
void setBufferContentFromProgmem(uint8_t *buffer, uint16_t content_len);
|
|
||||||
void setBufferContentPosition(uint16_t read_pos, uint16_t write_pos);
|
|
||||||
|
|
||||||
using Print::write;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
20
ObjectPool.h
20
ObjectPool.h
|
@ -1,3 +1,23 @@
|
||||||
|
/*
|
||||||
|
* © 2021, Gregor Baues, All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is part of DCC-EX/CommandStation-EX
|
||||||
|
*
|
||||||
|
* This is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* It is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef _ObjectPool_h_
|
#ifndef _ObjectPool_h_
|
||||||
#define _ObjectPool_h_
|
#define _ObjectPool_h_
|
||||||
|
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
#include "StringLogger.h"
|
|
||||||
|
|
||||||
// DIAG.h the #define DIAG points to here ...
|
|
||||||
// EthernetSetup , Wifisetup, etc can register a function to be called allowing the channel
|
|
||||||
// to publish the diag info to
|
|
||||||
// serial is default end enabled all the time
|
|
||||||
|
|
||||||
StringLogger StringLogger::singleton; // static instantiation;
|
|
||||||
|
|
||||||
void StringLogger::addDiagWriter(DiagWriter l) {
|
|
||||||
if ( registered == MAXWRITERS ) {
|
|
||||||
Serial.println("Error: Max amount of writers exceeded.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
writers[registered] = l;
|
|
||||||
registered++;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void StringLogger::diag(const FSH *input,...)
|
|
||||||
{
|
|
||||||
|
|
||||||
char b1[128];
|
|
||||||
|
|
||||||
va_list args;
|
|
||||||
va_start(args, input);
|
|
||||||
|
|
||||||
int len = 0;
|
|
||||||
len += sprintf(&b1[len], "<* ");
|
|
||||||
len += vsprintf_P(&b1[len], (const char *)input, args);
|
|
||||||
len += sprintf(&b1[len], " *>\n");
|
|
||||||
|
|
||||||
// allways print to Serial
|
|
||||||
Serial.print(b1);
|
|
||||||
|
|
||||||
// callback the other registered diag writers
|
|
||||||
for (size_t i = 0; i < (size_t) registered; i++)
|
|
||||||
{
|
|
||||||
writers[i](b1, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
va_end(args);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
#ifndef StringLogger_h
|
|
||||||
#define StringLogger_h
|
|
||||||
|
|
||||||
#include "MemStream.h"
|
|
||||||
#include "RingStream.h"
|
|
||||||
#include "StringFormatter.h"
|
|
||||||
#include <stdarg.h>
|
|
||||||
|
|
||||||
// stream for diagnostics in addition to serial you should be able to configure
|
|
||||||
// additional (besides Serial) 'outputs' to e.g. Ethernet or MQTT or WIFI etc ...
|
|
||||||
// Serial outpout is managed in StringFormatter on top for formatting the message
|
|
||||||
// which gets printed char by char
|
|
||||||
|
|
||||||
#define MAXWRITERS 10
|
|
||||||
|
|
||||||
typedef void (*DiagWriter)(const char *msg, const int length);
|
|
||||||
|
|
||||||
class StringLogger
|
|
||||||
{
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Methods
|
|
||||||
StringLogger() = default;
|
|
||||||
StringLogger(const StringLogger &); // non construction-copyable
|
|
||||||
StringLogger &operator=(const StringLogger &); // non copyable
|
|
||||||
|
|
||||||
// Members
|
|
||||||
static StringLogger singleton; // unique instance of the MQTTInterface object
|
|
||||||
DiagWriter writers[MAXWRITERS];
|
|
||||||
int registered = 0; // number of registered writers ( Serial is not counted as always used )
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Methods
|
|
||||||
static StringLogger &get() noexcept
|
|
||||||
{ // return a reference to the unique instance
|
|
||||||
return singleton;
|
|
||||||
}
|
|
||||||
void diag(const FSH *input...);
|
|
||||||
void addDiagWriter(DiagWriter l);
|
|
||||||
~StringLogger() = default;
|
|
||||||
|
|
||||||
// Members
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -42,14 +42,16 @@
|
||||||
#if ENABLE_MQTT && (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560) || defined(ARDUINO_SAMD_ZERO) || defined(TEENSYDUINO))
|
#if ENABLE_MQTT && (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560) || defined(ARDUINO_SAMD_ZERO) || defined(TEENSYDUINO))
|
||||||
#if ENABLE_ETHERNET
|
#if ENABLE_ETHERNET
|
||||||
#error Ethernet and MQTT can not be enabled simultaneaously
|
#error Ethernet and MQTT can not be enabled simultaneaously
|
||||||
|
#elif ENABLE_WIFI
|
||||||
|
#error WIFI and MQTT can not be enabled simultaneaously
|
||||||
#else
|
#else
|
||||||
#define MQTT_ON true
|
#define MQTT_ON true
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// #if WIFI_ON && ETHERNET_ON
|
#if WIFI_ON && ETHERNET_ON
|
||||||
// #error Command Station does not support WIFI and ETHERNET at the same time.
|
#error Command Station does not support WIFI and ETHERNET at the same time.
|
||||||
// #endif
|
#endif
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
|
Loading…
Reference in New Issue
Block a user