From 4c8b7f851754c5a648fabe0bd1d58ff7a62aa286 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sun, 3 Apr 2022 11:19:04 +0100 Subject: [PATCH 1/7] Rebuild throttle info getters UNTESTED... create different methods to obtain throttle info without being withrottle specific. Also implements turnout description of "*" as hidden. --- EXRAIL2.cpp | 20 ++--------- EXRAIL2.h | 19 ++++++----- EXRAILMacros.h | 93 ++++++++++++++++++++++++++++++-------------------- WiThrottle.cpp | 42 +++++++++++++++++------ 4 files changed, 101 insertions(+), 73 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 3c7af23..1a4f536 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -40,7 +40,6 @@ T2. Extend to >64k */ - #include #include "EXRAIL2.h" #include "DCC.h" @@ -347,13 +346,6 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { } return true; - case HASH_KEYWORD_ROUTES: // JMRI withrottle support - if (paramCount>1) return false; - StringFormatter::send(stream,F("")); - return true; - default: break; } @@ -408,11 +400,7 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { // Automations are given a state to set the button to "handoff" which implies // handing over the loco to the automation. // Routes are given "Set" buttons and do not cause the loco to be handed over. -void RMFT2::emitWithrottleRouteList(Print* stream) { - StringFormatter::send(stream,F("PRT]\\[Routes}|{Route]\\[Set}|{2]\\[Handoff}|{4\nPRL")); - emitWithrottleDescriptions(stream); - StringFormatter::send(stream,F("\n")); -} + RMFT2::RMFT2(int progCtr) { @@ -1037,8 +1025,4 @@ void RMFT2::printMessage2(const FSH * msg) { DIAG(F("EXRAIL(%d) %S"),loco,msg); } -// This is called by emitRouteDescriptions to emit a withrottle description for a route or autoomation. -void RMFT2::emitRouteDescription(Print * stream, char type, int id, const FSH * description) { - StringFormatter::send(stream,F("]\\[%c%d}|{%S}|{%c"), - type,id,description, type=='R'?'2':'4'); -} + diff --git a/EXRAIL2.h b/EXRAIL2.h index 6cc0243..97e0d98 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -87,17 +87,21 @@ class LookList { RMFT2(int route, uint16_t cab); ~RMFT2(); static void readLocoCallback(int16_t cv); - static void emitWithrottleRouteList(Print* stream); static void createNewTask(int route, uint16_t cab); static void turnoutEvent(int16_t id, bool closed); static void activateEvent(int16_t addr, bool active); - static void emitTurnoutDescription(Print* stream,int16_t id); - static const byte rosterNameCount; - static void emitWithrottleRoster(Print * stream); - static const FSH * getRosterFunctions(int16_t cabid); static const int16_t SERVO_SIGNAL_FLAG=0x4000; static const int16_t ACTIVE_HIGH_SIGNAL_FLAG=0x2000; - + + // Throttle Info Access functions built by exrail macros + static const byte rosterNameCount; + static const int16_t FLASH routeIdList[]; + static const int16_t FLASH rosterIdList[]; + static const FSH * getRouteDescription(int16_t id); + static const FSH * getTurnoutDescription(int16_t id); + static const FSH * getRosterName(int16_t id); + static const FSH * getRosterFunctions(int16_t id); + private: static void ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]); static bool parseSlash(Print * stream, byte & paramCount, int16_t p[]) ; @@ -106,8 +110,6 @@ private: static bool getFlag(VPIN id,byte mask); static int16_t progtrackLocoId; static void doSignal(VPIN id,bool red, bool amber, bool green); - static void emitRouteDescription(Print * stream, char type, int id, const FSH * description); - static void emitWithrottleDescriptions(Print * stream); static RMFT2 * loopTask; static RMFT2 * pausingTask; @@ -132,6 +134,7 @@ private: static LookList * onActivateLookup; static LookList * onDeactivateLookup; + // Local variables - exist for each instance/task RMFT2 *next; // loop chain int progCounter; // Byte offset of next route opcode in ROUTES table diff --git a/EXRAILMacros.h b/EXRAILMacros.h index e0b078c..60fd557 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -49,24 +49,38 @@ // CAUTION: The macros below are multiple passed over myAutomation.h +#define O_DESC(id, desc) case id: return ("" desc)[0]?F("" desc):NULL; + // Pass 1 Implements aliases #include "EXRAIL2MacroReset.h" #undef ALIAS #define ALIAS(name,value...) const int name= 1##value##0 ==10 ? -__COUNTER__ : value##0/10; #include "myAutomation.h" -// Pass 2 convert descriptions to withrottle format emitter function +// Pass 2 create throttle route list #include "EXRAIL2MacroReset.h" #undef ROUTE -#define ROUTE(id, description) emitRouteDescription(stream,'R',id,F(description)); +#define ROUTE(id, description) id, #undef AUTOMATION -#define AUTOMATION(id, description) emitRouteDescription(stream,'A',id,F(description)); -void RMFT2::emitWithrottleDescriptions(Print * stream) { - (void)stream; +#define AUTOMATION(id, description) -id, +const int16_t FLASH RMFT2::routeIdList[]= { #include "myAutomation.h" + 0}; + +// Pass 3 Create route descriptions: +#undef ROUTE +#define ROUTE(id, description) case id: return F(description); +#undef AUTOMATION +#define AUTOMATION(id, description) case id: return F(description); +const FSH * RMFT2::getRouteDescription(int16_t id) { + switch(id) { + #include "myAutomation.h" + default: break; + } + return NULL; } -// Pass 3... Create Text sending functions +// Pass 4... Create Text sending functions #include "EXRAIL2MacroReset.h" const int StringMacroTracker1=__COUNTER__; #undef BROADCAST @@ -96,58 +110,63 @@ void RMFT2::printMessage(uint16_t id) { } -// Pass 4: Turnout descriptions (optional) +// Pass 5: Turnout descriptions (optional) #include "EXRAIL2MacroReset.h" #undef TURNOUT -#define TURNOUT(id,addr,subaddr,description...) case id: desc=F("" description); break; +#define TURNOUT(id,addr,subaddr,description...) O_DESC(id,description) #undef PIN_TURNOUT -#define PIN_TURNOUT(id,pin,description...) case id: desc=F("" description); break; +#define PIN_TURNOUT(id,pin,description...) O_DESC(id,description) #undef SERVO_TURNOUT -#define SERVO_TURNOUT(id,pin,activeAngle,inactiveAngle,profile,description...) case id: desc=F("" description); break; +#define SERVO_TURNOUT(id,pin,activeAngle,inactiveAngle,profile,description...) O_DESC(id,description) #undef VIRTUAL_TURNOUT -#define VIRTUAL_TURNOUT(id,description...) case id: desc=F("" description); break; +#define VIRTUAL_TURNOUT(id,description...) O_DESC(id,description) -void RMFT2::emitTurnoutDescription(Print* stream,int16_t turnoutid) { - const FSH * desc=F(""); +const FSH * RMFT2::getTurnoutDescription(int16_t turnoutid) { switch (turnoutid) { #include "myAutomation.h" - default: break; + default:break; } - if (GETFLASH(desc)=='\0') desc=F("%d"); - StringFormatter::send(stream,desc,turnoutid); + return NULL; } -// Pass 5: Roster names (count) +// Pass 6: Roster IDs (count) #include "EXRAIL2MacroReset.h" #undef ROSTER #define ROSTER(cabid,name,funcmap...) +1 - const byte RMFT2::rosterNameCount=0 - #include "myAutomation.h" - ; - -// Pass 6: Roster names emitter + #include "myAutomation.h" + ; + +// Pass 6: Roster IDs #include "EXRAIL2MacroReset.h" #undef ROSTER -#define ROSTER(cabid,name,funcmap...) StringFormatter::send(stream,(FSH *)format,F(name),cabid,cabid<128?'S':'L'); -void RMFT2::emitWithrottleRoster(Print * stream) { - static const char format[] FLASH ="]\\[%S}|{%d}|{%c"; - (void)format; - StringFormatter::send(stream,F("RL%d"), rosterNameCount); - #include "myAutomation.h" - stream->write('\n'); -} +#define ROSTER(cabid,name,funcmap...) cabid, +const int16_t FLASH RMFT2::rosterIdList[]={ + #include "myAutomation.h" + 0}; -// Pass 7: functions getter +// Pass 7: Roster names getter #include "EXRAIL2MacroReset.h" #undef ROSTER -#define ROSTER(cabid,name,funcmap...) case cabid: return F("" funcmap); -const FSH * RMFT2::getRosterFunctions(int16_t cabid) { - switch(cabid) { +#define ROSTER(cabid,name,funcmap...) case cabid: return F(name); +const FSH * RMFT2::getRosterName(int16_t id) { + switch(id) { #include "myAutomation.h" - default: return NULL; - } -} + default: return NULL; + } + return NULL; +} + +// Pass to get roster functions +#undef ROSTER +#define ROSTER(cabid,name,funcmap...) O_DESC(cabid,funcmap) +const FSH * RMFT2::getRosterFunctions(int16_t id) { + switch(id) { + #include "myAutomation.h" + default: break; + } + return NULL; +} // Pass 8 Signal definitions #include "EXRAIL2MacroReset.h" diff --git a/WiThrottle.cpp b/WiThrottle.cpp index d91745c..72ee75a 100644 --- a/WiThrottle.cpp +++ b/WiThrottle.cpp @@ -120,13 +120,15 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) { StringFormatter::send(stream,F("PTL")); for(Turnout *tt=Turnout::first();tt!=NULL;tt=tt->next()){ int id=tt->getId(); - StringFormatter::send(stream,F("]\\[%d}|{"), id); -#ifdef EXRAIL_ACTIVE - RMFT2::emitTurnoutDescription(stream,id); -#else - StringFormatter::send(stream,F("%d"), id); -#endif - StringFormatter::send(stream,F("}|{%c"), Turnout::isClosed(id)?'2':'4'); + const FSH * tdesc=NULL; + #ifdef EXRAIL_ACTIVE + tdesc=RMFT2::getTurnoutDescription(id); + #endif + char tchar=Turnout::isClosed(id)?'2':'4'; + if (tdesc==NULL) // turnout with no description + StringFormatter::send(stream,F("]\\[%d}|{T%d}|{T%c"), id,id,tchar); + else if (GETFLASH(tdesc)!='*') // ignore hidden turnouts + StringFormatter::send(stream,F("]\\[%d}|{%S}|{%c"), id,tdesc,tchar); } StringFormatter::send(stream,F("\n")); turnoutListHash = Turnout::turnoutlistHash; // keep a copy of hash for later comparison @@ -136,7 +138,17 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) { // Send EX-RAIL routes list if not already sent (but not at same time as turnouts above) exRailSent=true; #ifdef EXRAIL_ACTIVE - RMFT2::emitWithrottleRouteList(stream); + StringFormatter::send(stream,F("PRT]\\[Routes}|{Route]\\[Set}|{2]\\[Handoff}|{4\nPRL")); + for (int ix=0;;ix+=2) { + int16_t id=GETFLASHW(RMFT2::routeIdList+ix); + if (id==0) break; + bool isRoute=id<0; + if (isRoute) id=-id; + const FSH * desc=RMFT2::getRouteDescription(id); + StringFormatter::send(stream,F("]\\[%c%d}|{%S}|{%c"), + isRoute?'R':'A',id,desc, isRoute?'2':'4'); + } + StringFormatter::send(stream,F("\n")); #endif // allow heartbeat to slow down once all metadata sent StringFormatter::send(stream,F("*%d\n"),HEARTBEAT_SECONDS); @@ -205,9 +217,19 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) { StringFormatter::send(stream,F("HtDCC-EX v%S, %S, %S, %S\n"), F(VERSION), F(ARDUINO_TYPE), DCC::getMotorShieldName(), F(GITHUB_SHA)); StringFormatter::send(stream,F("PTT]\\[Turnouts}|{Turnout]\\[THROW}|{2]\\[CLOSE}|{4\n")); StringFormatter::send(stream,F("PPA%x\n"),DCCWaveform::mainTrack.getPowerMode()==POWERMODE::ON); + + // Send the roster #ifdef EXRAIL_ACTIVE - RMFT2::emitWithrottleRoster(stream); -#endif + StringFormatter::send(stream,F("RL%d"), RMFT2::rosterNameCount); + for (int16_t r=0;rwrite('\n'); // end roster +#endif + + // set heartbeat to 1 second because we need to sync the metadata StringFormatter::send(stream,F("*1\n")); initSent = true; From 5846e0fe2384f159927fcfc691ddd3839188ca4a Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 8 Apr 2022 11:41:50 +0100 Subject: [PATCH 2/7] J command parsing JA JR JT commands parsed EXRAIL sets hidden turnout state HIDDEN description macro Turnouts hidden flag bit UNO seems OK, MEGA UNTESTED --- DCCEXParser.cpp | 66 +++++++++++++++++++++++++++++++++++++++++++++++-- DCCEXParser.h | 1 + EXRAIL2.cpp | 10 +++++--- EXRAIL2.h | 3 ++- EXRAILMacros.h | 4 +++ Turnouts.h | 12 ++++++--- 6 files changed, 86 insertions(+), 10 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 11a2796..bc2e759 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -37,6 +37,7 @@ #include "CommandDistributor.h" #include "EEStore.h" #include "DIAG.h" +#include "EXRAIL2.h" #include //////////////////////////////////////////////////////////////////////////////// @@ -74,8 +75,10 @@ const int16_t HASH_KEYWORD_SPEED28 = -17064; const int16_t HASH_KEYWORD_SPEED128 = 25816; const int16_t HASH_KEYWORD_SERVO=27709; const int16_t HASH_KEYWORD_VPIN=-415; -const int16_t HASH_KEYWORD_C=67; -const int16_t HASH_KEYWORD_T=84; +const int16_t HASH_KEYWORD_A='A'; +const int16_t HASH_KEYWORD_C='C'; +const int16_t HASH_KEYWORD_R='R'; +const int16_t HASH_KEYWORD_T='T'; const int16_t HASH_KEYWORD_LCN = 15137; const int16_t HASH_KEYWORD_HAL = 10853; const int16_t HASH_KEYWORD_SHOW = -21309; @@ -509,6 +512,57 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) } break; + case 'J' : // throttle info access + { + if ((params<1) | (params>2)) break; // + int16_t id=(params==2)?p[1]:0; + switch(p[0]) { + case HASH_KEYWORD_A: // returns automations/routes + StringFormatter::send(stream, F("\n")); + return; + case HASH_KEYWORD_R: // returns rosters + StringFormatter::send(stream, F("\n")); + return; + case HASH_KEYWORD_T: // returns turnout list + StringFormatter::send(stream, F(" + for ( Turnout * t=Turnout::first(); t; t=t->next()) { + if (t->isHidden()) continue; + StringFormatter::send(stream, F(" %c%d"), + t->isThrown()?'-':' ',t->getId()); + } + } + else { // + Turnout * t=Turnout::get(id); + if (t && !t->isHidden()) + StringFormatter::send(stream, F(" %d \"%S\""), + id, +#ifdef EXRAIL_ACTIVE + RMFT2::getTurnoutDescription(id) +#else + F("") +#endif + ); + } + StringFormatter::send(stream, F(">\n")); + return; + default: break; + } // switch(p[1]) + break; // case J + } + default: //anything else will diagnose and drop out to DIAG(F("Opcode=%c params=%d"), opcode, params); for (int i = 0; i < params; i++) @@ -521,6 +575,14 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) StringFormatter::send(stream, F("\n")); } +void DCCEXParser::sendFlashList(Print * stream,const int16_t flashList[]) { + for (int16_t i=0;;i+=2) { + int16_t value=GETFLASHW(flashList+i); + if (value==0) return; + StringFormatter::send(stream,F(" %d"),value); + } +} + bool DCCEXParser::parseZ(Print *stream, int16_t params, int16_t p[]) { diff --git a/DCCEXParser.h b/DCCEXParser.h index 5761eb0..227e3f0 100644 --- a/DCCEXParser.h +++ b/DCCEXParser.h @@ -70,6 +70,7 @@ struct DCCEXParser static FILTER_CALLBACK filterRMFTCallback; static AT_COMMAND_CALLBACK atCommandCallback; static void funcmap(int16_t cab, byte value, byte fstart, byte fstop); + static void sendFlashList(Print * stream,const int16_t flashList[]); }; diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 1a4f536..583bbd9 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -196,7 +196,7 @@ int16_t LookList::find(int16_t value) { VPIN id=operand; int addr=GET_OPERAND(1); byte subAddr=GET_OPERAND(2); - DCCTurnout::create(id,addr,subAddr); + setTurnoutHiddenState(DCCTurnout::create(id,addr,subAddr)); break; } @@ -206,14 +206,14 @@ int16_t LookList::find(int16_t value) { int activeAngle=GET_OPERAND(2); int inactiveAngle=GET_OPERAND(3); int profile=GET_OPERAND(4); - ServoTurnout::create(id,pin,activeAngle,inactiveAngle,profile); + setTurnoutHiddenState(ServoTurnout::create(id,pin,activeAngle,inactiveAngle,profile)); break; } case OPCODE_PINTURNOUT: { VPIN id=operand; VPIN pin=GET_OPERAND(1); - VpinTurnout::create(id,pin); + setTurnoutHiddenState(VpinTurnout::create(id,pin)); break; } @@ -257,6 +257,10 @@ int16_t LookList::find(int16_t value) { new RMFT2(0); // add the startup route } +void RMFT2::setTurnoutHiddenState(Turnout * t) { + t->setHidden(GETFLASH(getTurnoutDescription(t->getId()))==0x01); +} + // This filter intercepts <> commands to do the following: // - Implement RMFT specific commands/diagnostics // - Reject/modify JMRI commands that would interfere with RMFT processing diff --git a/EXRAIL2.h b/EXRAIL2.h index 97e0d98..2d3807b 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -22,6 +22,7 @@ #define EXRAIL2_H #include "FSH.h" #include "IODevice.h" +#include "Turnouts.h" // The following are the operation codes (or instructions) for a kind of virtual machine. // Each instruction is normally 3 bytes long with an operation code followed by a parameter. @@ -110,7 +111,7 @@ private: static bool getFlag(VPIN id,byte mask); static int16_t progtrackLocoId; static void doSignal(VPIN id,bool red, bool amber, bool green); - + static void setTurnoutHiddenState(Turnout * t); static RMFT2 * loopTask; static RMFT2 * pausingTask; void delayMe(long millisecs); diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 60fd557..07405fe 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -49,7 +49,11 @@ // CAUTION: The macros below are multiple passed over myAutomation.h + +// helper macro for turnout descriptions, creates NULL for missing description #define O_DESC(id, desc) case id: return ("" desc)[0]?F("" desc):NULL; +// helper macro for turnout description as HIDDEN +#define HIDDEN "\0x01" // Pass 1 Implements aliases #include "EXRAIL2MacroReset.h" diff --git a/Turnouts.h b/Turnouts.h index e394c85..3d1f0bc 100644 --- a/Turnouts.h +++ b/Turnouts.h @@ -3,7 +3,7 @@ * © 2021 M Steve Todd * © 2021 Fred Decker * © 2020-2021 Harald Barth - * © 2020-2021 Chris Harlow + * © 2020-2022 Chris Harlow * © 2013-2016 Gregg E. Berman * All rights reserved. * @@ -61,7 +61,8 @@ protected: struct { bool closed : 1; bool _rfu: 2; - uint8_t turnoutType : 5; + bool hidden: 1; + uint8_t turnoutType : 4; }; uint8_t flags; }; @@ -83,6 +84,7 @@ protected: _turnoutData.id = id; _turnoutData.turnoutType = turnoutType; _turnoutData.closed = closed; + _turnoutData.hidden=false; add(this); } @@ -104,11 +106,11 @@ protected: * Static functions */ - static Turnout *get(uint16_t id); static void add(Turnout *tt); public: + static Turnout *get(uint16_t id); /* * Static data */ @@ -120,6 +122,8 @@ public: */ inline bool isClosed() { return _turnoutData.closed; }; inline bool isThrown() { return !_turnoutData.closed; } + inline bool isHidden() { return _turnoutData.hidden; } + inline void setHidden(bool h) { _turnoutData.hidden=h; } inline bool isType(uint8_t type) { return _turnoutData.turnoutType == type; } inline uint16_t getId() { return _turnoutData.id; } inline Turnout *next() { return _nextTurnout; } @@ -169,7 +173,7 @@ public: #endif static void printAll(Print *stream) { for (Turnout *tt = _firstTurnout; tt != 0; tt = tt->_nextTurnout) - StringFormatter::send(stream, F("\n"),tt->getId(), tt->isThrown()); + if (!tt->isHidden()) StringFormatter::send(stream, F("\n"),tt->getId(), tt->isThrown()); } From bfb88bb30ae75ce9bad5af129745f654b5ed0142 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 8 Apr 2022 16:13:15 +0100 Subject: [PATCH 3/7] Assist notes draft & syntax tweaks --- DCCEXParser.cpp | 27 +++++++++++----- EXRAIL2.cpp | 15 ++++++++- EXRAIL2.h | 2 ++ EXRAILMacros.h | 9 ++++-- Release_Notes/ThrottleAssists.md | 55 ++++++++++++++++++++++++++++++++ WiThrottle.cpp | 17 +++++----- 6 files changed, 106 insertions(+), 19 deletions(-) create mode 100644 Release_Notes/ThrottleAssists.md diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index bc2e759..b0e9801 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -519,11 +519,23 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) switch(p[0]) { case HASH_KEYWORD_A: // returns automations/routes StringFormatter::send(stream, F(" #ifdef EXRAIL_ACTIVE - if (params==1) sendFlashList(stream,RMFT2::routeIdList); - else StringFormatter::send(stream,F(" %d \"%S\""), - id, RMFT2::getRouteDescription(id)); -#endif + sendFlashList(stream,RMFT2::automationIdList); + sendFlashList(stream,RMFT2::routeIdList); +#endif + } + else { // + StringFormatter::send(stream,F(" %d %c \"%S\""), + id, +#ifdef EXRAIL_ACTIVE + RMFT2::getRouteType(id), // A/R + RMFT2::getRouteDescription(id) +#else + 'X',F("") +#endif + ); + } StringFormatter::send(stream, F(">\n")); return; case HASH_KEYWORD_R: // returns rosters @@ -540,15 +552,14 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) if (params==1) { // for ( Turnout * t=Turnout::first(); t; t=t->next()) { if (t->isHidden()) continue; - StringFormatter::send(stream, F(" %c%d"), - t->isThrown()?'-':' ',t->getId()); + StringFormatter::send(stream, F(" %d"),t->getId()); } } else { // Turnout * t=Turnout::get(id); if (t && !t->isHidden()) - StringFormatter::send(stream, F(" %d \"%S\""), - id, + StringFormatter::send(stream, F(" %d %c \"%S\""), + id,t->isThrown()?'T':'C', #ifdef EXRAIL_ACTIVE RMFT2::getTurnoutDescription(id) #else diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 583bbd9..55be05a 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -260,7 +260,20 @@ int16_t LookList::find(int16_t value) { void RMFT2::setTurnoutHiddenState(Turnout * t) { t->setHidden(GETFLASH(getTurnoutDescription(t->getId()))==0x01); } - + +char RMFT2::getRouteType(int16_t id) { + for (int16_t i=0;;i+=2) { + int16_t rid= GETFLASHW(routeIdList+i); + if (rid==id) return 'R'; + if (rid==0) break; + } + for (int16_t i=0;;i+=2) { + int16_t rid= GETFLASHW(automationIdList+i); + if (rid==id) return 'A'; + if (rid==0) break; + } + return 'X'; +} // This filter intercepts <> commands to do the following: // - Implement RMFT specific commands/diagnostics // - Reject/modify JMRI commands that would interfere with RMFT processing diff --git a/EXRAIL2.h b/EXRAIL2.h index 2d3807b..616b05d 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -97,8 +97,10 @@ class LookList { // Throttle Info Access functions built by exrail macros static const byte rosterNameCount; static const int16_t FLASH routeIdList[]; + static const int16_t FLASH automationIdList[]; static const int16_t FLASH rosterIdList[]; static const FSH * getRouteDescription(int16_t id); + static char getRouteType(int16_t id); static const FSH * getTurnoutDescription(int16_t id); static const FSH * getRosterName(int16_t id); static const FSH * getRosterFunctions(int16_t id); diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 07405fe..c11bd35 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -65,11 +65,16 @@ #include "EXRAIL2MacroReset.h" #undef ROUTE #define ROUTE(id, description) id, -#undef AUTOMATION -#define AUTOMATION(id, description) -id, const int16_t FLASH RMFT2::routeIdList[]= { #include "myAutomation.h" 0}; +// Pass 2a create throttle automation list +#include "EXRAIL2MacroReset.h" +#undef AUTOMATION +#define AUTOMATION(id, description) id, +const int16_t FLASH RMFT2::automationIdList[]= { + #include "myAutomation.h" + 0}; // Pass 3 Create route descriptions: #undef ROUTE diff --git a/Release_Notes/ThrottleAssists.md b/Release_Notes/ThrottleAssists.md new file mode 100644 index 0000000..41d0250 --- /dev/null +++ b/Release_Notes/ThrottleAssists.md @@ -0,0 +1,55 @@ +Throttle Assist updates for versiuon 4.? + +Chris Harlow April 2022 + +There are a number of additional throttle information commands that have been implemented to assist throttle authors to obtain information from the Command Station in order to implement turnout, route/automation and roster features which are already found in the Withrottle implementations. +These commands are new and not overlapped with the existing commands which are probabaly due to be obsoleted as they are over complex and unfit for purpose. + +Turnouts: + +The conventional turnout definition commands and the `````` responses do not contain information about the turnout description which may have been provided in an EXRAIL script. A turnout description is much more user friendly than T123 and having a list helps the throttle UI build a suitable set of buttons. + +`````` command returns a list of turnout ids. The throttle should be uninterested in the turnout technology used but needs to know the ids it can throw/close and monitor the current state. +e.g. response `````` + +````` requests info on turnout 17. +e.g. response `````` or `````` +(T=thrown, C=closed) +or `````` indicating turnout description not given. +or `````` indicating turnout unknown. + +Note: It is still the throttles responsibility to monitor the status broadcasts. + (TBD I'm thinking that the existing broadcast is messy and needs cleaning up) + However, I'm not keen on dynamically created/deleted turnouts so I have no intention of providing a command that indicates the turnout list has been updated since the throttle started. + Also note that turnouts marked in EXRAIL with the HIDDEN keyword instead of a "description" will NOT show up in these commands. + + + Automations/Routes + + A throttle need to know which EXRAIL Automations and Routes it can show the user. + + `````` Returns a list of Automations/Routes + e.g. `````` + Indicates route/automation ids. + Information on each route needs to be obtained by + `````` + returns e.g. `````` for a route + or `````` for an automation. + + Whats the difference: + A Route is just a call to an EXRAIL ROUTE, traditionally to set some turnouts or signals but can be used to perform any kind of EXRAIL function... but its not expecting to know the loco. + Thus a route can be triggered by sending in for example ``````. + + An Automation is a handoff of the last accessed loco id to an EXRAIL AUTOMATION which would typically drive the loco away. + Thus an Automation expects a start command with a cab id + e.g. `````` + + + Roster Information: + The `````` command requests a list of cab ids from the roster. + e.g. responding `````` + or for none. + + Each Roster entry had a name and descripotion + + diff --git a/WiThrottle.cpp b/WiThrottle.cpp index 72ee75a..7459a9c 100644 --- a/WiThrottle.cpp +++ b/WiThrottle.cpp @@ -139,14 +139,15 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) { exRailSent=true; #ifdef EXRAIL_ACTIVE StringFormatter::send(stream,F("PRT]\\[Routes}|{Route]\\[Set}|{2]\\[Handoff}|{4\nPRL")); - for (int ix=0;;ix+=2) { - int16_t id=GETFLASHW(RMFT2::routeIdList+ix); - if (id==0) break; - bool isRoute=id<0; - if (isRoute) id=-id; - const FSH * desc=RMFT2::getRouteDescription(id); - StringFormatter::send(stream,F("]\\[%c%d}|{%S}|{%c"), - isRoute?'R':'A',id,desc, isRoute?'2':'4'); + for (byte pass=0;pass<2;pass++) { + + for (int ix=0;;ix+=2) { + int16_t id=GETFLASHW((pass?RMFT2::routeIdList:RMFT2::automationIdList)+ix); + if (id==0) break; + const FSH * desc=RMFT2::getRouteDescription(id); + StringFormatter::send(stream,F("]\\[%c%d}|{%S}|{%c"), + pass?'A':'R',id,desc, pass?'4':'2'); + } } StringFormatter::send(stream,F("\n")); #endif From b41ca2f44a9074507331fdfaa4137f0184849907 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 12 Apr 2022 17:05:44 +0100 Subject: [PATCH 4/7] Throttle notes --- Release_Notes/ThrottleAssists.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Release_Notes/ThrottleAssists.md b/Release_Notes/ThrottleAssists.md index 41d0250..2d32979 100644 --- a/Release_Notes/ThrottleAssists.md +++ b/Release_Notes/ThrottleAssists.md @@ -35,6 +35,7 @@ Note: It is still the throttles responsibility to monitor the status broadcasts. `````` returns e.g. `````` for a route or `````` for an automation. + or `````` for id not found Whats the difference: A Route is just a call to an EXRAIL ROUTE, traditionally to set some turnouts or signals but can be used to perform any kind of EXRAIL function... but its not expecting to know the loco. @@ -50,6 +51,22 @@ Note: It is still the throttles responsibility to monitor the status broadcasts. e.g. responding `````` or for none. - Each Roster entry had a name and descripotion + Each Roster entry had a name and function map obtained by: + `````` reply like ``` + Refer to EXRAIL ROSTER command for function map format. + + + TODO: + `````` Requests a deliberate update on the cab speed/functions in the same format as the cab broadcast. + + COMMANDS TO AVOID + + `````` Use `````` + `````` + `````` other than `````` + `````` + `````` + + From e13175635c2443d9cdc0d31719cac7492a891a1e Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 12 Apr 2022 17:05:55 +0100 Subject: [PATCH 5/7] uno memory saver --- DCCEXParser.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index b0e9801..4f866c6 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -503,6 +503,7 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) DCC::setFn(p[0], p[1], p[2] == 1); return; +#if WIFI_ON case '+': // Complex Wifi interface command (not usual parse) if (atCommandCallback && !ringStream) { DCCWaveform::mainTrack.setPowerMode(POWERMODE::OFF); @@ -511,6 +512,7 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) return; } break; +#endif case 'J' : // throttle info access { From 20b12bcb7c66c37b0ea6073ce3919324b535a9b7 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 12 Apr 2022 18:47:06 +0100 Subject: [PATCH 6/7] JA JR and --- DCC.cpp | 5 ++++- DCC.h | 5 ++--- DCCEXParser.cpp | 19 ++++++++++++++++--- EXRAIL2MacroReset.h | 4 ++-- EXRAILMacros.h | 10 +++++----- 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index 6674fb9..1c53456 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -647,7 +647,7 @@ byte DCC::cv2(int cv) { return lowByte(cv); } -int DCC::lookupSpeedTable(int locoId) { +int DCC::lookupSpeedTable(int locoId, bool autoCreate) { // determine speed reg for this loco int firstEmpty = MAX_LOCOS; int reg; @@ -655,6 +655,9 @@ int DCC::lookupSpeedTable(int locoId) { if (speedTable[reg].loco == locoId) break; if (speedTable[reg].loco == 0 && firstEmpty == MAX_LOCOS) firstEmpty = reg; } + + // return -1 if not found and not auto creating + if (reg== MAX_LOCOS && !autoCreate) return -1; if (reg == MAX_LOCOS) reg = firstEmpty; if (reg >= MAX_LOCOS) { DIAG(F("Too many locos")); diff --git a/DCC.h b/DCC.h index fbeb603..5d9064c 100644 --- a/DCC.h +++ b/DCC.h @@ -128,7 +128,6 @@ public: static void forgetLoco(int cab); // removes any speed reminders for this loco static void forgetAllLocos(); // removes all speed reminders static void displayCabList(Print *stream); - static FSH *getMotorShieldName(); static inline void setGlobalSpeedsteps(byte s) { globalSpeedsteps = s; @@ -148,7 +147,8 @@ public: unsigned long functions; }; static LOCO speedTable[MAX_LOCOS]; - + static int lookupSpeedTable(int locoId, bool autoCreate=true); + private: static byte joinRelay; static byte loopStatus; @@ -162,7 +162,6 @@ private: static byte cv1(byte opcode, int cv); static byte cv2(int cv); - static int lookupSpeedTable(int locoId); static void issueReminders(); static void callback(int value); diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 4f866c6..11a91d2 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -217,10 +217,23 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) return; // filterCallback asked us to ignore case 't': // THROTTLE { + if (params==1) { // display state + + int16_t slot=DCC::lookupSpeedTable(p[0],false); + if (slot>=0) { + DCC::LOCO * sp=&DCC::speedTable[slot]; + StringFormatter::send(stream,F("\n"), + sp->loco,slot,sp->speedCode,sp->functions); + } + else // send dummy state speed 0 fwd no functions. + StringFormatter::send(stream,F("\n"),p[0]); + return; + } + int16_t cab; int16_t tspeed; int16_t direction; - + if (params == 4) { // cab = p[1]; @@ -523,8 +536,8 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) StringFormatter::send(stream, F(" #ifdef EXRAIL_ACTIVE - sendFlashList(stream,RMFT2::automationIdList); sendFlashList(stream,RMFT2::routeIdList); + sendFlashList(stream,RMFT2::automationIdList); #endif } else { // @@ -589,7 +602,7 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) } void DCCEXParser::sendFlashList(Print * stream,const int16_t flashList[]) { - for (int16_t i=0;;i+=2) { + for (int16_t i=0;;i++) { int16_t value=GETFLASHW(flashList+i); if (value==0) return; StringFormatter::send(stream,F(" %d"),value); diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index e28452a..edbfd6d 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -131,7 +131,7 @@ #define ATGTE(sensor_id,value) #define ATLT(sensor_id,value) #define ATTIMEOUT(sensor_id,timeout_ms) -#define AUTOMATION(id, description) +#define AUTOMATION(id,description) #define AUTOSTART #define BROADCAST(msg) #define CALL(route) @@ -192,7 +192,7 @@ #define RESUME #define RETURN #define REV(speed) -#define ROUTE(id, description) +#define ROUTE(id,description) #define ROSTER(cab,name,funcmap...) #define SENDLOCO(cab,route) #define SEQUENCE(id) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index c11bd35..075a69c 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -86,7 +86,7 @@ const FSH * RMFT2::getRouteDescription(int16_t id) { #include "myAutomation.h" default: break; } - return NULL; + return F(""); } // Pass 4... Create Text sending functions @@ -161,20 +161,20 @@ const int16_t FLASH RMFT2::rosterIdList[]={ const FSH * RMFT2::getRosterName(int16_t id) { switch(id) { #include "myAutomation.h" - default: return NULL; + default: break; } - return NULL; + return F(""); } // Pass to get roster functions #undef ROSTER -#define ROSTER(cabid,name,funcmap...) O_DESC(cabid,funcmap) +#define ROSTER(cabid,name,funcmap...) case cabid: return F("" funcmap); const FSH * RMFT2::getRosterFunctions(int16_t id) { switch(id) { #include "myAutomation.h" default: break; } - return NULL; + return F(""); } // Pass 8 Signal definitions From 28a44060446a59907f9f30408358698dab754a70 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 12 Apr 2022 23:10:29 +0100 Subject: [PATCH 7/7] Subtle corrections --- DCCEXParser.cpp | 16 ++++++++-------- EXRAIL2.cpp | 4 ++-- EXRAILMacros.h | 2 +- Release_Notes/ThrottleAssists.md | 11 +++++++---- WiThrottle.cpp | 9 +++++---- 5 files changed, 23 insertions(+), 19 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 11a91d2..9d0cf5c 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -566,21 +566,21 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) StringFormatter::send(stream, F(" for ( Turnout * t=Turnout::first(); t; t=t->next()) { - if (t->isHidden()) continue; - StringFormatter::send(stream, F(" %d"),t->getId()); + if (t->isHidden()) continue; + StringFormatter::send(stream, F(" %d"),t->getId()); } } else { // Turnout * t=Turnout::get(id); - if (t && !t->isHidden()) - StringFormatter::send(stream, F(" %d %c \"%S\""), - id,t->isThrown()?'T':'C', + if (!t || t->isHidden()) StringFormatter::send(stream, F(" %d X"),id); + else StringFormatter::send(stream, F(" %d %c \"%S\""), + id,t->isThrown()?'T':'C', #ifdef EXRAIL_ACTIVE - RMFT2::getTurnoutDescription(id) + RMFT2::getTurnoutDescription(id) #else - F("") + F("") #endif - ); + ); } StringFormatter::send(stream, F(">\n")); return; diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 55be05a..dbbb038 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -262,12 +262,12 @@ void RMFT2::setTurnoutHiddenState(Turnout * t) { } char RMFT2::getRouteType(int16_t id) { - for (int16_t i=0;;i+=2) { + for (int16_t i=0;;i++) { int16_t rid= GETFLASHW(routeIdList+i); if (rid==id) return 'R'; if (rid==0) break; } - for (int16_t i=0;;i+=2) { + for (int16_t i=0;;i++) { int16_t rid= GETFLASHW(automationIdList+i); if (rid==id) return 'A'; if (rid==0) break; diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 075a69c..8cdbf41 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -53,7 +53,7 @@ // helper macro for turnout descriptions, creates NULL for missing description #define O_DESC(id, desc) case id: return ("" desc)[0]?F("" desc):NULL; // helper macro for turnout description as HIDDEN -#define HIDDEN "\0x01" +#define HIDDEN "\x01" // Pass 1 Implements aliases #include "EXRAIL2MacroReset.h" diff --git a/Release_Notes/ThrottleAssists.md b/Release_Notes/ThrottleAssists.md index 2d32979..9d979c9 100644 --- a/Release_Notes/ThrottleAssists.md +++ b/Release_Notes/ThrottleAssists.md @@ -16,7 +16,7 @@ e.g. response `````` e.g. response `````` or `````` (T=thrown, C=closed) or `````` indicating turnout description not given. -or `````` indicating turnout unknown. +or `````` indicating turnout unknown (or possibly hidden.) Note: It is still the throttles responsibility to monitor the status broadcasts. (TBD I'm thinking that the existing broadcast is messy and needs cleaning up) @@ -57,16 +57,19 @@ Note: It is still the throttles responsibility to monitor the status broadcasts. Refer to EXRAIL ROSTER command for function map format. - TODO: + Obtaining throttle status. `````` Requests a deliberate update on the cab speed/functions in the same format as the cab broadcast. + `````` + Note that a slot of -1 indicates that the cab is not in the reminders table and this comand will not reserve a slot until such time as the cab is throttled. + COMMANDS TO AVOID `````` Use `````` - `````` + `````` Just drop the slot number `````` other than `````` `````` `````` - + diff --git a/WiThrottle.cpp b/WiThrottle.cpp index 7459a9c..29a8c3a 100644 --- a/WiThrottle.cpp +++ b/WiThrottle.cpp @@ -119,6 +119,7 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) { if (turnoutListHash != Turnout::turnoutlistHash) { StringFormatter::send(stream,F("PTL")); for(Turnout *tt=Turnout::first();tt!=NULL;tt=tt->next()){ + if (tt->isHidden()) continue; int id=tt->getId(); const FSH * tdesc=NULL; #ifdef EXRAIL_ACTIVE @@ -127,7 +128,7 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) { char tchar=Turnout::isClosed(id)?'2':'4'; if (tdesc==NULL) // turnout with no description StringFormatter::send(stream,F("]\\[%d}|{T%d}|{T%c"), id,id,tchar); - else if (GETFLASH(tdesc)!='*') // ignore hidden turnouts + else StringFormatter::send(stream,F("]\\[%d}|{%S}|{%c"), id,tdesc,tchar); } StringFormatter::send(stream,F("\n")); @@ -140,9 +141,9 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) { #ifdef EXRAIL_ACTIVE StringFormatter::send(stream,F("PRT]\\[Routes}|{Route]\\[Set}|{2]\\[Handoff}|{4\nPRL")); for (byte pass=0;pass<2;pass++) { - - for (int ix=0;;ix+=2) { - int16_t id=GETFLASHW((pass?RMFT2::routeIdList:RMFT2::automationIdList)+ix); + // first pass automations, second pass routes. + for (int ix=0;;ix++) { + int16_t id=GETFLASHW((pass?RMFT2::automationIdList:RMFT2::routeIdList)+ix); if (id==0) break; const FSH * desc=RMFT2::getRouteDescription(id); StringFormatter::send(stream,F("]\\[%c%d}|{%S}|{%c"),