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

AYSNC prog cmds from Wifi/Ethernet

prog track commands from wifi/ethernet will no longer block loop while waiting
for ACK
This commit is contained in:
Asbelos 2021-03-08 10:40:32 +00:00
parent df4bae365d
commit 36e38bf861
7 changed files with 114 additions and 102 deletions

View File

@ -25,7 +25,7 @@ DCCEXParser * CommandDistributor::parser=0;
void CommandDistributor::parse(byte clientId,byte * buffer, RingStream * streamer) { void CommandDistributor::parse(byte clientId,byte * buffer, RingStream * streamer) {
if (buffer[0] == '<') { if (buffer[0] == '<') {
if (!parser) parser = new DCCEXParser(); if (!parser) parser = new DCCEXParser();
parser->parse(streamer, buffer, true); // tell JMRI parser that ACKS are blocking because we can't handle the async parser->parse(streamer, buffer, streamer);
} }
else WiThrottle::getThrottle(clientId)->parse(streamer, buffer); else WiThrottle::getThrottle(clientId)->parse(streamer, buffer);
} }

85
DCC.cpp
View File

@ -447,56 +447,47 @@ const ackOp FLASH LONG_LOCO_ID_PROG[] = {
FAIL FAIL
}; };
// On the following prog-track functions blocking defaults to false. void DCC::writeCVByte(int cv, byte byteValue, ACK_CALLBACK callback) {
// blocking=true forces the API to block, waiting for the response and invoke the callback BEFORE returning. ackManagerSetup(cv, byteValue, WRITE_BYTE_PROG, callback);
// During that wait, other parts of the system will be unresponsive.
// blocking =false means the callback will be called some time after the API returns (typically a few tenths of a second)
// but that would be very inconvenient in a Wifi situaltion where the stream becomes
// unuavailable immediately after the API rerturns.
void DCC::writeCVByte(int cv, byte byteValue, ACK_CALLBACK callback, bool blocking) {
ackManagerSetup(cv, byteValue, WRITE_BYTE_PROG, callback, blocking);
} }
void DCC::writeCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback) {
void DCC::writeCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback, bool blocking) {
if (bitNum >= 8) callback(-1); if (bitNum >= 8) callback(-1);
else ackManagerSetup(cv, bitNum, bitValue?WRITE_BIT1_PROG:WRITE_BIT0_PROG, callback, blocking); else ackManagerSetup(cv, bitNum, bitValue?WRITE_BIT1_PROG:WRITE_BIT0_PROG, callback);
} }
void DCC::verifyCVByte(int cv, byte byteValue, ACK_CALLBACK callback, bool blocking) { void DCC::verifyCVByte(int cv, byte byteValue, ACK_CALLBACK callback) {
ackManagerSetup(cv, byteValue, VERIFY_BYTE_PROG, callback, blocking); ackManagerSetup(cv, byteValue, VERIFY_BYTE_PROG, callback);
} }
void DCC::verifyCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback) {
void DCC::verifyCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback, bool blocking) {
if (bitNum >= 8) callback(-1); if (bitNum >= 8) callback(-1);
else ackManagerSetup(cv, bitNum, bitValue?VERIFY_BIT1_PROG:VERIFY_BIT0_PROG, callback, blocking); else ackManagerSetup(cv, bitNum, bitValue?VERIFY_BIT1_PROG:VERIFY_BIT0_PROG, callback);
} }
void DCC::readCVBit(int cv, byte bitNum, ACK_CALLBACK callback, bool blocking) { void DCC::readCVBit(int cv, byte bitNum, ACK_CALLBACK callback) {
if (bitNum >= 8) callback(-1); if (bitNum >= 8) callback(-1);
else ackManagerSetup(cv, bitNum,READ_BIT_PROG, callback, blocking); else ackManagerSetup(cv, bitNum,READ_BIT_PROG, callback);
} }
void DCC::readCV(int cv, ACK_CALLBACK callback, bool blocking) { void DCC::readCV(int cv, ACK_CALLBACK callback) {
ackManagerSetup(cv, 0,READ_CV_PROG, callback, blocking); ackManagerSetup(cv, 0,READ_CV_PROG, callback);
} }
void DCC::getLocoId(ACK_CALLBACK callback, bool blocking) { void DCC::getLocoId(ACK_CALLBACK callback) {
ackManagerSetup(0,0, LOCO_ID_PROG, callback, blocking); ackManagerSetup(0,0, LOCO_ID_PROG, callback);
} }
void DCC::setLocoId(int id,ACK_CALLBACK callback, bool blocking) { void DCC::setLocoId(int id,ACK_CALLBACK callback) {
if (id<1 || id>10239) { //0x27FF according to standard if (id<1 || id>10239) { //0x27FF according to standard
callback(-1); callback(-1);
return; return;
} }
if (id<=127) if (id<=127)
ackManagerSetup(id, SHORT_LOCO_ID_PROG, callback, blocking); ackManagerSetup(id, SHORT_LOCO_ID_PROG, callback);
else else
ackManagerSetup(id | 0xc000,LONG_LOCO_ID_PROG, callback, blocking); ackManagerSetup(id | 0xc000,LONG_LOCO_ID_PROG, callback);
} }
void DCC::forgetLoco(int cab) { // removes any speed reminders for this loco void DCC::forgetLoco(int cab) { // removes any speed reminders for this loco
@ -511,7 +502,7 @@ byte DCC::loopStatus=0;
void DCC::loop() { void DCC::loop() {
DCCWaveform::loop(ackManagerProg!=NULL); // power overload checks DCCWaveform::loop(ackManagerProg!=NULL); // power overload checks
ackManagerLoop(false); // maintain prog track ack manager ackManagerLoop(); // maintain prog track ack manager
issueReminders(); issueReminders();
} }
@ -638,42 +629,34 @@ bool DCC::ackReceived;
ACK_CALLBACK DCC::ackManagerCallback; ACK_CALLBACK DCC::ackManagerCallback;
void DCC::ackManagerSetup(int cv, byte byteValueOrBitnum, ackOp const program[], ACK_CALLBACK callback, bool blocking) { void DCC::ackManagerSetup(int cv, byte byteValueOrBitnum, ackOp const program[], ACK_CALLBACK callback) {
ackManagerCv = cv; ackManagerCv = cv;
ackManagerProg = program; ackManagerProg = program;
ackManagerByte = byteValueOrBitnum; ackManagerByte = byteValueOrBitnum;
ackManagerBitNum=byteValueOrBitnum; ackManagerBitNum=byteValueOrBitnum;
ackManagerCallback = callback; ackManagerCallback = callback;
if (blocking) ackManagerLoop(blocking);
} }
void DCC::ackManagerSetup(int wordval, ackOp const program[], ACK_CALLBACK callback, bool blocking) { void DCC::ackManagerSetup(int wordval, ackOp const program[], ACK_CALLBACK callback) {
ackManagerWord=wordval; ackManagerWord=wordval;
ackManagerProg = program; ackManagerProg = program;
ackManagerCallback = callback; ackManagerCallback = callback;
if (blocking) ackManagerLoop(blocking); }
}
const byte RESET_MIN=8; // tuning of reset counter before sending message const byte RESET_MIN=8; // tuning of reset counter before sending message
// checkRessets return true if the caller should yield back to loop and try later. // checkRessets return true if the caller should yield back to loop and try later.
bool DCC::checkResets(bool blocking, uint8_t numResets) { bool DCC::checkResets(uint8_t numResets) {
if (blocking) {
// must block waiting for restest to be issued
while(DCCWaveform::progTrack.sentResetsSincePacket < numResets);
return false; // caller need not yield
}
return DCCWaveform::progTrack.sentResetsSincePacket < numResets; return DCCWaveform::progTrack.sentResetsSincePacket < numResets;
} }
void DCC::ackManagerLoop(bool blocking) { void DCC::ackManagerLoop() {
while (ackManagerProg) { while (ackManagerProg) {
byte opcode=GETFLASH(ackManagerProg); byte opcode=GETFLASH(ackManagerProg);
// breaks from this switch will step to next prog entry // breaks from this switch will step to next prog entry
// returns from this switch will stay on same entry // returns from this switch will stay on same entry
// (typically waiting for a reset counter or ACK waiting, or when all finished.) // (typically waiting for a reset counter or ACK waiting, or when all finished.)
// if blocking then we must ONLY return AFTER callback issued
switch (opcode) { switch (opcode) {
case BASELINE: case BASELINE:
if (DCCWaveform::progTrack.getPowerMode() == POWERMODE::OFF) { if (DCCWaveform::progTrack.getPowerMode() == POWERMODE::OFF) {
@ -681,15 +664,15 @@ void DCC::ackManagerLoop(bool blocking) {
DCCWaveform::progTrack.setPowerMode(POWERMODE::ON); DCCWaveform::progTrack.setPowerMode(POWERMODE::ON);
DCCWaveform::progTrack.sentResetsSincePacket = 0; DCCWaveform::progTrack.sentResetsSincePacket = 0;
DCCWaveform::progTrack.autoPowerOff=true; DCCWaveform::progTrack.autoPowerOff=true;
if (!blocking) return; return;
} }
if (checkResets(blocking, DCCWaveform::progTrack.autoPowerOff ? 20 : 3)) return; if (checkResets(DCCWaveform::progTrack.autoPowerOff ? 20 : 3)) return;
DCCWaveform::progTrack.setAckBaseline(); DCCWaveform::progTrack.setAckBaseline();
break; break;
case W0: // write 0 bit case W0: // write 0 bit
case W1: // write 1 bit case W1: // write 1 bit
{ {
if (checkResets(blocking, RESET_MIN)) return; if (checkResets(RESET_MIN)) return;
if (Diag::ACK) DIAG(F("\nW%d cv=%d bit=%d"),opcode==W1, ackManagerCv,ackManagerBitNum); if (Diag::ACK) DIAG(F("\nW%d cv=%d bit=%d"),opcode==W1, ackManagerCv,ackManagerBitNum);
byte instruction = WRITE_BIT | (opcode==W1 ? BIT_ON : BIT_OFF) | ackManagerBitNum; byte instruction = WRITE_BIT | (opcode==W1 ? BIT_ON : BIT_OFF) | ackManagerBitNum;
byte message[] = {cv1(BIT_MANIPULATE, ackManagerCv), cv2(ackManagerCv), instruction }; byte message[] = {cv1(BIT_MANIPULATE, ackManagerCv), cv2(ackManagerCv), instruction };
@ -700,7 +683,7 @@ void DCC::ackManagerLoop(bool blocking) {
case WB: // write byte case WB: // write byte
{ {
if (checkResets(blocking, RESET_MIN)) return; if (checkResets( RESET_MIN)) return;
if (Diag::ACK) DIAG(F("\nWB cv=%d value=%d"),ackManagerCv,ackManagerByte); if (Diag::ACK) DIAG(F("\nWB cv=%d value=%d"),ackManagerCv,ackManagerByte);
byte message[] = {cv1(WRITE_BYTE, ackManagerCv), cv2(ackManagerCv), ackManagerByte}; byte message[] = {cv1(WRITE_BYTE, ackManagerCv), cv2(ackManagerCv), ackManagerByte};
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS); DCCWaveform::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS);
@ -710,7 +693,7 @@ void DCC::ackManagerLoop(bool blocking) {
case VB: // Issue validate Byte packet case VB: // Issue validate Byte packet
{ {
if (checkResets(blocking, RESET_MIN)) return; if (checkResets( RESET_MIN)) return;
if (Diag::ACK) DIAG(F("\nVB cv=%d value=%d"),ackManagerCv,ackManagerByte); if (Diag::ACK) DIAG(F("\nVB cv=%d value=%d"),ackManagerCv,ackManagerByte);
byte message[] = { cv1(VERIFY_BYTE, ackManagerCv), cv2(ackManagerCv), ackManagerByte}; byte message[] = { cv1(VERIFY_BYTE, ackManagerCv), cv2(ackManagerCv), ackManagerByte};
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS); DCCWaveform::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS);
@ -721,7 +704,7 @@ void DCC::ackManagerLoop(bool blocking) {
case V0: case V0:
case V1: // Issue validate bit=0 or bit=1 packet case V1: // Issue validate bit=0 or bit=1 packet
{ {
if (checkResets(blocking, RESET_MIN)) return; if (checkResets(RESET_MIN)) return;
if (Diag::ACK) DIAG(F("\nV%d cv=%d bit=%d"),opcode==V1, ackManagerCv,ackManagerBitNum); if (Diag::ACK) DIAG(F("\nV%d cv=%d bit=%d"),opcode==V1, ackManagerCv,ackManagerBitNum);
byte instruction = VERIFY_BIT | (opcode==V0?BIT_OFF:BIT_ON) | ackManagerBitNum; byte instruction = VERIFY_BIT | (opcode==V0?BIT_OFF:BIT_ON) | ackManagerBitNum;
byte message[] = {cv1(BIT_MANIPULATE, ackManagerCv), cv2(ackManagerCv), instruction }; byte message[] = {cv1(BIT_MANIPULATE, ackManagerCv), cv2(ackManagerCv), instruction };
@ -733,13 +716,9 @@ void DCC::ackManagerLoop(bool blocking) {
case WACK: // wait for ack (or absence of ack) case WACK: // wait for ack (or absence of ack)
{ {
byte ackState=2; // keep polling byte ackState=2; // keep polling
if (blocking) {
while(ackState==2) ackState=DCCWaveform::progTrack.getAck(); ackState=DCCWaveform::progTrack.getAck();
} if (ackState==2) return; // keep polling
else {
ackState=DCCWaveform::progTrack.getAck();
if (ackState==2) return; // keep polling
}
ackReceived=ackState==1; ackReceived=ackState==1;
break; // we have a genuine ACK result break; // we have a genuine ACK result
} }

25
DCC.h
View File

@ -22,6 +22,7 @@
#include "MotorDriver.h" #include "MotorDriver.h"
#include "MotorDrivers.h" #include "MotorDrivers.h"
#include "FSH.h" #include "FSH.h"
typedef void (*ACK_CALLBACK)(int result); typedef void (*ACK_CALLBACK)(int result);
enum ackOp : byte enum ackOp : byte
@ -85,15 +86,15 @@ public:
static void setProgTrackBoost(bool on); // when true, special prog track current limit does not apply static void setProgTrackBoost(bool on); // when true, special prog track current limit does not apply
// ACKable progtrack calls bitresults callback 0,0 or -1, cv returns value or -1 // ACKable progtrack calls bitresults callback 0,0 or -1, cv returns value or -1
static void readCV(int cv, ACK_CALLBACK callback, bool blocking = false); static void readCV(int cv, ACK_CALLBACK callback);
static void readCVBit(int cv, byte bitNum, ACK_CALLBACK callback, bool blocking = false); // -1 for error static void readCVBit(int cv, byte bitNum, ACK_CALLBACK callback); // -1 for error
static void writeCVByte(int cv, byte byteValue, ACK_CALLBACK callback, bool blocking = false); static void writeCVByte(int cv, byte byteValue, ACK_CALLBACK callback);
static void writeCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback, bool blocking = false); static void writeCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback);
static void verifyCVByte(int cv, byte byteValue, ACK_CALLBACK callback, bool blocking = false); static void verifyCVByte(int cv, byte byteValue, ACK_CALLBACK callback);
static void verifyCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback, bool blocking = false); static void verifyCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback);
static void getLocoId(ACK_CALLBACK callback, bool blocking = false); static void getLocoId(ACK_CALLBACK callback);
static void setLocoId(int id,ACK_CALLBACK callback, bool blocking = false); static void setLocoId(int id,ACK_CALLBACK callback);
// Enhanced API functions // Enhanced API functions
static void forgetLoco(int cab); // removes any speed reminders for this loco static void forgetLoco(int cab); // removes any speed reminders for this loco
@ -135,10 +136,10 @@ private:
static byte ackManagerStash; static byte ackManagerStash;
static bool ackReceived; static bool ackReceived;
static ACK_CALLBACK ackManagerCallback; static ACK_CALLBACK ackManagerCallback;
static void ackManagerSetup(int cv, byte bitNumOrbyteValue, ackOp const program[], ACK_CALLBACK callback, bool blocking); static void ackManagerSetup(int cv, byte bitNumOrbyteValue, ackOp const program[], ACK_CALLBACK callback);
static void ackManagerSetup(int wordval, ackOp const program[], ACK_CALLBACK callback, bool blocking); static void ackManagerSetup(int wordval, ackOp const program[], ACK_CALLBACK callback);
static void ackManagerLoop(bool blocking); static void ackManagerLoop();
static bool checkResets(bool blocking, uint8_t numResets); static bool checkResets( uint8_t numResets);
static const int PROG_REPEATS = 8; // repeats of programming commands (some decoders need at least 8 to be reliable) static const int PROG_REPEATS = 8; // repeats of programming commands (some decoders need at least 8 to be reliable)
// NMRA codes # // NMRA codes #

View File

@ -56,6 +56,8 @@ int DCCEXParser::stashP[MAX_COMMAND_PARAMS];
bool DCCEXParser::stashBusy; bool DCCEXParser::stashBusy;
Print *DCCEXParser::stashStream = NULL; Print *DCCEXParser::stashStream = NULL;
RingStream *DCCEXParser::stashRingStream = NULL;
byte DCCEXParser::stashTarget=0;
// This is a JMRI command parser, one instance per incoming stream // 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 doesnt know how the string got here, nor how it gets back.
@ -90,7 +92,7 @@ void DCCEXParser::loop(Stream &stream)
else if (ch == '>') else if (ch == '>')
{ {
buffer[bufferLength] = '\0'; buffer[bufferLength] = '\0';
parse(&stream, buffer, false); // Parse this allowing async responses parse(&stream, buffer, NULL); // Parse this (No ringStream for serial)
inCommandPayload = false; inCommandPayload = false;
break; break;
} }
@ -242,11 +244,11 @@ void DCCEXParser::parse(const FSH * cmd) {
int size=strlen_P((char *)cmd)+1; int size=strlen_P((char *)cmd)+1;
char buffer[size]; char buffer[size];
strcpy_P(buffer,(char *)cmd); strcpy_P(buffer,(char *)cmd);
parse(&Serial,(byte *)buffer,true); parse(&Serial,(byte *)buffer,NULL);
} }
// See documentation on DCC class for info on this section // See documentation on DCC class for info on this section
void DCCEXParser::parse(Print *stream, byte *com, bool blocking) void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream)
{ {
(void)EEPROM; // tell compiler not to warn this is unused (void)EEPROM; // tell compiler not to warn this is unused
if (Diag::CMD) if (Diag::CMD)
@ -381,50 +383,50 @@ void DCCEXParser::parse(Print *stream, byte *com, bool blocking)
return; return;
case 'W': // WRITE CV ON PROG <W CV VALUE CALLBACKNUM CALLBACKSUB> case 'W': // WRITE CV ON PROG <W CV VALUE CALLBACKNUM CALLBACKSUB>
if (!stashCallback(stream, p)) if (!stashCallback(stream, p, ringStream))
break; break;
if (params == 1) // <W id> Write new loco id (clearing consist and managing short/long) if (params == 1) // <W id> Write new loco id (clearing consist and managing short/long)
DCC::setLocoId(p[0],callback_Wloco, blocking); DCC::setLocoId(p[0],callback_Wloco);
else // WRITE CV ON PROG <W CV VALUE [CALLBACKNUM] [CALLBACKSUB]> else // WRITE CV ON PROG <W CV VALUE [CALLBACKNUM] [CALLBACKSUB]>
DCC::writeCVByte(p[0], p[1], callback_W, blocking); DCC::writeCVByte(p[0], p[1], callback_W);
return; return;
case 'V': // VERIFY CV ON PROG <V CV VALUE> <V CV BIT 0|1> case 'V': // VERIFY CV ON PROG <V CV VALUE> <V CV BIT 0|1>
if (params == 2) if (params == 2)
{ // <V CV VALUE> { // <V CV VALUE>
if (!stashCallback(stream, p)) if (!stashCallback(stream, p, ringStream))
break; break;
DCC::verifyCVByte(p[0], p[1], callback_Vbyte, blocking); DCC::verifyCVByte(p[0], p[1], callback_Vbyte);
return; return;
} }
if (params == 3) if (params == 3)
{ {
if (!stashCallback(stream, p)) if (!stashCallback(stream, p, ringStream))
break; break;
DCC::verifyCVBit(p[0], p[1], p[2], callback_Vbit, blocking); DCC::verifyCVBit(p[0], p[1], p[2], callback_Vbit);
return; return;
} }
break; break;
case 'B': // WRITE CV BIT ON PROG <B CV BIT VALUE CALLBACKNUM CALLBACKSUB> case 'B': // WRITE CV BIT ON PROG <B CV BIT VALUE CALLBACKNUM CALLBACKSUB>
if (!stashCallback(stream, p)) if (!stashCallback(stream, p, ringStream))
break; break;
DCC::writeCVBit(p[0], p[1], p[2], callback_B, blocking); DCC::writeCVBit(p[0], p[1], p[2], callback_B);
return; return;
case 'R': // READ CV ON PROG case 'R': // READ CV ON PROG
if (params == 3) if (params == 3)
{ // <R CV CALLBACKNUM CALLBACKSUB> { // <R CV CALLBACKNUM CALLBACKSUB>
if (!stashCallback(stream, p)) if (!stashCallback(stream, p, ringStream))
break; break;
DCC::readCV(p[0], callback_R, blocking); DCC::readCV(p[0], callback_R);
return; return;
} }
if (params == 0) if (params == 0)
{ // <R> New read loco id { // <R> New read loco id
if (!stashCallback(stream, p)) if (!stashCallback(stream, p, ringStream))
break; break;
DCC::getLocoId(callback_Rloco, blocking); DCC::getLocoId(callback_Rloco);
return; return;
} }
break; break;
@ -771,52 +773,70 @@ bool DCCEXParser::parseD(Print *stream, int params, int p[])
} }
// CALLBACKS must be static // CALLBACKS must be static
bool DCCEXParser::stashCallback(Print *stream, int p[MAX_COMMAND_PARAMS]) bool DCCEXParser::stashCallback(Print *stream, int p[MAX_COMMAND_PARAMS], RingStream * ringStream)
{ {
if (stashBusy ) if (stashBusy )
return false; return false;
stashBusy = true; stashBusy = true;
stashStream = stream; stashStream = stream;
stashRingStream=ringStream;
if (ringStream) stashTarget= ringStream->peekTargetMark();
memcpy(stashP, p, MAX_COMMAND_PARAMS * sizeof(p[0])); memcpy(stashP, p, MAX_COMMAND_PARAMS * sizeof(p[0]));
return true; return true;
} }
Print * DCCEXParser::getAsyncReplyStream() {
if (stashRingStream) {
stashRingStream->mark(stashTarget);
return stashRingStream;
}
return stashStream;
}
void DCCEXParser::commitAsyncReplyStream() {
if (stashRingStream) stashRingStream->commit();
stashBusy = false;
}
void DCCEXParser::callback_W(int result) void DCCEXParser::callback_W(int result)
{ {
StringFormatter::send(stashStream, F("<r%d|%d|%d %d>"), stashP[2], stashP[3], stashP[0], result == 1 ? stashP[1] : -1); StringFormatter::send(getAsyncReplyStream(),
stashBusy = false; F("<r%d|%d|%d %d>"), stashP[2], stashP[3], stashP[0], result == 1 ? stashP[1] : -1);
commitAsyncReplyStream();
} }
void DCCEXParser::callback_B(int result) void DCCEXParser::callback_B(int result)
{ {
StringFormatter::send(stashStream, F("<r%d|%d|%d %d %d>"), stashP[3], stashP[4], stashP[0], stashP[1], result == 1 ? stashP[2] : -1); StringFormatter::send(getAsyncReplyStream(),
stashBusy = false; F("<r%d|%d|%d %d %d>"), stashP[3], stashP[4], stashP[0], stashP[1], result == 1 ? stashP[2] : -1);
commitAsyncReplyStream();
} }
void DCCEXParser::callback_Vbit(int result) void DCCEXParser::callback_Vbit(int result)
{ {
StringFormatter::send(stashStream, F("<v %d %d %d>"), stashP[0], stashP[1], result); StringFormatter::send(getAsyncReplyStream(), F("<v %d %d %d>"), stashP[0], stashP[1], result);
stashBusy = false; commitAsyncReplyStream();
} }
void DCCEXParser::callback_Vbyte(int result) void DCCEXParser::callback_Vbyte(int result)
{ {
StringFormatter::send(stashStream, F("<v %d %d>"), stashP[0], result); StringFormatter::send(getAsyncReplyStream(), F("<v %d %d>"), stashP[0], result);
stashBusy = false; commitAsyncReplyStream();
} }
void DCCEXParser::callback_R(int result) void DCCEXParser::callback_R(int result)
{ {
StringFormatter::send(stashStream, F("<r%d|%d|%d %d>"), stashP[1], stashP[2], stashP[0], result); StringFormatter::send(getAsyncReplyStream(), F("<r%d|%d|%d %d>"), stashP[1], stashP[2], stashP[0], result);
stashBusy = false; commitAsyncReplyStream();
} }
void DCCEXParser::callback_Rloco(int result) void DCCEXParser::callback_Rloco(int result)
{ {
StringFormatter::send(stashStream, F("<r %d>"), result); StringFormatter::send(getAsyncReplyStream(), F("<r %d>"), result);
stashBusy = false; commitAsyncReplyStream();
} }
void DCCEXParser::callback_Wloco(int result) void DCCEXParser::callback_Wloco(int result)
{ {
if (result==1) result=stashP[0]; // pick up original requested id from command if (result==1) result=stashP[0]; // pick up original requested id from command
StringFormatter::send(stashStream, F("<w %d>"), result); StringFormatter::send(getAsyncReplyStream(), F("<w %d>"), result);
stashBusy = false; commitAsyncReplyStream();
} }

View File

@ -20,6 +20,7 @@
#define DCCEXParser_h #define DCCEXParser_h
#include <Arduino.h> #include <Arduino.h>
#include "FSH.h" #include "FSH.h"
#include "RingStream.h"
typedef void (*FILTER_CALLBACK)(Print * stream, byte & opcode, byte & paramCount, int p[]); typedef void (*FILTER_CALLBACK)(Print * stream, byte & opcode, byte & paramCount, int p[]);
typedef void (*AT_COMMAND_CALLBACK)(const byte * command); typedef void (*AT_COMMAND_CALLBACK)(const byte * command);
@ -28,7 +29,7 @@ struct DCCEXParser
{ {
DCCEXParser(); DCCEXParser();
void loop(Stream & stream); void loop(Stream & stream);
void parse(Print * stream, byte * command, bool blocking); void parse(Print * stream, byte * command, RingStream * ringStream);
void parse(const FSH * cmd); void parse(const FSH * cmd);
void flush(); void flush();
static void setFilter(FILTER_CALLBACK filter); static void setFilter(FILTER_CALLBACK filter);
@ -51,12 +52,16 @@ struct DCCEXParser
bool parsef(Print * stream, int params, int p[]); bool parsef(Print * stream, int params, int p[]);
bool parseD(Print * stream, int params, int p[]); bool parseD(Print * stream, int params, int p[]);
static Print * getAsyncReplyStream();
static void commitAsyncReplyStream();
static bool stashBusy; static bool stashBusy;
static byte stashTarget;
static Print * stashStream; static Print * stashStream;
static RingStream * stashRingStream;
static int stashP[MAX_COMMAND_PARAMS]; static int stashP[MAX_COMMAND_PARAMS];
bool stashCallback(Print * stream, int p[MAX_COMMAND_PARAMS]); bool stashCallback(Print * stream, int p[MAX_COMMAND_PARAMS], RingStream * ringStream);
static void callback_W(int result); static void callback_W(int result);
static void callback_B(int result); static void callback_B(int result);
static void callback_R(int result); static void callback_R(int result);

View File

@ -75,6 +75,12 @@ void RingStream::mark(uint8_t b) {
_count=0; _count=0;
} }
// peekTargetMark is used by the parser stash routines to know which client
// to send a callback response to some time later.
uint8_t RingStream::peekTargetMark() {
return _buffer[_mark];
}
bool RingStream::commit() { bool RingStream::commit() {
if (_overflow) { if (_overflow) {
DIAG(F("\nRingStream(%d) commit(%d) OVERFLOW\n"),_len, _count); DIAG(F("\nRingStream(%d) commit(%d) OVERFLOW\n"),_len, _count);

View File

@ -33,6 +33,7 @@ class RingStream : public Print {
int freeSpace(); int freeSpace();
void mark(uint8_t b); void mark(uint8_t b);
bool commit(); bool commit();
uint8_t peekTargetMark();
private: private:
int _len; int _len;