diff --git a/DCC.cpp b/DCC.cpp index 97564be..6a33585 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -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) { - ackManagerSetup(cv, byteValue, WRITE_BYTE_PROG, callback); +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); - 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); - 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) { - ackManagerSetup(cv, 0,READ_CV_PROG, callback); +void DCC::readCV(int cv, ACK_CALLBACK callback, bool blocking) { + ackManagerSetup(cv, 0,READ_CV_PROG, callback, blocking); } -void DCC::getLocoId(ACK_CALLBACK callback) { - ackManagerSetup(0,0, LOCO_ID_PROG, callback); +void DCC::getLocoId(ACK_CALLBACK callback, bool blocking) { + ackManagerSetup(0,0, LOCO_ID_PROG, callback, blocking); } void DCC::forgetLoco(int cab) { // removes any speed reminders for this loco @@ -303,7 +309,7 @@ byte DCC::loopStatus=0; void DCC::loop() { DCCWaveform::loop(); // power overload checks - ackManagerLoop(); // maintain prog track ack manager + ackManagerLoop(false); // maintain prog track ack manager issueReminders(); } @@ -428,34 +434,44 @@ bool DCC::debugMode=false; 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; ackManagerProg = program; ackManagerByte = byteValueOrBitnum; ackManagerBitNum=byteValueOrBitnum; ackManagerCallback = callback; + if (blocking) ackManagerLoop(blocking); } const byte RESET_MIN=8; // tuning of reset counter before sending message -void DCC::ackManagerLoop() { - while (ackManagerProg) { +// checkRessets return true if the caller should yield back to loop and try later. +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 - // returns from this switch will stay on same entry (typically WACK waiting and when all finished.) +void DCC::ackManagerLoop(bool blocking) { + while (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) { case BASELINE: - if (resets if (!stashCallback(stream,p)) break; - DCC::writeCVByte(p[0],p[1],callback_W); + DCC::writeCVByte(p[0],p[1],callback_W,blocking); return; case 'B': // WRITE CV BIT ON PROG 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; case 'R': // READ CV ON PROG if (!stashCallback(stream,p)) break; - DCC::readCV(p[0],callback_R); + DCC::readCV(p[0],callback_R,blocking); return; case '1': // POWERON <1 [MAIN|PROG]> diff --git a/DCCEXParser.h b/DCCEXParser.h index 71d9cc8..423df76 100644 --- a/DCCEXParser.h +++ b/DCCEXParser.h @@ -26,7 +26,7 @@ struct DCCEXParser { DCCEXParser(); void loop(Stream & pstream); - void parse(Print & stream, const byte * command, bool banAsync); + void parse(Print & stream, const byte * command, bool blocking); void flush(); static void setFilter(FILTER_CALLBACK filter); static const int MAX_PARAMS=10; // Must not exceed this