mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-12-24 21:21:24 +01:00
Blocking progtrack call optoin
Note, as yet UNTESTED
This commit is contained in:
parent
0ea0cc6391
commit
2ae7aedcd2
72
DCC.cpp
72
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) {
|
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.
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCC::ackManagerLoop(bool blocking) {
|
||||||
while (ackManagerProg) {
|
while (ackManagerProg) {
|
||||||
|
byte opcode=pgm_read_byte_near(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 (typically WACK waiting and when all finished.)
|
// returns from this switch will stay on same entry
|
||||||
byte opcode=pgm_read_byte_near(ackManagerProg);
|
// (typically waiting for a reset counter or ACK waiting, or when all finished.)
|
||||||
int resets=DCCWaveform::progTrack.sentResetsSincePacket;
|
// 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
15
DCC.h
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
@ -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]>
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user