mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-22 23:56:13 +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:
parent
df4bae365d
commit
36e38bf861
|
@ -25,7 +25,7 @@ DCCEXParser * CommandDistributor::parser=0;
|
|||
void CommandDistributor::parse(byte clientId,byte * buffer, RingStream * streamer) {
|
||||
if (buffer[0] == '<') {
|
||||
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);
|
||||
}
|
||||
|
|
85
DCC.cpp
85
DCC.cpp
|
@ -447,56 +447,47 @@ const ackOp FLASH LONG_LOCO_ID_PROG[] = {
|
|||
FAIL
|
||||
};
|
||||
|
||||
// On the following prog-track functions blocking defaults to false.
|
||||
// blocking=true forces the API to block, waiting for the response and invoke the callback BEFORE returning.
|
||||
// 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::writeCVByte(int cv, byte byteValue, ACK_CALLBACK callback) {
|
||||
ackManagerSetup(cv, byteValue, WRITE_BYTE_PROG, callback);
|
||||
}
|
||||
|
||||
|
||||
void DCC::writeCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback, bool blocking) {
|
||||
void DCC::writeCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback) {
|
||||
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) {
|
||||
ackManagerSetup(cv, byteValue, VERIFY_BYTE_PROG, callback, blocking);
|
||||
void DCC::verifyCVByte(int cv, byte byteValue, ACK_CALLBACK callback) {
|
||||
ackManagerSetup(cv, byteValue, VERIFY_BYTE_PROG, callback);
|
||||
}
|
||||
|
||||
|
||||
void DCC::verifyCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback, bool blocking) {
|
||||
void DCC::verifyCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback) {
|
||||
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);
|
||||
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) {
|
||||
ackManagerSetup(cv, 0,READ_CV_PROG, callback, blocking);
|
||||
void DCC::readCV(int cv, ACK_CALLBACK callback) {
|
||||
ackManagerSetup(cv, 0,READ_CV_PROG, callback);
|
||||
}
|
||||
|
||||
void DCC::getLocoId(ACK_CALLBACK callback, bool blocking) {
|
||||
ackManagerSetup(0,0, LOCO_ID_PROG, callback, blocking);
|
||||
void DCC::getLocoId(ACK_CALLBACK callback) {
|
||||
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
|
||||
callback(-1);
|
||||
return;
|
||||
}
|
||||
if (id<=127)
|
||||
ackManagerSetup(id, SHORT_LOCO_ID_PROG, callback, blocking);
|
||||
ackManagerSetup(id, SHORT_LOCO_ID_PROG, callback);
|
||||
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
|
||||
|
@ -511,7 +502,7 @@ byte DCC::loopStatus=0;
|
|||
|
||||
void DCC::loop() {
|
||||
DCCWaveform::loop(ackManagerProg!=NULL); // power overload checks
|
||||
ackManagerLoop(false); // maintain prog track ack manager
|
||||
ackManagerLoop(); // maintain prog track ack manager
|
||||
issueReminders();
|
||||
}
|
||||
|
||||
|
@ -638,42 +629,34 @@ bool DCC::ackReceived;
|
|||
|
||||
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;
|
||||
ackManagerProg = program;
|
||||
ackManagerByte = byteValueOrBitnum;
|
||||
ackManagerBitNum=byteValueOrBitnum;
|
||||
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;
|
||||
ackManagerProg = program;
|
||||
ackManagerCallback = callback;
|
||||
if (blocking) ackManagerLoop(blocking);
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
bool DCC::checkResets(bool blocking, uint8_t numResets) {
|
||||
if (blocking) {
|
||||
// must block waiting for restest to be issued
|
||||
while(DCCWaveform::progTrack.sentResetsSincePacket < numResets);
|
||||
return false; // caller need not yield
|
||||
}
|
||||
bool DCC::checkResets(uint8_t numResets) {
|
||||
return DCCWaveform::progTrack.sentResetsSincePacket < numResets;
|
||||
}
|
||||
|
||||
void DCC::ackManagerLoop(bool blocking) {
|
||||
void DCC::ackManagerLoop() {
|
||||
while (ackManagerProg) {
|
||||
byte opcode=GETFLASH(ackManagerProg);
|
||||
|
||||
// breaks from this switch will step to next prog entry
|
||||
// returns from this switch will stay on same entry
|
||||
// (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) {
|
||||
case BASELINE:
|
||||
if (DCCWaveform::progTrack.getPowerMode() == POWERMODE::OFF) {
|
||||
|
@ -681,15 +664,15 @@ void DCC::ackManagerLoop(bool blocking) {
|
|||
DCCWaveform::progTrack.setPowerMode(POWERMODE::ON);
|
||||
DCCWaveform::progTrack.sentResetsSincePacket = 0;
|
||||
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();
|
||||
break;
|
||||
case W0: // write 0 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);
|
||||
byte instruction = WRITE_BIT | (opcode==W1 ? BIT_ON : BIT_OFF) | ackManagerBitNum;
|
||||
byte message[] = {cv1(BIT_MANIPULATE, ackManagerCv), cv2(ackManagerCv), instruction };
|
||||
|
@ -700,7 +683,7 @@ void DCC::ackManagerLoop(bool blocking) {
|
|||
|
||||
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);
|
||||
byte message[] = {cv1(WRITE_BYTE, ackManagerCv), cv2(ackManagerCv), ackManagerByte};
|
||||
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS);
|
||||
|
@ -710,7 +693,7 @@ void DCC::ackManagerLoop(bool blocking) {
|
|||
|
||||
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);
|
||||
byte message[] = { cv1(VERIFY_BYTE, ackManagerCv), cv2(ackManagerCv), ackManagerByte};
|
||||
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS);
|
||||
|
@ -721,7 +704,7 @@ void DCC::ackManagerLoop(bool blocking) {
|
|||
case V0:
|
||||
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);
|
||||
byte instruction = VERIFY_BIT | (opcode==V0?BIT_OFF:BIT_ON) | ackManagerBitNum;
|
||||
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)
|
||||
{
|
||||
byte ackState=2; // keep polling
|
||||
if (blocking) {
|
||||
while(ackState==2) ackState=DCCWaveform::progTrack.getAck();
|
||||
}
|
||||
else {
|
||||
ackState=DCCWaveform::progTrack.getAck();
|
||||
if (ackState==2) return; // keep polling
|
||||
}
|
||||
|
||||
ackState=DCCWaveform::progTrack.getAck();
|
||||
if (ackState==2) return; // keep polling
|
||||
ackReceived=ackState==1;
|
||||
break; // we have a genuine ACK result
|
||||
}
|
||||
|
|
25
DCC.h
25
DCC.h
|
@ -22,6 +22,7 @@
|
|||
#include "MotorDriver.h"
|
||||
#include "MotorDrivers.h"
|
||||
#include "FSH.h"
|
||||
|
||||
typedef void (*ACK_CALLBACK)(int result);
|
||||
|
||||
enum ackOp : byte
|
||||
|
@ -85,15 +86,15 @@ public:
|
|||
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
|
||||
static void readCV(int cv, ACK_CALLBACK callback, bool blocking = false);
|
||||
static void readCVBit(int cv, byte bitNum, ACK_CALLBACK callback, bool blocking = false); // -1 for error
|
||||
static void writeCVByte(int cv, byte byteValue, ACK_CALLBACK callback, bool blocking = false);
|
||||
static void writeCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback, bool blocking = false);
|
||||
static void verifyCVByte(int cv, byte byteValue, ACK_CALLBACK callback, bool blocking = false);
|
||||
static void verifyCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback, bool blocking = false);
|
||||
static void readCV(int cv, ACK_CALLBACK callback);
|
||||
static void readCVBit(int cv, byte bitNum, ACK_CALLBACK callback); // -1 for error
|
||||
static void writeCVByte(int cv, byte byteValue, ACK_CALLBACK callback);
|
||||
static void writeCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback);
|
||||
static void verifyCVByte(int cv, byte byteValue, ACK_CALLBACK callback);
|
||||
static void verifyCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback);
|
||||
|
||||
static void getLocoId(ACK_CALLBACK callback, bool blocking = false);
|
||||
static void setLocoId(int id,ACK_CALLBACK callback, bool blocking = false);
|
||||
static void getLocoId(ACK_CALLBACK callback);
|
||||
static void setLocoId(int id,ACK_CALLBACK callback);
|
||||
|
||||
// Enhanced API functions
|
||||
static void forgetLoco(int cab); // removes any speed reminders for this loco
|
||||
|
@ -135,10 +136,10 @@ private:
|
|||
static byte ackManagerStash;
|
||||
static bool ackReceived;
|
||||
static ACK_CALLBACK ackManagerCallback;
|
||||
static void ackManagerSetup(int cv, byte bitNumOrbyteValue, ackOp const program[], ACK_CALLBACK callback, bool blocking);
|
||||
static void ackManagerSetup(int wordval, ackOp const program[], ACK_CALLBACK callback, bool blocking);
|
||||
static void ackManagerLoop(bool blocking);
|
||||
static bool checkResets(bool blocking, uint8_t numResets);
|
||||
static void ackManagerSetup(int cv, byte bitNumOrbyteValue, ackOp const program[], ACK_CALLBACK callback);
|
||||
static void ackManagerSetup(int wordval, ackOp const program[], ACK_CALLBACK callback);
|
||||
static void ackManagerLoop();
|
||||
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)
|
||||
|
||||
// NMRA codes #
|
||||
|
|
|
@ -56,6 +56,8 @@ int DCCEXParser::stashP[MAX_COMMAND_PARAMS];
|
|||
bool DCCEXParser::stashBusy;
|
||||
|
||||
Print *DCCEXParser::stashStream = NULL;
|
||||
RingStream *DCCEXParser::stashRingStream = NULL;
|
||||
byte DCCEXParser::stashTarget=0;
|
||||
|
||||
// This is a JMRI command parser, one instance per incoming stream
|
||||
// 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 == '>')
|
||||
{
|
||||
buffer[bufferLength] = '\0';
|
||||
parse(&stream, buffer, false); // Parse this allowing async responses
|
||||
parse(&stream, buffer, NULL); // Parse this (No ringStream for serial)
|
||||
inCommandPayload = false;
|
||||
break;
|
||||
}
|
||||
|
@ -242,11 +244,11 @@ void DCCEXParser::parse(const FSH * cmd) {
|
|||
int size=strlen_P((char *)cmd)+1;
|
||||
char buffer[size];
|
||||
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
|
||||
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
|
||||
if (Diag::CMD)
|
||||
|
@ -381,50 +383,50 @@ void DCCEXParser::parse(Print *stream, byte *com, bool blocking)
|
|||
return;
|
||||
|
||||
case 'W': // WRITE CV ON PROG <W CV VALUE CALLBACKNUM CALLBACKSUB>
|
||||
if (!stashCallback(stream, p))
|
||||
if (!stashCallback(stream, p, ringStream))
|
||||
break;
|
||||
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]>
|
||||
DCC::writeCVByte(p[0], p[1], callback_W, blocking);
|
||||
DCC::writeCVByte(p[0], p[1], callback_W);
|
||||
return;
|
||||
|
||||
case 'V': // VERIFY CV ON PROG <V CV VALUE> <V CV BIT 0|1>
|
||||
if (params == 2)
|
||||
{ // <V CV VALUE>
|
||||
if (!stashCallback(stream, p))
|
||||
if (!stashCallback(stream, p, ringStream))
|
||||
break;
|
||||
DCC::verifyCVByte(p[0], p[1], callback_Vbyte, blocking);
|
||||
DCC::verifyCVByte(p[0], p[1], callback_Vbyte);
|
||||
return;
|
||||
}
|
||||
if (params == 3)
|
||||
{
|
||||
if (!stashCallback(stream, p))
|
||||
if (!stashCallback(stream, p, ringStream))
|
||||
break;
|
||||
DCC::verifyCVBit(p[0], p[1], p[2], callback_Vbit, blocking);
|
||||
DCC::verifyCVBit(p[0], p[1], p[2], callback_Vbit);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'B': // WRITE CV BIT ON PROG <B CV BIT VALUE CALLBACKNUM CALLBACKSUB>
|
||||
if (!stashCallback(stream, p))
|
||||
if (!stashCallback(stream, p, ringStream))
|
||||
break;
|
||||
DCC::writeCVBit(p[0], p[1], p[2], callback_B, blocking);
|
||||
DCC::writeCVBit(p[0], p[1], p[2], callback_B);
|
||||
return;
|
||||
|
||||
case 'R': // READ CV ON PROG
|
||||
if (params == 3)
|
||||
{ // <R CV CALLBACKNUM CALLBACKSUB>
|
||||
if (!stashCallback(stream, p))
|
||||
if (!stashCallback(stream, p, ringStream))
|
||||
break;
|
||||
DCC::readCV(p[0], callback_R, blocking);
|
||||
DCC::readCV(p[0], callback_R);
|
||||
return;
|
||||
}
|
||||
if (params == 0)
|
||||
{ // <R> New read loco id
|
||||
if (!stashCallback(stream, p))
|
||||
if (!stashCallback(stream, p, ringStream))
|
||||
break;
|
||||
DCC::getLocoId(callback_Rloco, blocking);
|
||||
DCC::getLocoId(callback_Rloco);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
@ -771,52 +773,70 @@ bool DCCEXParser::parseD(Print *stream, int params, int p[])
|
|||
}
|
||||
|
||||
// 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 )
|
||||
return false;
|
||||
stashBusy = true;
|
||||
stashStream = stream;
|
||||
stashRingStream=ringStream;
|
||||
if (ringStream) stashTarget= ringStream->peekTargetMark();
|
||||
memcpy(stashP, p, MAX_COMMAND_PARAMS * sizeof(p[0]));
|
||||
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)
|
||||
{
|
||||
StringFormatter::send(stashStream, F("<r%d|%d|%d %d>"), stashP[2], stashP[3], stashP[0], result == 1 ? stashP[1] : -1);
|
||||
stashBusy = false;
|
||||
StringFormatter::send(getAsyncReplyStream(),
|
||||
F("<r%d|%d|%d %d>"), stashP[2], stashP[3], stashP[0], result == 1 ? stashP[1] : -1);
|
||||
commitAsyncReplyStream();
|
||||
}
|
||||
|
||||
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);
|
||||
stashBusy = false;
|
||||
StringFormatter::send(getAsyncReplyStream(),
|
||||
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)
|
||||
{
|
||||
StringFormatter::send(stashStream, F("<v %d %d %d>"), stashP[0], stashP[1], result);
|
||||
stashBusy = false;
|
||||
StringFormatter::send(getAsyncReplyStream(), F("<v %d %d %d>"), stashP[0], stashP[1], result);
|
||||
commitAsyncReplyStream();
|
||||
}
|
||||
void DCCEXParser::callback_Vbyte(int result)
|
||||
{
|
||||
StringFormatter::send(stashStream, F("<v %d %d>"), stashP[0], result);
|
||||
stashBusy = false;
|
||||
StringFormatter::send(getAsyncReplyStream(), F("<v %d %d>"), stashP[0], result);
|
||||
commitAsyncReplyStream();
|
||||
}
|
||||
|
||||
void DCCEXParser::callback_R(int result)
|
||||
{
|
||||
StringFormatter::send(stashStream, F("<r%d|%d|%d %d>"), stashP[1], stashP[2], stashP[0], result);
|
||||
stashBusy = false;
|
||||
StringFormatter::send(getAsyncReplyStream(), F("<r%d|%d|%d %d>"), stashP[1], stashP[2], stashP[0], result);
|
||||
commitAsyncReplyStream();
|
||||
}
|
||||
|
||||
void DCCEXParser::callback_Rloco(int result)
|
||||
{
|
||||
StringFormatter::send(stashStream, F("<r %d>"), result);
|
||||
stashBusy = false;
|
||||
StringFormatter::send(getAsyncReplyStream(), F("<r %d>"), result);
|
||||
commitAsyncReplyStream();
|
||||
}
|
||||
|
||||
void DCCEXParser::callback_Wloco(int result)
|
||||
{
|
||||
if (result==1) result=stashP[0]; // pick up original requested id from command
|
||||
StringFormatter::send(stashStream, F("<w %d>"), result);
|
||||
stashBusy = false;
|
||||
StringFormatter::send(getAsyncReplyStream(), F("<w %d>"), result);
|
||||
commitAsyncReplyStream();
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#define DCCEXParser_h
|
||||
#include <Arduino.h>
|
||||
#include "FSH.h"
|
||||
#include "RingStream.h"
|
||||
|
||||
typedef void (*FILTER_CALLBACK)(Print * stream, byte & opcode, byte & paramCount, int p[]);
|
||||
typedef void (*AT_COMMAND_CALLBACK)(const byte * command);
|
||||
|
@ -28,7 +29,7 @@ struct DCCEXParser
|
|||
{
|
||||
DCCEXParser();
|
||||
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 flush();
|
||||
static void setFilter(FILTER_CALLBACK filter);
|
||||
|
@ -51,12 +52,16 @@ struct DCCEXParser
|
|||
bool parsef(Print * stream, int params, int p[]);
|
||||
bool parseD(Print * stream, int params, int p[]);
|
||||
|
||||
|
||||
static Print * getAsyncReplyStream();
|
||||
static void commitAsyncReplyStream();
|
||||
|
||||
static bool stashBusy;
|
||||
|
||||
static byte stashTarget;
|
||||
static Print * stashStream;
|
||||
static RingStream * stashRingStream;
|
||||
|
||||
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_B(int result);
|
||||
static void callback_R(int result);
|
||||
|
|
|
@ -75,6 +75,12 @@ void RingStream::mark(uint8_t b) {
|
|||
_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() {
|
||||
if (_overflow) {
|
||||
DIAG(F("\nRingStream(%d) commit(%d) OVERFLOW\n"),_len, _count);
|
||||
|
|
|
@ -33,7 +33,8 @@ class RingStream : public Print {
|
|||
int freeSpace();
|
||||
void mark(uint8_t b);
|
||||
bool commit();
|
||||
|
||||
uint8_t peekTargetMark();
|
||||
|
||||
private:
|
||||
int _len;
|
||||
int _pos_write;
|
||||
|
|
Loading…
Reference in New Issue
Block a user