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

Restructure to support multiple input streams

Preparation for Wifi without adding any Wifi specifics.
This commit is contained in:
Asbelos 2020-06-10 17:31:26 +01:00
parent 4d80152ad0
commit aebc35b183
10 changed files with 179 additions and 168 deletions

View File

@ -17,6 +17,9 @@ void myCallback(int result) {
DIAG(F("\n getting Loco Id callback result=%d"),result);
}
DCCEXParser serialParser(Serial);;
DCCEXParser wifiParser(Serial1);
void setup() {
Serial.begin(115200);
DCC::begin();
@ -31,5 +34,6 @@ void loop() {
DCC::loop(); // required to keep locos running and check powwer
// This line passes input on Serial to the DCCEXParser
StringParser::loop(Serial, DCCEXParser::parse);
serialParser.loop();
wifiParser.loop();
}

View File

@ -1,4 +1,4 @@
#include "StringParser.h"
#include "StringFormatter.h"
#include "DCCEXParser.h"
#include "DCC.h"
#include "DCCWaveform.h"
@ -10,19 +10,92 @@
const char VERSION[]="99.666";
// This is a JMRI command parser
int DCCEXParser::stashP[MAX_PARAMS];
bool DCCEXParser::stashBusy;
Stream & DCCEXParser::stashStream=Serial; // keep compiler happy but ovevride in constructor
DCCEXParser::DCCEXParser(Stream & myStream) {
stream=myStream;
}
// This is a JMRI command parser, one instance per incoming stream
// It doesnt know how the string got here, nor how it gets back.
// It knows nothing about hardware or tracks... it just parses strings and
// calls the corresponding DCC api.
// Non-DCC things like turnouts, pins and sensors are handled in additional JMRI interface classes.
void DCCEXParser::loop() {
while(stream.available()) {
if (bufferLength==MAX_BUFFER) {
bufferLength=0;
inCommandPayload=false;
}
char ch = stream.read();
if (ch == '<') {
inCommandPayload = true;
bufferLength=0;
buffer[0]='\0';
}
else if (ch == '>') {
buffer[bufferLength]='\0';
parse(buffer);
inCommandPayload = false;
break;
} else if(inCommandPayload) {
buffer[bufferLength++]= ch;
}
}
}
int DCCEXParser::splitValues( int result[MAX_PARAMS]) {
byte state=1;
byte parameterCount=0;
int runningValue=0;
const char * remainingCmd=buffer+1; // skips the opcode
bool signNegative=false;
// clear all parameters in case not enough found
for (int i=0;i<MAX_PARAMS;i++) result[i]=0;
while(parameterCount<MAX_PARAMS) {
char hot=*remainingCmd;
switch (state) {
case 1: // skipping spaces before a param
if (hot==' ') break;
if (hot == '\0' || hot=='>') return parameterCount;
state=2;
continue;
case 2: // checking sign
signNegative=false;
runningValue=0;
state=3;
if (hot!='-') continue;
signNegative=true;
break;
case 3: // building a parameter
if (hot>='0' && hot<='9') {
runningValue=10*runningValue+(hot-'0');
break;
}
result[parameterCount] = runningValue * (signNegative ?-1:1);
parameterCount++;
state=1;
continue;
}
remainingCmd++;
}
return parameterCount;
}
// See documentation on DCC class for info on this section
void DCCEXParser::parse(Stream & stream,const char *com) {
void DCCEXParser::parse(const char *com) {
(void) EEPROM; // tell compiler not to warn thi is unused
int p[MAX_PARAMS];
int params=StringParser::parse(com+1,p,MAX_PARAMS);
int params=splitValues(p);
// Functions return from this switch if complete, break from switch implies error <X> to send
@ -30,7 +103,7 @@ void DCCEXParser::parse(Stream & stream,const char *com) {
case 't': // THROTTLE <t REGISTER CAB SPEED DIRECTION>
DCC::setThrottle(p[1],p[2],p[3]);
StringParser::send(stream,F("<T %d %d %d>"), p[0], p[2],p[3]);
StringFormatter::send(stream,F("<T %d %d %d>"), p[0], p[2],p[3]);
return;
case 'f': // FUNCTION <f CAB BYTE1 [BYTE2]>
@ -44,15 +117,15 @@ void DCCEXParser::parse(Stream & stream,const char *com) {
return;
case 'T': // TURNOUT <T ...>
if (parseT(stream,params,p)) return;
if (parseT(params,p)) return;
break;
case 'Z': // OUTPUT <Z ...>
if (parseZ(stream,params,p)) return;
if (parseZ(params,p)) return;
break;
case 'S': // SENSOR <S ...>
if (parseS(stream,params,p)) return;
if (parseS(params,p)) return;
break;
case 'w': // WRITE CV on MAIN <w CAB CV VALUE>
@ -81,17 +154,17 @@ void DCCEXParser::parse(Stream & stream,const char *com) {
case '1': // POWERON <1>
DCCWaveform::mainTrack.setPowerMode(POWERMODE::ON);
DCCWaveform::progTrack.setPowerMode(POWERMODE::ON);
StringParser::send(stream,F("<p1>"));
StringFormatter::send(stream,F("<p1>"));
return;
case '0': // POWEROFF <0>
DCCWaveform::mainTrack.setPowerMode(POWERMODE::OFF);
DCCWaveform::progTrack.setPowerMode(POWERMODE::OFF);
StringParser::send(stream,F("<p0>"));
StringFormatter::send(stream,F("<p0>"));
return;
case 'c': // READ CURRENT <c>
StringParser::send(stream,F("<a %d>"), DCCWaveform::mainTrack.getLastCurrent());
StringFormatter::send(stream,F("<a %d>"), DCCWaveform::mainTrack.getLastCurrent());
return;
case 'Q': // SENSORS <Q>
@ -99,7 +172,7 @@ void DCCEXParser::parse(Stream & stream,const char *com) {
break;
case 's': // <s>
StringParser::send(stream,F("<iDCC-Asbelos BASE STATION FOR ARDUINO / %s: V-%s %s/%s\n>"), BOARD_NAME, VERSION, __DATE__, __TIME__ );
StringFormatter::send(stream,F("<iDCC-Asbelos BASE STATION FOR ARDUINO / %s: V-%s %s/%s\n>"), BOARD_NAME, VERSION, __DATE__, __TIME__ );
// TODO send power status
// TODO Send stats of speed reminders table
// TODO send status of turnouts etc etc
@ -107,16 +180,16 @@ void DCCEXParser::parse(Stream & stream,const char *com) {
case 'E': // STORE EPROM <E>
EEStore::store();
StringParser::send(stream,F("<e %d %d %d>"), EEStore::eeStore->data.nTurnouts, EEStore::eeStore->data.nSensors, EEStore::eeStore->data.nOutputs);
StringFormatter::send(stream,F("<e %d %d %d>"), EEStore::eeStore->data.nTurnouts, EEStore::eeStore->data.nSensors, EEStore::eeStore->data.nOutputs);
return;
case 'e': // CLEAR EPROM <e>
EEStore::clear();
StringParser::send(stream, F("<O>"));
StringFormatter::send(stream, F("<O>"));
return;
case ' ': // < >
StringParser::send(stream,F("\n"));
StringFormatter::send(stream,F("\n"));
return;
default: //anything else will drop out to <X>
@ -125,10 +198,10 @@ void DCCEXParser::parse(Stream & stream,const char *com) {
} // end of opcode switch
// Any fallout here sends an <X>
StringParser::send(stream, F("<X>"));
StringFormatter::send(stream, F("<X>"));
}
bool DCCEXParser::parseZ(Stream & stream, int params, int p[]){
bool DCCEXParser::parseZ( int params, int p[]){
switch (params) {
@ -138,7 +211,7 @@ bool DCCEXParser::parseZ(Stream & stream, int params, int p[]){
Output * o=Output::get(p[0]);
if(o==NULL) return false;
o->activate(p[1]);
StringParser::send(stream,F("<Y %d %d>"), p[0],p[1]);
StringFormatter::send(stream,F("<Y %d %d>"), p[0],p[1]);
}
return true;
@ -159,14 +232,14 @@ bool DCCEXParser::parseZ(Stream & stream, int params, int p[]){
//===================================
bool DCCEXParser::parseT(Stream & stream, int params, int p[]) {
bool DCCEXParser::parseT( int params, int p[]) {
switch(params){
case 0: // <T>
return (Turnout::showAll(stream)); break;
case 1: // <T id>
if (!Turnout::remove(p[0])) return false;
StringParser::send(stream,F("<O>"));
StringFormatter::send(stream,F("<O>"));
return true;
case 2: // <T id 0|1>
@ -176,7 +249,7 @@ bool DCCEXParser::parseT(Stream & stream, int params, int p[]) {
case 3: // <T id addr subaddr>
if (!Turnout::create(p[0],p[1],p[2])) return false;
StringParser::send(stream,F("<O>"));
StringFormatter::send(stream,F("<O>"));
return true;
default:
@ -184,7 +257,7 @@ bool DCCEXParser::parseT(Stream & stream, int params, int p[]) {
}
}
bool DCCEXParser::parseS(Stream & stream, int params, int p[]) {
bool DCCEXParser::parseS( int params, int p[]) {
switch(params){
@ -205,11 +278,9 @@ bool DCCEXParser::parseS(Stream & stream, int params, int p[]) {
}
return false;
}
int DCCEXParser::stashP[MAX_PARAMS];
bool DCCEXParser::stashBusy=false;
Stream & DCCEXParser::stashStream=Serial; // only to keep compiler happy
// CALLBACKS must be static
bool DCCEXParser::stashCallback(Stream & stream, int p[MAX_PARAMS]) {
if (stashBusy) return false;
stashBusy=true;
@ -218,16 +289,16 @@ bool DCCEXParser::stashCallback(Stream & stream, int p[MAX_PARAMS]) {
return true;
}
void DCCEXParser::callback_W(int result) {
StringParser::send(stashStream,F("<r%d|%d|%d %d>"), stashP[2], stashP[3],stashP[0],result==1?stashP[1]:-1);
StringFormatter::send(stashStream,F("<r%d|%d|%d %d>"), stashP[2], stashP[3],stashP[0],result==1?stashP[1]:-1);
stashBusy=false;
}
void DCCEXParser::callback_B(int result) {
StringParser::send(stashStream,F("<r%d|%d|%d %d %d>"), stashP[3],stashP[4], stashP[0],stashP[1],result==1?stashP[2]:-1);
StringFormatter::send(stashStream,F("<r%d|%d|%d %d %d>"), stashP[3],stashP[4], stashP[0],stashP[1],result==1?stashP[2]:-1);
stashBusy=false;
}
void DCCEXParser::callback_R(int result) {
StringParser::send(stashStream,F("<r%d|%d|%d %d>"),stashP[1],stashP[2],stashP[0],result);
StringFormatter::send(stashStream,F("<r%d|%d|%d %d>"),stashP[1],stashP[2],stashP[0],result);
stashBusy=false;
}

View File

@ -2,19 +2,28 @@
#define DCCEXParser_h
struct DCCEXParser
{
static void parse(Stream & stream,const char * command);
DCCEXParser(Stream & myStream);
void loop();
private:
static const int MAX_PARAMS=10; // longest command sent in
static const int MAX_BUFFER=50; // longest command sent in
static bool parseT(Stream & stream, int params, int p[]);
static bool parseZ(Stream & stream, int params, int p[]);
static bool parseS(Stream & stream, int params, int p[]);
Stream & stream;
byte bufferLength=0;
bool inCommandPayload=false;
char buffer[MAX_BUFFER];
void parse(const char * command);
int splitValues( int result[MAX_PARAMS]);
bool parseT(int params, int p[]);
bool parseZ(int params, int p[]);
bool parseS( int params, int p[]);
static int stashP[MAX_PARAMS];
static bool stashBusy;
static Stream & stashStream;
static int stashP[MAX_PARAMS];
static bool stashCallback(Stream & stream, int p[MAX_PARAMS]);
static void callback_W(int result);
static void callback_B(int result);

4
DIAG.h
View File

@ -1,9 +1,9 @@
#ifndef DIAG_h
#define DIAG_h
#include "StringParser.h"
#include "StringFormatter.h"
#ifndef DIAG_ENABLED
#define DIAG_ENABLED true
#endif
#define DIAG if (DIAG_ENABLED) StringParser::print
#define DIAG if (DIAG_ENABLED) StringFormatter::print
#endif

View File

@ -65,7 +65,7 @@ the state of any outputs being monitored or controlled by a separate interface o
#include "Outputs.h"
#include "EEStore.h"
#include "StringParser.h"
#include "StringFormatter.h"
void Output::activate(int s){
data.oStatus=(s>0); // if s>0, set status to active, else inactive
digitalWrite(data.pin,data.oStatus ^ bitRead(data.iFlag,0)); // set state of output pin to HIGH or LOW depending on whether bit zero of iFlag is set to 0 (ACTIVE=HIGH) or 1 (ACTIVE=LOW)
@ -106,14 +106,14 @@ bool Output::showAll(Stream & stream){
bool gotone=false;
for(Output * tt=firstOutput;tt!=NULL;tt=tt->nextOutput){
gotone=true;
StringParser::send(stream,F("<Y %d %d %d %d>"), tt->data.id, tt->data.pin, tt->data.iFlag, tt->data.oStatus);
StringFormatter::send(stream,F("<Y %d %d %d %d>"), tt->data.id, tt->data.pin, tt->data.iFlag, tt->data.oStatus);
}
return gotone;
}
void Output::show(Stream & stream){
for(Output * tt=firstOutput;tt!=NULL;tt=tt->nextOutput){
StringParser::send(stream,F("<Y %d %d>"), tt->data.id, tt->data.oStatus);
StringFormatter::send(stream,F("<Y %d %d>"), tt->data.id, tt->data.oStatus);
}
}

View File

@ -50,7 +50,7 @@ decide to ignore the <q ID> return and only react to <Q ID> triggers.
#include "Sensors.h"
#include "EEStore.h"
#include "StringParser.h"
#include "StringFormatter.h"
///////////////////////////////////////////////////////////////////////////////
@ -62,10 +62,10 @@ void Sensor::check(Stream & stream){
if(!tt->active && tt->signal<0.5){
tt->active=true;
StringParser::send(stream,F("<Q %d>"), tt->data.snum);
StringFormatter::send(stream,F("<Q %d>"), tt->data.snum);
} else if(tt->active && tt->signal>0.9){
tt->active=false;
StringParser::send(stream,F("<q %d>"), tt->data.snum);
StringFormatter::send(stream,F("<q %d>"), tt->data.snum);
}
} // loop over all sensors
@ -131,7 +131,7 @@ bool Sensor::remove(int n){
void Sensor::show(Stream & stream){
for(Sensor * tt=firstSensor;tt!=NULL;tt=tt->nextSensor){
StringParser::send(stream, F("<Q %d %d %d>"), tt->data.snum, tt->data.pin, tt->data.pullUp);
StringFormatter::send(stream, F("<Q %d %d %d>"), tt->data.snum, tt->data.pin, tt->data.pullUp);
}
}
@ -139,7 +139,7 @@ void Sensor::show(Stream & stream){
void Sensor::status(Stream & stream){
for(Sensor * tt=firstSensor;tt!=NULL;tt=tt->nextSensor){
StringParser::send(stream,F("<%s %d>"), tt->active?"Q":"q", tt->data.snum);
StringFormatter::send(stream,F("<%s %d>"), tt->active?"Q":"q", tt->data.snum);
}
}

40
StringFormatter.cpp Normal file
View File

@ -0,0 +1,40 @@
#include "StringFormatter.h"
#include <stdarg.h>
void StringFormatter::print( const __FlashStringHelper* input...) {
va_list args;
va_start(args, input);
send(Serial,input,args);
}
void StringFormatter::send(Stream & stream, const __FlashStringHelper* input...) {
va_list args;
va_start(args, input);
send(stream,input,args);
}
void StringFormatter::send(Stream & stream,const __FlashStringHelper* format, va_list args) {
// thanks to Jan Turoň https://arduino.stackexchange.com/questions/56517/formatting-strings-in-arduino-for-output
char* flash=(char*)format;
for(int i=0; ; ++i) {
char c=pgm_read_byte_near(flash+i);
if (c=='\0') return;
if(c!='%') { stream.print(c); continue; }
i++;
c=pgm_read_byte_near(flash+i);
switch(c) {
case '%': stream.write('%'); break;
case 's': stream.print(va_arg(args, char*)); break;
case 'd': stream.print(va_arg(args, int), DEC); break;
case 'b': stream.print(va_arg(args, int), BIN); break;
case 'o': stream.print(va_arg(args, int), OCT); break;
case 'x': stream.print(va_arg(args, int), HEX); break;
case 'c': stream.write(va_arg(args, int)); break;
case 'f': stream.print(va_arg(args, double), 2); break;
}
}
va_end(args);
}

View File

@ -1,19 +1,15 @@
#ifndef StringParser_h
#define StringParser_h
#ifndef StringFormatter_h
#define StringFormatter_h
#include <Arduino.h>
class StringParser
class StringFormatter
{
public:
static void loop(Stream & stream, void (*callback)(Stream & stream, const char * data) );
static int parse(const char * com, int result[], byte maxResults);
static void print( const __FlashStringHelper* input...);
static void send(Stream & serial, const __FlashStringHelper* input...);
private:
static void send(Stream & serial, const __FlashStringHelper* input,va_list args);
static byte bufferLength;
static bool inCommandPayload;
static const byte MAX_BUFFER=100;
static char buffer[MAX_BUFFER];
};
#endif

View File

@ -1,109 +0,0 @@
#include "StringParser.h"
#include <stdarg.h>
byte StringParser::bufferLength=0;
bool StringParser::inCommandPayload=false;
char StringParser::buffer[MAX_BUFFER];
void StringParser::loop(Stream & stream, void (*callback)(Stream & stream, const char * data) ) {
while(stream.available()) {
if (bufferLength==MAX_BUFFER) {
bufferLength=0;
inCommandPayload=false;
}
char ch = stream.read();
if (ch == '<') {
inCommandPayload = true;
bufferLength=0;
buffer[0]='\0';
}
else if (ch == '>') {
buffer[bufferLength]='\0';
(*callback)(Serial, buffer);
inCommandPayload = false;
} else if(inCommandPayload) {
buffer[bufferLength++]= ch;
}
}
}
int StringParser::parse(const char * com, int result[], byte maxResults) {
byte state=1;
byte parameterCount=0;
int runningValue=0;
const char * remainingCmd=com; // skips the opcode
bool signNegative=false;
// clear all parameters in case not enough found
for (int i=0;i<maxResults;i++) result[i]=0;
while(parameterCount<maxResults) {
char hot=*remainingCmd;
switch (state) {
case 1: // skipping spaces before a param
if (hot==' ') break;
if (hot == '\0' || hot=='>') return parameterCount;
state=2;
continue;
case 2: // checking sign
signNegative=false;
runningValue=0;
state=3;
if (hot!='-') continue;
signNegative=true;
break;
case 3: // building a parameter
if (hot>='0' && hot<='9') {
runningValue=10*runningValue+(hot-'0');
break;
}
result[parameterCount] = runningValue * (signNegative ?-1:1);
parameterCount++;
state=1;
continue;
}
remainingCmd++;
}
return parameterCount;
}
void StringParser::print( const __FlashStringHelper* input...) {
va_list args;
va_start(args, input);
send(Serial,input,args);
}
void StringParser::send(Stream & stream, const __FlashStringHelper* input...) {
va_list args;
va_start(args, input);
send(stream,input,args);
}
void StringParser::send(Stream & stream,const __FlashStringHelper* format, va_list args) {
// thanks to Jan Turoň https://arduino.stackexchange.com/questions/56517/formatting-strings-in-arduino-for-output
char* flash=(char*)format;
for(int i=0; ; ++i) {
char c=pgm_read_byte_near(flash+i);
if (c=='\0') return;
if(c!='%') { stream.print(c); continue; }
i++;
c=pgm_read_byte_near(flash+i);
switch(c) {
case '%': stream.print('%'); break;
case 's': stream.print(va_arg(args, char*)); break;
case 'd': stream.print(va_arg(args, int), DEC); break;
case 'b': stream.print(va_arg(args, int), BIN); break;
case 'o': stream.print(va_arg(args, int), OCT); break;
case 'x': stream.print(va_arg(args, int), HEX); break;
case 'f': stream.print(va_arg(args, double), 2); break;
}
}
va_end(args);
}

View File

@ -1,6 +1,6 @@
#include "Turnouts.h"
#include "EEStore.h"
#include "StringParser.h"
#include "StringFormatter.h"
bool Turnout::activate(int n,bool state){
Turnout * tt=get(n);
@ -43,7 +43,7 @@ bool Turnout::remove(int n){
void Turnout::show(Stream & stream, int n){
for(Turnout *tt=firstTurnout;tt!=NULL;tt=tt->nextTurnout){
if (tt->data.id==n) {
StringParser::send(stream,F("<H %d %d>"), tt->data.id, tt->data.tStatus);
StringFormatter::send(stream,F("<H %d %d>"), tt->data.id, tt->data.tStatus);
return;
}
}
@ -52,7 +52,7 @@ void Turnout::show(Stream & stream, int n){
bool Turnout::showAll(Stream & stream){
bool gotOne=false;
for(Turnout * tt=firstTurnout;tt!=NULL;tt=tt->nextTurnout){
StringParser::send(stream,F("<H %d %d %d %d>"), tt->data.id, tt->data.address, tt->data.subAddress, tt->data.tStatus);
StringFormatter::send(stream,F("<H %d %d %d %d>"), tt->data.id, tt->data.address, tt->data.subAddress, tt->data.tStatus);
gotOne=true;
}
return gotOne;