diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index 85df03b..9c3dba4 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -30,16 +30,29 @@ #include "DCC.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; -RingStream * CommandDistributor::ring=0; -byte CommandDistributor::ringClient=NO_CLIENT; -CommandDistributor::clientType CommandDistributor::clients[8]={ - NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE}; -RingStream * CommandDistributor::broadcastBufferWriter=new RingStream(100); +#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; + byte CommandDistributor::ringClient=NO_CLIENT; + CommandDistributor::clientType CommandDistributor::clients[8]={ + NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE}; + +// 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) { ring=stream; ringClient=stream->peekTargetMark(); @@ -56,15 +69,15 @@ void CommandDistributor::parse(byte clientId,byte * buffer, RingStream * stream void CommandDistributor::forget(byte clientId) { clients[clientId]=NONE_TYPE; } +#endif - -void CommandDistributor::broadcast(bool includeWithrottleClients) { - broadcastBufferWriter->write((byte)'\0'); +// This will not be called on a uno +void CommandDistributor::broadcastToClients(clientType type) { /* 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 // before merging broadcasts in the ring, then reinstate it in case // the process continues to output to its client. @@ -72,49 +85,46 @@ void CommandDistributor::broadcast(bool includeWithrottleClients) { /* loop through ring clients */ for (byte clientId=0; clientIdmark(clientId); - broadcastBufferWriter->printBuffer(ring); - ring->commit(); + if (clients[clientId]==type) { + ring->mark(clientId); + ring->print(broadcastBufferWriter->getString()); + ring->commit(); + } } if (ringClient!=NO_CLIENT) ring->mark(ringClient); #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 ) { - StringFormatter::send(broadcastBufferWriter,F("<%c %d>\n"), on?'Q':'q', id); - broadcast(false); + FLUSH + StringFormatter::send(BUFFER,F("<%c %d>\n"), on?'Q':'q', id); + SHOVE(COMMAND_TYPE) } void CommandDistributor::broadcastTurnout(int16_t id, bool isClosed ) { // 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 // be safe for both types. - StringFormatter::send(broadcastBufferWriter,F("\n"),id, !isClosed); -#if defined(WIFI_ON) | defined(ETHERNET_ON) - StringFormatter::send(broadcastBufferWriter,F("PTA%c%d\n"), isClosed?'2':'4', id); + FLUSH + StringFormatter::send(BUFFER,F("\n"),id, !isClosed); + SHOVE(COMMAND_TYPE) + +#ifdef CD_HANDLE_RING + FLUSH + StringFormatter::send(BUFFER,F("PTA%c%d\n"), isClosed?'2':'4', id); + SHOVE(WITHROTTLE_TYPE) #endif - broadcast(true); } void CommandDistributor::broadcastLoco(byte slot) { DCC::LOCO * sp=&DCC::speedTable[slot]; - StringFormatter::send(broadcastBufferWriter,F("\n"), + FLUSH + StringFormatter::send(BUFFER,F("\n"), sp->loco,slot,sp->speedCode,sp->functions); - broadcast(false); -#if defined(WIFI_ON) | defined(ETHERNET_ON) + SHOVE(COMMAND_TYPE) +#ifdef CD_HANDLE_RING WiThrottle::markForBroadcast(sp->loco); #endif } @@ -130,14 +140,24 @@ void CommandDistributor::broadcastPower() { else if (main) reason=F(" MAIN"); else if (prog) reason=F(" PROG"); else state='0'; - - StringFormatter::send(broadcastBufferWriter, - F("\nPPA%c\n"),state,reason, main?'1':'0'); - LCD(2,F("Power %S%S"),state=='1'?F("On"):F("Off"),reason); - broadcast(true); + FLUSH + StringFormatter::send(BUFFER,F("

\n"),state,reason); + 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); } void CommandDistributor::broadcastText(const FSH * msg) { - StringFormatter::send(broadcastBufferWriter,F("%S"),msg); - broadcast(false); + FLUSH + 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 } diff --git a/CommandDistributor.h b/CommandDistributor.h index a9af3bb..7da3c8f 100644 --- a/CommandDistributor.h +++ b/CommandDistributor.h @@ -23,6 +23,13 @@ #define CommandDistributor_h #include "DCCEXParser.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 { @@ -35,14 +42,15 @@ public : static void broadcastText(const FSH * msg); static void forget(byte clientId); private: - static void broadcast(bool includeWithrottleClients); - static RingStream * ring; - static RingStream * broadcastBufferWriter; - static byte ringClient; - - // each bit in broadcastlist = 1<next) s->broadcast2(ring); +void SerialManager::broadcast(char * stringBuffer) { + for (SerialManager * s=first;s;s=s->next) s->broadcast2(stringBuffer); } -void SerialManager::broadcast2(RingStream * ring) { - ring->printBuffer(serial); +void SerialManager::broadcast2(char * stringBuffer) { + serial->print(stringBuffer); } void SerialManager::loop() { diff --git a/SerialManager.h b/SerialManager.h index fb58cba..ad1eaf3 100644 --- a/SerialManager.h +++ b/SerialManager.h @@ -23,7 +23,7 @@ #include "Arduino.h" #include "defines.h" -#include "RingStream.h" + #ifndef COMMAND_BUFFER_SIZE #define COMMAND_BUFFER_SIZE 100 @@ -33,13 +33,13 @@ class SerialManager { public: static void init(); static void loop(); - static void broadcast(RingStream * ring); + static void broadcast(char * stringBuffer); private: static SerialManager * first; SerialManager(Stream * myserial); void loop2(); - void broadcast2(RingStream * ring); + void broadcast2(char * stringBuffer); Stream * serial; SerialManager * next; byte bufferLength; diff --git a/StringBuffer.cpp b/StringBuffer.cpp new file mode 100644 index 0000000..dd4e477 --- /dev/null +++ b/StringBuffer.cpp @@ -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 . + */ + +#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; +} + + diff --git a/StringBuffer.h b/StringBuffer.h new file mode 100644 index 0000000..fe227fb --- /dev/null +++ b/StringBuffer.h @@ -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 . + */ + +#ifndef StringBuffer_h +#define StringBuffer_h +#include + +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 \ No newline at end of file diff --git a/version.h b/version.h index 5ab7e44..98b4317 100644 --- a/version.h +++ b/version.h @@ -6,6 +6,7 @@ #define VERSION "4.0.3" // 4.0.3 Track Manager additions: +// Broadcast improvements to separate <> and Withrottle responses // Float eliminated saving >1.5kb PROGMEM and speed. // SET_TRACK(track,mode) Functions (A-H, MAIN|PROG|DC|DCX|OFF) // New DC track function and DCX reverse polarity function