1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-11-22 23:56:13 +01:00

Command Distributor

Improve and split multi-language responses. Remove dependency on RingStream.
This commit is contained in:
Asbelos 2022-06-10 12:22:28 +01:00
parent 2b2012ef1d
commit 16fafccf15
7 changed files with 172 additions and 59 deletions

View File

@ -30,16 +30,29 @@
#include "DCC.h" #include "DCC.h"
#include "TrackManager.h" #include "TrackManager.h"
#if defined(BIG_MEMORY) | defined(WIFI_ON) | defined(ETHERNET_ON)
// This section of CommandDistributor is simply not relevant on a uno or similar
const byte NO_CLIENT=255;
#ifdef BIG_RAM
// use a buffer to allow broadcast
#define BUFFER broadcastBufferWriter
#define FLUSH broadcastBufferWriter->flush();
#define SHOVE(type) broadcastToClients(type);
StringBuffer * CommandDistributor::broadcastBufferWriter=new StringBuffer();
#else
// on a UNO/NANO write direct to Serial and ignore flush/shove
#define BUFFER &Serial
#define FLUSH
#define SHOVE(type)
#endif
#ifdef CD_HANDLE_RING
// wifi or ethernet ring streams with multiple client types
RingStream * CommandDistributor::ring=0; RingStream * CommandDistributor::ring=0;
byte CommandDistributor::ringClient=NO_CLIENT; byte CommandDistributor::ringClient=NO_CLIENT;
CommandDistributor::clientType CommandDistributor::clients[8]={ CommandDistributor::clientType CommandDistributor::clients[8]={
NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE}; NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE};
RingStream * CommandDistributor::broadcastBufferWriter=new RingStream(100);
// Parse is called by Withrottle or Ethernet interface to determine which
// protocol the client is using and call the appropriate part of dcc++Ex
void CommandDistributor::parse(byte clientId,byte * buffer, RingStream * stream) { void CommandDistributor::parse(byte clientId,byte * buffer, RingStream * stream) {
ring=stream; ring=stream;
ringClient=stream->peekTargetMark(); ringClient=stream->peekTargetMark();
@ -56,15 +69,15 @@ void CommandDistributor::parse(byte clientId,byte * buffer, RingStream * stream
void CommandDistributor::forget(byte clientId) { void CommandDistributor::forget(byte clientId) {
clients[clientId]=NONE_TYPE; clients[clientId]=NONE_TYPE;
} }
#endif
// This will not be called on a uno
void CommandDistributor::broadcast(bool includeWithrottleClients) { void CommandDistributor::broadcastToClients(clientType type) {
broadcastBufferWriter->write((byte)'\0');
/* Boadcast to Serials */ /* Boadcast to Serials */
SerialManager::broadcast(broadcastBufferWriter); if (type==COMMAND_TYPE) SerialManager::broadcast(broadcastBufferWriter->getString());
#if defined(WIFI_ON) | defined(ETHERNET_ON) #ifdef CD_HANDLE_RING
// If we are broadcasting from a wifi/eth process we need to complete its output // If we are broadcasting from a wifi/eth process we need to complete its output
// before merging broadcasts in the ring, then reinstate it in case // before merging broadcasts in the ring, then reinstate it in case
// the process continues to output to its client. // the process continues to output to its client.
@ -72,49 +85,46 @@ void CommandDistributor::broadcast(bool includeWithrottleClients) {
/* loop through ring clients */ /* loop through ring clients */
for (byte clientId=0; clientId<sizeof(clients); clientId++) { for (byte clientId=0; clientId<sizeof(clients); clientId++) {
if (clients[clientId]==NONE_TYPE) continue; if (clients[clientId]==type) {
if ( clients[clientId]==WITHROTTLE_TYPE && !includeWithrottleClients) continue;
ring->mark(clientId); ring->mark(clientId);
broadcastBufferWriter->printBuffer(ring); ring->print(broadcastBufferWriter->getString());
ring->commit(); ring->commit();
} }
}
if (ringClient!=NO_CLIENT) ring->mark(ringClient); if (ringClient!=NO_CLIENT) ring->mark(ringClient);
#endif #endif
broadcastBufferWriter->flush();
} }
#else
// For a UNO/NANO we can broadcast direct to just one Serial instead of the ring
// Redirect ring output ditrect to Serial
#define broadcastBufferWriter &Serial
// and ignore the internal broadcast call.
void CommandDistributor::broadcast(bool includeWithrottleClients) {
(void)includeWithrottleClients;
}
#endif
// Public broadcast functions below
void CommandDistributor::broadcastSensor(int16_t id, bool on ) { void CommandDistributor::broadcastSensor(int16_t id, bool on ) {
StringFormatter::send(broadcastBufferWriter,F("<%c %d>\n"), on?'Q':'q', id); FLUSH
broadcast(false); StringFormatter::send(BUFFER,F("<%c %d>\n"), on?'Q':'q', id);
SHOVE(COMMAND_TYPE)
} }
void CommandDistributor::broadcastTurnout(int16_t id, bool isClosed ) { void CommandDistributor::broadcastTurnout(int16_t id, bool isClosed ) {
// For DCC++ classic compatibility, state reported to JMRI is 1 for thrown and 0 for closed; // For DCC++ classic compatibility, state reported to JMRI is 1 for thrown and 0 for closed;
// The string below contains serial and Withrottle protocols which should // The string below contains serial and Withrottle protocols which should
// be safe for both types. // be safe for both types.
StringFormatter::send(broadcastBufferWriter,F("<H %d %d>\n"),id, !isClosed); FLUSH
#if defined(WIFI_ON) | defined(ETHERNET_ON) StringFormatter::send(BUFFER,F("<H %d %d>\n"),id, !isClosed);
StringFormatter::send(broadcastBufferWriter,F("PTA%c%d\n"), isClosed?'2':'4', id); SHOVE(COMMAND_TYPE)
#ifdef CD_HANDLE_RING
FLUSH
StringFormatter::send(BUFFER,F("PTA%c%d\n"), isClosed?'2':'4', id);
SHOVE(WITHROTTLE_TYPE)
#endif #endif
broadcast(true);
} }
void CommandDistributor::broadcastLoco(byte slot) { void CommandDistributor::broadcastLoco(byte slot) {
DCC::LOCO * sp=&DCC::speedTable[slot]; DCC::LOCO * sp=&DCC::speedTable[slot];
StringFormatter::send(broadcastBufferWriter,F("<l %d %d %d %l>\n"), FLUSH
StringFormatter::send(BUFFER,F("<l %d %d %d %l>\n"),
sp->loco,slot,sp->speedCode,sp->functions); sp->loco,slot,sp->speedCode,sp->functions);
broadcast(false); SHOVE(COMMAND_TYPE)
#if defined(WIFI_ON) | defined(ETHERNET_ON) #ifdef CD_HANDLE_RING
WiThrottle::markForBroadcast(sp->loco); WiThrottle::markForBroadcast(sp->loco);
#endif #endif
} }
@ -130,14 +140,24 @@ void CommandDistributor::broadcastPower() {
else if (main) reason=F(" MAIN"); else if (main) reason=F(" MAIN");
else if (prog) reason=F(" PROG"); else if (prog) reason=F(" PROG");
else state='0'; else state='0';
FLUSH
StringFormatter::send(broadcastBufferWriter, StringFormatter::send(BUFFER,F("<p %c%S>\n"),state,reason);
F("<p%c%S>\nPPA%c\n"),state,reason, main?'1':'0'); SHOVE(COMMAND_TYPE)
#ifdef CD_HANDLE_RING
FLUSH
StringFormatter::send(BUFFER,F("PPA%c\n"), main?'1':'0');
SHOVE(WITHROTTLE_TYPE)
#endif
LCD(2,F("Power %S%S"),state=='1'?F("On"):F("Off"),reason); LCD(2,F("Power %S%S"),state=='1'?F("On"):F("Off"),reason);
broadcast(true);
} }
void CommandDistributor::broadcastText(const FSH * msg) { void CommandDistributor::broadcastText(const FSH * msg) {
StringFormatter::send(broadcastBufferWriter,F("%S"),msg); FLUSH
broadcast(false); StringFormatter::send(BUFFER,F("%S"),msg);
SHOVE(COMMAND_TYPE)
#ifdef CD_HANDLE_RING
FLUSH
StringFormatter::send(BUFFER,F("Hm%S\n"), msg);
SHOVE(WITHROTTLE_TYPE)
#endif
} }

View File

@ -23,6 +23,13 @@
#define CommandDistributor_h #define CommandDistributor_h
#include "DCCEXParser.h" #include "DCCEXParser.h"
#include "RingStream.h" #include "RingStream.h"
#include "StringBuffer.h"
#include "defines.h"
#if WIFI_ON | ETHERNET_ON
// Command Distributor must handle a RingStream of clients
#define CD_HANDLE_RING
#endif
class CommandDistributor { class CommandDistributor {
@ -35,14 +42,15 @@ public :
static void broadcastText(const FSH * msg); static void broadcastText(const FSH * msg);
static void forget(byte clientId); static void forget(byte clientId);
private: private:
static void broadcast(bool includeWithrottleClients);
static RingStream * ring;
static RingStream * broadcastBufferWriter;
static byte ringClient;
// each bit in broadcastlist = 1<<clientid
enum clientType: byte {NONE_TYPE,COMMAND_TYPE,WITHROTTLE_TYPE}; enum clientType: byte {NONE_TYPE,COMMAND_TYPE,WITHROTTLE_TYPE};
static void broadcastToClients(clientType type);
static StringBuffer * broadcastBufferWriter;
#ifdef CD_HANDLE_RING
static RingStream * ring;
static const byte NO_CLIENT=255;
static byte ringClient;
static clientType clients[8]; static clientType clients[8];
#endif
}; };
#endif #endif

View File

@ -21,6 +21,7 @@
#include "SerialManager.h" #include "SerialManager.h"
#include "DCCEXParser.h" #include "DCCEXParser.h"
SerialManager * SerialManager::first=NULL; SerialManager * SerialManager::first=NULL;
SerialManager::SerialManager(Stream * myserial) { SerialManager::SerialManager(Stream * myserial) {
@ -49,11 +50,11 @@ void SerialManager::init() {
#endif #endif
} }
void SerialManager::broadcast(RingStream * ring) { void SerialManager::broadcast(char * stringBuffer) {
for (SerialManager * s=first;s;s=s->next) s->broadcast2(ring); for (SerialManager * s=first;s;s=s->next) s->broadcast2(stringBuffer);
} }
void SerialManager::broadcast2(RingStream * ring) { void SerialManager::broadcast2(char * stringBuffer) {
ring->printBuffer(serial); serial->print(stringBuffer);
} }
void SerialManager::loop() { void SerialManager::loop() {

View File

@ -23,7 +23,7 @@
#include "Arduino.h" #include "Arduino.h"
#include "defines.h" #include "defines.h"
#include "RingStream.h"
#ifndef COMMAND_BUFFER_SIZE #ifndef COMMAND_BUFFER_SIZE
#define COMMAND_BUFFER_SIZE 100 #define COMMAND_BUFFER_SIZE 100
@ -33,13 +33,13 @@ class SerialManager {
public: public:
static void init(); static void init();
static void loop(); static void loop();
static void broadcast(RingStream * ring); static void broadcast(char * stringBuffer);
private: private:
static SerialManager * first; static SerialManager * first;
SerialManager(Stream * myserial); SerialManager(Stream * myserial);
void loop2(); void loop2();
void broadcast2(RingStream * ring); void broadcast2(char * stringBuffer);
Stream * serial; Stream * serial;
SerialManager * next; SerialManager * next;
byte bufferLength; byte bufferLength;

45
StringBuffer.cpp Normal file
View File

@ -0,0 +1,45 @@
/*
* © 2022 Chris Harlow
* 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 "StringBuffer.h"
#include "DIAG.h"
StringBuffer::StringBuffer() {
flush();
};
char * StringBuffer::getString() {
return _buffer;
}
void StringBuffer::flush() {
_pos_write=0;
_buffer[0]='\0';
}
size_t StringBuffer::write(uint8_t b) {
if (_pos_write>=buffer_max) return 0;
_buffer[_pos_write] = b;
++_pos_write;
_buffer[_pos_write]='\0';
return 1;
}

38
StringBuffer.h Normal file
View File

@ -0,0 +1,38 @@
/*
* © 2022 Chris Harlow
* All rights reserved.
*
* This file is part of DCC++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 StringBuffer_h
#define StringBuffer_h
#include <Arduino.h>
class StringBuffer : public Print {
public:
StringBuffer();
// Override Print default
virtual size_t write(uint8_t b);
void flush();
char * getString();
private:
static const int buffer_max=64; // enough for long text msgs to throttles
int16_t _pos_write;
char _buffer[buffer_max+1];
};
#endif

View File

@ -6,6 +6,7 @@
#define VERSION "4.0.3" #define VERSION "4.0.3"
// 4.0.3 Track Manager additions: // 4.0.3 Track Manager additions:
// Broadcast improvements to separate <> and Withrottle responses
// Float eliminated saving >1.5kb PROGMEM and speed. // Float eliminated saving >1.5kb PROGMEM and speed.
// SET_TRACK(track,mode) Functions (A-H, MAIN|PROG|DC|DCX|OFF) // SET_TRACK(track,mode) Functions (A-H, MAIN|PROG|DC|DCX|OFF)
// New DC track function and DCX reverse polarity function // New DC track function and DCX reverse polarity function