mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-26 17:46: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:
parent
df4bae365d
commit
36e38bf861
|
@ -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
85
DCC.cpp
|
@ -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
25
DCC.h
|
@ -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 #
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -33,7 +33,8 @@ 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;
|
||||||
int _pos_write;
|
int _pos_write;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user