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:
parent
2b2012ef1d
commit
16fafccf15
|
@ -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;
|
|
||||||
|
|
||||||
RingStream * CommandDistributor::ring=0;
|
#ifdef BIG_RAM
|
||||||
byte CommandDistributor::ringClient=NO_CLIENT;
|
// use a buffer to allow broadcast
|
||||||
CommandDistributor::clientType CommandDistributor::clients[8]={
|
#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};
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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
45
StringBuffer.cpp
Normal 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
38
StringBuffer.h
Normal 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
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user