1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-12-24 13:21:23 +01:00

Blocking progtrack call optoin

Note, as yet UNTESTED
This commit is contained in:
Asbelos 2020-07-19 18:39:08 +01:00
parent 0ea0cc6391
commit 2ae7aedcd2
4 changed files with 60 additions and 38 deletions

72
DCC.cpp
View File

@ -262,29 +262,35 @@ const ackOp PROGMEM LOCO_ID_PROG[] = {
}; };
// 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) { void DCC::writeCVByte(int cv, byte byteValue, ACK_CALLBACK callback, bool blocking) {
ackManagerSetup(cv, byteValue, WRITE_BYTE_PROG, callback); 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); else ackManagerSetup(cv, bitNum, bitValue?WRITE_BIT1_PROG:WRITE_BIT0_PROG, callback, blocking);
} }
void DCC::readCVBit(int cv, byte bitNum, ACK_CALLBACK callback) { void DCC::readCVBit(int cv, byte bitNum, ACK_CALLBACK callback, bool blocking) {
if (bitNum >= 8) callback(-1); if (bitNum >= 8) callback(-1);
else ackManagerSetup(cv, bitNum,READ_BIT_PROG, callback); else ackManagerSetup(cv, bitNum,READ_BIT_PROG, callback, blocking);
} }
void DCC::readCV(int cv, ACK_CALLBACK callback) { void DCC::readCV(int cv, ACK_CALLBACK callback, bool blocking) {
ackManagerSetup(cv, 0,READ_CV_PROG, callback); ackManagerSetup(cv, 0,READ_CV_PROG, callback, blocking);
} }
void DCC::getLocoId(ACK_CALLBACK callback) { void DCC::getLocoId(ACK_CALLBACK callback, bool blocking) {
ackManagerSetup(0,0, LOCO_ID_PROG, callback); ackManagerSetup(0,0, LOCO_ID_PROG, callback, blocking);
} }
void DCC::forgetLoco(int cab) { // removes any speed reminders for this loco void DCC::forgetLoco(int cab) { // removes any speed reminders for this loco
@ -303,7 +309,7 @@ byte DCC::loopStatus=0;
void DCC::loop() { void DCC::loop() {
DCCWaveform::loop(); // power overload checks DCCWaveform::loop(); // power overload checks
ackManagerLoop(); // maintain prog track ack manager ackManagerLoop(false); // maintain prog track ack manager
issueReminders(); issueReminders();
} }
@ -428,34 +434,44 @@ bool DCC::debugMode=false;
ACK_CALLBACK DCC::ackManagerCallback; ACK_CALLBACK DCC::ackManagerCallback;
void DCC::ackManagerSetup(int cv, byte byteValueOrBitnum, ackOp const program[], ACK_CALLBACK callback) { void DCC::ackManagerSetup(int cv, byte byteValueOrBitnum, ackOp const program[], ACK_CALLBACK callback, bool blocking) {
ackManagerCv = cv; ackManagerCv = cv;
ackManagerProg = program; ackManagerProg = program;
ackManagerByte = byteValueOrBitnum; ackManagerByte = byteValueOrBitnum;
ackManagerBitNum=byteValueOrBitnum; ackManagerBitNum=byteValueOrBitnum;
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
void DCC::ackManagerLoop() { // checkRessets return true if the caller should yield back to loop and try later.
while (ackManagerProg) { bool DCC::checkResets(bool blocking) {
if (blocking) {
// must block waiting for restest to be issued
while(DCCWaveform::progTrack.sentResetsSincePacket < RESET_MIN);
return false; // caller need not yield
}
return DCCWaveform::progTrack.sentResetsSincePacket < RESET_MIN;
}
// breaks from this switch will step to next prog entry void DCC::ackManagerLoop(bool blocking) {
// returns from this switch will stay on same entry (typically WACK waiting and when all finished.) while (ackManagerProg) {
byte opcode=pgm_read_byte_near(ackManagerProg); byte opcode=pgm_read_byte_near(ackManagerProg);
int resets=DCCWaveform::progTrack.sentResetsSincePacket;
// 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) { switch (opcode) {
case BASELINE: case BASELINE:
if (resets<RESET_MIN) return; // try later if (checkResets(blocking)) return;
DCCWaveform::progTrack.setAckBaseline(debugMode); DCCWaveform::progTrack.setAckBaseline(debugMode);
break; break;
case W0: // write 0 bit case W0: // write 0 bit
case W1: // write 1 bit case W1: // write 1 bit
{ {
if (resets<RESET_MIN) return; // try later if (checkResets(blocking)) return;
if (debugMode) DIAG(F("\nW%d cv=%d bit=%d"),opcode==W1, ackManagerCv,ackManagerBitNum); if (debugMode) 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 };
@ -466,7 +482,7 @@ void DCC::ackManagerLoop() {
case WB: // write byte case WB: // write byte
{ {
if (resets<RESET_MIN) return; // try later if (checkResets(blocking)) return;
if (debugMode) DIAG(F("\nWB cv=%d value=%d"),ackManagerCv,ackManagerByte); if (debugMode) 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);
@ -476,7 +492,7 @@ void DCC::ackManagerLoop() {
case VB: // Issue validate Byte packet case VB: // Issue validate Byte packet
{ {
if (resets<RESET_MIN) return; // try later if (checkResets(blocking)) return;
if (debugMode) DIAG(F("\nVB cv=%d value=%d"),ackManagerCv,ackManagerByte); if (debugMode) 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);
@ -487,7 +503,7 @@ void DCC::ackManagerLoop() {
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 (resets<RESET_MIN) return; // try later if (checkResets(blocking)) return;
if (debugMode) DIAG(F("\nV%d cv=%d bit=%d"),opcode==V1, ackManagerCv,ackManagerBitNum); if (debugMode) 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 };
@ -498,8 +514,14 @@ void DCC::ackManagerLoop() {
case WACK: // wait for ack (or absence of ack) case WACK: // wait for ack (or absence of ack)
{ {
byte ackState=DCCWaveform::progTrack.getAck(debugMode); byte ackState=2; // keep polling
if (ackState==2) return; // keep polling if (blocking) {
while(ackState==2) ackState=DCCWaveform::progTrack.getAck(debugMode);
}
else {
ackState=DCCWaveform::progTrack.getAck(debugMode);
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
} }

15
DCC.h
View File

@ -66,12 +66,12 @@ class DCC {
static void setProgTrackSyncMain(bool on); // when true, prog track becomes driveable static void setProgTrackSyncMain(bool on); // when true, prog track becomes driveable
// 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); static void readCV(int cv, ACK_CALLBACK callback, bool blocking=false);
static void readCVBit(int cv, byte bitNum, ACK_CALLBACK callback); // -1 for error 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) ; 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); static void writeCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback, bool blocking=false);
static void getLocoId(ACK_CALLBACK callback); static void getLocoId(ACK_CALLBACK callback, bool blocking=false);
// 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
@ -106,8 +106,9 @@ 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); static void ackManagerSetup(int cv, byte bitNumOrbyteValue, ackOp const program[], ACK_CALLBACK callback, bool blocking);
static void ackManagerLoop(); static void ackManagerLoop(bool blocking);
static bool checkResets(bool blocking);
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)

View File

@ -127,9 +127,8 @@ void DCCEXParser::setFilter(FILTER_CALLBACK filter) {
} }
// 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, const byte *com, bool banAsync) { void DCCEXParser::parse(Print & stream, const byte *com, bool blocking) {
DIAG(F("\nPARSING:%s\n"),com); DIAG(F("\nPARSING:%s\n"),com);
asyncBanned=banAsync;
(void) EEPROM; // tell compiler not to warn thi is unused (void) EEPROM; // tell compiler not to warn thi is unused
int p[MAX_PARAMS]; int p[MAX_PARAMS];
while (com[0]=='<' || com[0]==' ') com++; // strip off any number of < or spaces while (com[0]=='<' || com[0]==' ') com++; // strip off any number of < or spaces
@ -177,17 +176,17 @@ void DCCEXParser::parse(Print & stream, const byte *com, bool banAsync) {
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)) break; if (!stashCallback(stream,p)) break;
DCC::writeCVByte(p[0],p[1],callback_W); DCC::writeCVByte(p[0],p[1],callback_W,blocking);
return; return;
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)) break; if (!stashCallback(stream,p)) break;
DCC::writeCVBit(p[0],p[1],p[2],callback_B); DCC::writeCVBit(p[0],p[1],p[2],callback_B,blocking);
return; return;
case 'R': // READ CV ON PROG <R CV CALLBACKNUM CALLBACKSUB> case 'R': // READ CV ON PROG <R CV CALLBACKNUM CALLBACKSUB>
if (!stashCallback(stream,p)) break; if (!stashCallback(stream,p)) break;
DCC::readCV(p[0],callback_R); DCC::readCV(p[0],callback_R,blocking);
return; return;
case '1': // POWERON <1 [MAIN|PROG]> case '1': // POWERON <1 [MAIN|PROG]>

View File

@ -26,7 +26,7 @@ struct DCCEXParser
{ {
DCCEXParser(); DCCEXParser();
void loop(Stream & pstream); void loop(Stream & pstream);
void parse(Print & stream, const byte * command, bool banAsync); void parse(Print & stream, const byte * command, bool blocking);
void flush(); void flush();
static void setFilter(FILTER_CALLBACK filter); static void setFilter(FILTER_CALLBACK filter);
static const int MAX_PARAMS=10; // Must not exceed this static const int MAX_PARAMS=10; // Must not exceed this