1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-11-27 01:56:14 +01:00

EXRAIL ROUTE_STATE and ROUTE_CAPTION

This commit is contained in:
Asbelos 2023-11-10 19:25:24 +00:00
parent 2cbcecf9e6
commit a4eabf235e
5 changed files with 348 additions and 291 deletions

View File

@ -781,27 +781,11 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
TrackManager::reportCurrent(stream); // <g limit...limit> TrackManager::reportCurrent(stream); // <g limit...limit>
return; return;
case HASH_KEYWORD_A: // <JA> returns automations/routes case HASH_KEYWORD_A: // <JA> intercepted by EXRAIL// <JA> returns automations/routes
StringFormatter::send(stream, F("<jA")); if (params!=1) break; // <JA>
if (params==1) {// <JA> StringFormatter::send(stream, F("<jA>\n"));
#ifdef EXRAIL_ACTIVE
SENDFLASHLIST(stream,RMFT2::routeIdList)
SENDFLASHLIST(stream,RMFT2::automationIdList)
#endif
}
else { // <JA id>
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; return;
case HASH_KEYWORD_R: // <JR> returns rosters case HASH_KEYWORD_R: // <JR> returns rosters
StringFormatter::send(stream, F("<jR")); StringFormatter::send(stream, F("<jR"));
#ifdef EXRAIL_ACTIVE #ifdef EXRAIL_ACTIVE
@ -1121,7 +1105,6 @@ bool DCCEXParser::parseS(Print *stream, int16_t params, int16_t p[])
bool DCCEXParser::parseC(Print *stream, int16_t params, int16_t p[]) { bool DCCEXParser::parseC(Print *stream, int16_t params, int16_t p[]) {
if (params == 0) if (params == 0)
return false; return false;
bool onOff = (params > 0) && (p[1] == 1 || p[1] == HASH_KEYWORD_ON); // dont care if other stuff or missing... just means off
switch (p[0]) switch (p[0])
{ {
#ifndef DISABLE_PROG #ifndef DISABLE_PROG
@ -1159,6 +1142,8 @@ bool DCCEXParser::parseC(Print *stream, int16_t params, int16_t p[]) {
LCD(0, F("Ack Retry=%d Sum=%d"), p[2], DCCACK::setAckRetry(p[2])); // <D ACK RETRY 2> LCD(0, F("Ack Retry=%d Sum=%d"), p[2], DCCACK::setAckRetry(p[2])); // <D ACK RETRY 2>
} }
} else { } else {
bool onOff = (params > 0) && (p[1] == 1 || p[1] == HASH_KEYWORD_ON); // dont care if other stuff or missing... just means off
DIAG(F("Ack diag %S"), onOff ? F("on") : F("off")); DIAG(F("Ack diag %S"), onOff ? F("on") : F("off"));
Diag::ACK = onOff; Diag::ACK = onOff;
} }

View File

@ -55,22 +55,6 @@
#include "Turntables.h" #include "Turntables.h"
#include "IODevice.h" #include "IODevice.h"
// Command parsing keywords
const int16_t HASH_KEYWORD_EXRAIL=15435;
const int16_t HASH_KEYWORD_ON = 2657;
const int16_t HASH_KEYWORD_START=23232;
const int16_t HASH_KEYWORD_RESERVE=11392;
const int16_t HASH_KEYWORD_FREE=-23052;
const int16_t HASH_KEYWORD_LATCH=1618;
const int16_t HASH_KEYWORD_UNLATCH=1353;
const int16_t HASH_KEYWORD_PAUSE=-4142;
const int16_t HASH_KEYWORD_RESUME=27609;
const int16_t HASH_KEYWORD_KILL=5218;
const int16_t HASH_KEYWORD_ALL=3457;
const int16_t HASH_KEYWORD_ROUTES=-3702;
const int16_t HASH_KEYWORD_RED=26099;
const int16_t HASH_KEYWORD_AMBER=18713;
const int16_t HASH_KEYWORD_GREEN=-31493;
// One instance of RMFT clas is used for each "thread" in the automation. // One instance of RMFT clas is used for each "thread" in the automation.
// Each thread manages a loco on a journey through the layout, and/or may manage a scenery automation. // Each thread manages a loco on a journey through the layout, and/or may manage a scenery automation.
@ -103,9 +87,6 @@ LookList * RMFT2::onOverloadLookup=NULL;
byte * RMFT2::routeStateArray=nullptr; byte * RMFT2::routeStateArray=nullptr;
const FSH * * RMFT2::routeCaptionArray=nullptr; const FSH * * RMFT2::routeCaptionArray=nullptr;
#define GET_OPCODE GETHIGHFLASH(RMFT2::RouteCode,progCounter)
#define SKIPOP progCounter+=3
// getOperand instance version, uses progCounter from instance. // getOperand instance version, uses progCounter from instance.
uint16_t RMFT2::getOperand(byte n) { uint16_t RMFT2::getOperand(byte n) {
return getOperand(progCounter,n); return getOperand(progCounter,n);
@ -146,6 +127,13 @@ void LookList::chain(LookList * chain) {
m_chain=chain; m_chain=chain;
} }
void LookList::stream(Print * _stream) {
for (int16_t i=0;i<m_size;i++) {
_stream->print(" ");
_stream->print(m_lookupArray[i]);
}
}
int16_t LookList::findPosition(int16_t value) { int16_t LookList::findPosition(int16_t value) {
for (int16_t i=0;i<m_size;i++) { for (int16_t i=0;i<m_size;i++) {
if (m_lookupArray[i]==value) return i; if (m_lookupArray[i]==value) return i;
@ -189,9 +177,10 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) {
// create lookups // create lookups
routeLookup=LookListLoader(OPCODE_ROUTE, OPCODE_AUTOMATION); routeLookup=LookListLoader(OPCODE_ROUTE, OPCODE_AUTOMATION);
routeLookup->chain(LookListLoader(OPCODE_SEQUENCE)); routeLookup->chain(LookListLoader(OPCODE_SEQUENCE));
if (compileFeatures && FEATURE_ROUTESTATE) {
routeStateArray=(byte *)calloc(routeLookup->size(),sizeof(byte)); routeStateArray=(byte *)calloc(routeLookup->size(),sizeof(byte));
routeCaptionArray=(const FSH * *)calloc(routeLookup->size(),sizeof(const FSH *)); routeCaptionArray=(const FSH * *)calloc(routeLookup->size(),sizeof(const FSH *));
}
onThrowLookup=LookListLoader(OPCODE_ONTHROW); onThrowLookup=LookListLoader(OPCODE_ONTHROW);
onCloseLookup=LookListLoader(OPCODE_ONCLOSE); onCloseLookup=LookListLoader(OPCODE_ONCLOSE);
onActivateLookup=LookListLoader(OPCODE_ONACTIVATE); onActivateLookup=LookListLoader(OPCODE_ONACTIVATE);
@ -334,238 +323,15 @@ void RMFT2::setTurntableHiddenState(Turntable * tto) {
#endif #endif
char RMFT2::getRouteType(int16_t id) { char RMFT2::getRouteType(int16_t id) {
for (int16_t i=0;;i+=2) { int16_t progCounter=routeLookup->find(id);
int16_t rid= GETHIGHFLASHW(routeIdList,i); if (progCounter>=0) {
if (rid==INT16_MAX) break; OPCODE type=GET_OPCODE;
if (rid==id) return 'R'; if (type==OPCODE_ROUTE) return 'R';
} if (type==OPCODE_AUTOMATION) return 'A';
for (int16_t i=0;;i+=2) {
int16_t rid= GETHIGHFLASHW(automationIdList,i);
if (rid==INT16_MAX) break;
if (rid==id) return 'A';
} }
return 'X'; 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
void RMFT2::ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]) {
(void)stream; // avoid compiler warning if we don't access this parameter
bool reject=false;
switch(opcode) {
case 'D':
if (p[0]==HASH_KEYWORD_EXRAIL) { // <D EXRAIL ON/OFF>
diag = paramCount==2 && (p[1]==HASH_KEYWORD_ON || p[1]==1);
opcode=0;
}
break;
case '/': // New EXRAIL command
reject=!parseSlash(stream,paramCount,p);
opcode=0;
break;
case 'L':
if (compileFeatures & FEATURE_LCC) {
// This entire code block is compiled out if LLC macros not used
if (paramCount==0) { //<L> LCC adapter introducing self
LCCSerial=stream; // now we know where to send events we raise
// loop through all possible sent events
for (int progCounter=0;; SKIPOP) {
byte opcode=GET_OPCODE;
if (opcode==OPCODE_ENDEXRAIL) break;
if (opcode==OPCODE_LCC) StringFormatter::send(stream,F("<LS x%h>\n"),getOperand(progCounter,0));
if (opcode==OPCODE_LCCX) { // long form LCC
StringFormatter::send(stream,F("<LS x%h%h%h%h>\n"),
getOperand(progCounter,1),
getOperand(progCounter,2),
getOperand(progCounter,3),
getOperand(progCounter,0)
);
}}
// we stream the hex events we wish to listen to
// and at the same time build the event index looku.
int eventIndex=0;
for (int progCounter=0;; SKIPOP) {
byte opcode=GET_OPCODE;
if (opcode==OPCODE_ENDEXRAIL) break;
if (opcode==OPCODE_ONLCC) {
onLCCLookup[eventIndex]=progCounter; // TODO skip...
StringFormatter::send(stream,F("<LL %d x%h%h%h:%h>\n"),
eventIndex,
getOperand(progCounter,1),
getOperand(progCounter,2),
getOperand(progCounter,3),
getOperand(progCounter,0)
);
eventIndex++;
}
}
StringFormatter::send(stream,F("<LR>\n")); // Ready to rumble
opcode=0;
break;
}
if (paramCount==1) { // <L eventid> LCC event arrived from adapter
int16_t eventid=p[0];
reject=eventid<0 || eventid>=countLCCLookup;
if (!reject) startNonRecursiveTask(F("LCC"),eventid,onLCCLookup[eventid]);
opcode=0;
}
}
break;
default: // other commands pass through
break;
}
if (reject) {
opcode=0;
StringFormatter::send(stream,F("<X>\n"));
}
}
bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) {
if (paramCount==0) { // STATUS
StringFormatter::send(stream, F("<* EXRAIL STATUS"));
RMFT2 * task=loopTask;
while(task) {
StringFormatter::send(stream,F("\nID=%d,PC=%d,LOCO=%d%c,SPEED=%d%c"),
(int)(task->taskId),task->progCounter,task->loco,
task->invert?'I':' ',
task->speedo,
task->forward?'F':'R'
);
task=task->next;
if (task==loopTask) break;
}
// Now stream the flags
for (int id=0;id<MAX_FLAGS; id++) {
byte flag=flags[id];
if (flag & ~TASK_FLAG & ~SIGNAL_MASK) { // not interested in TASK_FLAG only. Already shown above
StringFormatter::send(stream,F("\nflags[%d] "),id);
if (flag & SECTION_FLAG) StringFormatter::send(stream,F(" RESERVED"));
if (flag & LATCH_FLAG) StringFormatter::send(stream,F(" LATCHED"));
}
}
if (compileFeatures & FEATURE_SIGNAL) {
// do the signals
// flags[n] represents the state of the nth signal in the table
for (int sigslot=0;;sigslot++) {
VPIN sigid=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigslot*8);
if (sigid==0) break; // end of signal list
byte flag=flags[sigslot] & SIGNAL_MASK; // obtain signal flags for this id
StringFormatter::send(stream,F("\n%S[%d]"),
(flag == SIGNAL_RED)? F("RED") : (flag==SIGNAL_GREEN) ? F("GREEN") : F("AMBER"),
sigid & SIGNAL_ID_MASK);
}
}
StringFormatter::send(stream,F(" *>\n"));
return true;
}
switch (p[0]) {
case HASH_KEYWORD_PAUSE: // </ PAUSE>
if (paramCount!=1) return false;
DCC::setThrottle(0,1,true); // pause all locos on the track
pausingTask=(RMFT2 *)1; // Impossible task address
return true;
case HASH_KEYWORD_RESUME: // </ RESUME>
if (paramCount!=1) return false;
pausingTask=NULL;
{
RMFT2 * task=loopTask;
while(task) {
if (task->loco) task->driveLoco(task->speedo);
task=task->next;
if (task==loopTask) break;
}
}
return true;
case HASH_KEYWORD_START: // </ START [cab] route >
if (paramCount<2 || paramCount>3) return false;
{
int route=(paramCount==2) ? p[1] : p[2];
uint16_t cab=(paramCount==2)? 0 : p[1];
int pc=routeLookup->find(route);
if (pc<0) return false;
RMFT2* task=new RMFT2(pc);
task->loco=cab;
}
return true;
default:
break;
}
// check KILL ALL here, otherwise the next validation confuses ALL with a flag
if (p[0]==HASH_KEYWORD_KILL && p[1]==HASH_KEYWORD_ALL) {
while (loopTask) loopTask->kill(F("KILL ALL")); // destructor changes loopTask
return true;
}
// all other / commands take 1 parameter
if (paramCount!=2 ) return false;
switch (p[0]) {
case HASH_KEYWORD_KILL: // Kill taskid|ALL
{
if ( p[1]<0 || p[1]>=MAX_FLAGS) return false;
RMFT2 * task=loopTask;
while(task) {
if (task->taskId==p[1]) {
task->kill(F("KILL"));
return true;
}
task=task->next;
if (task==loopTask) break;
}
}
return false;
case HASH_KEYWORD_RESERVE: // force reserve a section
return setFlag(p[1],SECTION_FLAG);
case HASH_KEYWORD_FREE: // force free a section
return setFlag(p[1],0,SECTION_FLAG);
case HASH_KEYWORD_LATCH:
return setFlag(p[1], LATCH_FLAG);
case HASH_KEYWORD_UNLATCH:
return setFlag(p[1], 0, LATCH_FLAG);
case HASH_KEYWORD_RED:
doSignal(p[1],SIGNAL_RED);
return true;
case HASH_KEYWORD_AMBER:
doSignal(p[1],SIGNAL_AMBER);
return true;
case HASH_KEYWORD_GREEN:
doSignal(p[1],SIGNAL_GREEN);
return true;
default:
return false;
}
}
// This emits Routes and Automations to Withrottle
// 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.
RMFT2::RMFT2(int progCtr) { RMFT2::RMFT2(int progCtr) {
progCounter=progCtr; progCounter=progCtr;
@ -1484,6 +1250,7 @@ void RMFT2::thrungeString(uint32_t strfar, thrunger mode, byte id) {
} }
void RMFT2::manageRouteState(uint16_t id, byte state) { void RMFT2::manageRouteState(uint16_t id, byte state) {
if (compileFeatures && FEATURE_ROUTESTATE) {
// Route state must be maintained for when new throttles connect. // Route state must be maintained for when new throttles connect.
// locate route id in the Routes lookup // locate route id in the Routes lookup
int16_t position=routeLookup->findPosition(id); int16_t position=routeLookup->findPosition(id);
@ -1492,8 +1259,10 @@ void RMFT2::manageRouteState(uint16_t id, byte state) {
if (routeStateArray[position]==state) return; if (routeStateArray[position]==state) return;
routeStateArray[position]=state; routeStateArray[position]=state;
CommandDistributor::broadcastRouteState(id,state); CommandDistributor::broadcastRouteState(id,state);
}
} }
void RMFT2::manageRouteCaption(uint16_t id,const FSH* caption) { void RMFT2::manageRouteCaption(uint16_t id,const FSH* caption) {
if (compileFeatures && FEATURE_ROUTESTATE) {
// Route state must be maintained for when new throttles connect. // Route state must be maintained for when new throttles connect.
// locate route id in the Routes lookup // locate route id in the Routes lookup
int16_t position=routeLookup->findPosition(id); int16_t position=routeLookup->findPosition(id);
@ -1501,6 +1270,8 @@ void RMFT2::manageRouteCaption(uint16_t id,const FSH* caption) {
// set state beside it // set state beside it
if (routeCaptionArray[position]==caption) return; if (routeCaptionArray[position]==caption) return;
routeCaptionArray[position]=caption; routeCaptionArray[position]=caption;
DIAG(F("rCA[%d]=%d, c=%d"),position,routeStateArray[position],caption);
CommandDistributor::broadcastRouteCaption(id,caption); CommandDistributor::broadcastRouteCaption(id,caption);
}
} }

View File

@ -100,6 +100,7 @@ enum thrunger: byte {
static const byte FEATURE_SIGNAL= 0x80; static const byte FEATURE_SIGNAL= 0x80;
static const byte FEATURE_LCC = 0x40; static const byte FEATURE_LCC = 0x40;
static const byte FEATURE_ROSTER= 0x20; static const byte FEATURE_ROSTER= 0x20;
static const byte FEATURE_ROUTESTATE= 0x10;
// Flag bits for status of hardware and TPL // Flag bits for status of hardware and TPL
@ -125,6 +126,7 @@ class LookList {
int16_t find(int16_t value); // finds result value int16_t find(int16_t value); // finds result value
int16_t findPosition(int16_t value); // finds index int16_t findPosition(int16_t value); // finds index
int16_t size(); int16_t size();
void stream(Print * _stream);
private: private:
int16_t m_size; int16_t m_size;
int16_t m_loaded; int16_t m_loaded;
@ -224,7 +226,7 @@ private:
static void manageRouteState(uint16_t id, byte state); static void manageRouteState(uint16_t id, byte state);
static void manageRouteCaption(uint16_t id, const FSH* caption); static void manageRouteCaption(uint16_t id, const FSH* caption);
static byte * routeStateArray; static byte * routeStateArray;
static const FSH * * routeCaptionArray; static const FSH ** routeCaptionArray;
// Local variables - exist for each instance/task // Local variables - exist for each instance/task
RMFT2 *next; // loop chain RMFT2 *next; // loop chain
@ -246,4 +248,8 @@ private:
byte stackDepth; byte stackDepth;
int callStack[MAX_STACK_DEPTH]; int callStack[MAX_STACK_DEPTH];
}; };
#define GET_OPCODE GETHIGHFLASH(RMFT2::RouteCode,progCounter)
#define SKIPOP progCounter+=3
#endif #endif

287
EXRAIL2Parser.cpp Normal file
View File

@ -0,0 +1,287 @@
/*
* © 2021 Neil McKechnie
* © 2021-2023 Harald Barth
* © 2020-2023 Chris Harlow
* © 2022-2023 Colin Murdoch
* All rights reserved.
*
* This file is part of CommandStation-EX
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* It is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
*/
// THIS file is an extension of the RMFT2 class
// normally found in EXRAIL2.cpp
#include <Arduino.h>
#include "defines.h"
#include "EXRAIL2.h"
#include "DCC.h"
// Command parsing keywords
const int16_t HASH_KEYWORD_EXRAIL=15435;
const int16_t HASH_KEYWORD_ON = 2657;
const int16_t HASH_KEYWORD_START=23232;
const int16_t HASH_KEYWORD_RESERVE=11392;
const int16_t HASH_KEYWORD_FREE=-23052;
const int16_t HASH_KEYWORD_LATCH=1618;
const int16_t HASH_KEYWORD_UNLATCH=1353;
const int16_t HASH_KEYWORD_PAUSE=-4142;
const int16_t HASH_KEYWORD_RESUME=27609;
const int16_t HASH_KEYWORD_KILL=5218;
const int16_t HASH_KEYWORD_ALL=3457;
const int16_t HASH_KEYWORD_ROUTES=-3702;
const int16_t HASH_KEYWORD_RED=26099;
const int16_t HASH_KEYWORD_AMBER=18713;
const int16_t HASH_KEYWORD_GREEN=-31493;
const int16_t HASH_KEYWORD_A='A';
// This filter intercepts <> commands to do the following:
// - Implement RMFT specific commands/diagnostics
// - Reject/modify JMRI commands that would interfere with RMFT processing
void RMFT2::ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]) {
(void)stream; // avoid compiler warning if we don't access this parameter
bool reject=false;
switch(opcode) {
case 'D':
if (p[0]==HASH_KEYWORD_EXRAIL) { // <D EXRAIL ON/OFF>
diag = paramCount==2 && (p[1]==HASH_KEYWORD_ON || p[1]==1);
opcode=0;
}
break;
case '/': // New EXRAIL command
reject=!parseSlash(stream,paramCount,p);
opcode=0;
break;
case 'L':
// This entire code block is compiled out if LLC macros not used
if (!(compileFeatures & FEATURE_LCC)) return;
if (paramCount==0) { //<L> LCC adapter introducing self
LCCSerial=stream; // now we know where to send events we raise
// loop through all possible sent events
for (int progCounter=0;; SKIPOP) {
byte opcode=GET_OPCODE;
if (opcode==OPCODE_ENDEXRAIL) break;
if (opcode==OPCODE_LCC) StringFormatter::send(stream,F("<LS x%h>\n"),getOperand(progCounter,0));
if (opcode==OPCODE_LCCX) { // long form LCC
StringFormatter::send(stream,F("<LS x%h%h%h%h>\n"),
getOperand(progCounter,1),
getOperand(progCounter,2),
getOperand(progCounter,3),
getOperand(progCounter,0)
);
}}
// we stream the hex events we wish to listen to
// and at the same time build the event index looku.
int eventIndex=0;
for (int progCounter=0;; SKIPOP) {
byte opcode=GET_OPCODE;
if (opcode==OPCODE_ENDEXRAIL) break;
if (opcode==OPCODE_ONLCC) {
onLCCLookup[eventIndex]=progCounter; // TODO skip...
StringFormatter::send(stream,F("<LL %d x%h%h%h:%h>\n"),
eventIndex,
getOperand(progCounter,1),
getOperand(progCounter,2),
getOperand(progCounter,3),
getOperand(progCounter,0)
);
eventIndex++;
}
}
StringFormatter::send(stream,F("<LR>\n")); // Ready to rumble
opcode=0;
break;
}
if (paramCount==1) { // <L eventid> LCC event arrived from adapter
int16_t eventid=p[0];
reject=eventid<0 || eventid>=countLCCLookup;
if (!reject) startNonRecursiveTask(F("LCC"),eventid,onLCCLookup[eventid]);
opcode=0;
}
break;
case 'J': // throttle info commands
// This entire code block is compiled out if FEATURE_ROUTESTATE macros not used
if (paramCount<1 || !(compileFeatures & FEATURE_ROUTESTATE)) return;
switch(p[0])
case HASH_KEYWORD_A: // <JA> returns automations/routes
if (paramCount==1) {// <JA>
StringFormatter::send(stream, F("<jA"));
routeLookup->stream(stream);
StringFormatter::send(stream, F(">\n"));
opcode=0;
return;
}
if (paramCount==2) { // <JA id>
uint16_t id=p[1];
StringFormatter::send(stream,F("<jA %d %c \"%S\">\n"),
id, getRouteType(id), getRouteDescription(id));
// Send any non-default button states or captions
int16_t statePos=routeLookup->findPosition(id);
if (statePos>=0) {
if (routeStateArray[statePos])
StringFormatter::send(stream,F("<jB %d %d>\n"), id, routeStateArray[statePos]);
if (routeCaptionArray[statePos])
StringFormatter::send(stream,F("<jB %d \"%S\">\n"), id,routeCaptionArray[statePos]);
}
opcode=0;
return;
}
break;
default: // other commands pass through
break;
}
}
bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) {
if (paramCount==0) { // STATUS
StringFormatter::send(stream, F("<* EXRAIL STATUS"));
RMFT2 * task=loopTask;
while(task) {
StringFormatter::send(stream,F("\nID=%d,PC=%d,LOCO=%d%c,SPEED=%d%c"),
(int)(task->taskId),task->progCounter,task->loco,
task->invert?'I':' ',
task->speedo,
task->forward?'F':'R'
);
task=task->next;
if (task==loopTask) break;
}
// Now stream the flags
for (int id=0;id<MAX_FLAGS; id++) {
byte flag=flags[id];
if (flag & ~TASK_FLAG & ~SIGNAL_MASK) { // not interested in TASK_FLAG only. Already shown above
StringFormatter::send(stream,F("\nflags[%d] "),id);
if (flag & SECTION_FLAG) StringFormatter::send(stream,F(" RESERVED"));
if (flag & LATCH_FLAG) StringFormatter::send(stream,F(" LATCHED"));
}
}
if (compileFeatures & FEATURE_SIGNAL) {
// do the signals
// flags[n] represents the state of the nth signal in the table
for (int sigslot=0;;sigslot++) {
VPIN sigid=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigslot*8);
if (sigid==0) break; // end of signal list
byte flag=flags[sigslot] & SIGNAL_MASK; // obtain signal flags for this id
StringFormatter::send(stream,F("\n%S[%d]"),
(flag == SIGNAL_RED)? F("RED") : (flag==SIGNAL_GREEN) ? F("GREEN") : F("AMBER"),
sigid & SIGNAL_ID_MASK);
}
}
StringFormatter::send(stream,F(" *>\n"));
return true;
}
switch (p[0]) {
case HASH_KEYWORD_PAUSE: // </ PAUSE>
if (paramCount!=1) return false;
DCC::setThrottle(0,1,true); // pause all locos on the track
pausingTask=(RMFT2 *)1; // Impossible task address
return true;
case HASH_KEYWORD_RESUME: // </ RESUME>
if (paramCount!=1) return false;
pausingTask=NULL;
{
RMFT2 * task=loopTask;
while(task) {
if (task->loco) task->driveLoco(task->speedo);
task=task->next;
if (task==loopTask) break;
}
}
return true;
case HASH_KEYWORD_START: // </ START [cab] route >
if (paramCount<2 || paramCount>3) return false;
{
int route=(paramCount==2) ? p[1] : p[2];
uint16_t cab=(paramCount==2)? 0 : p[1];
int pc=routeLookup->find(route);
if (pc<0) return false;
RMFT2* task=new RMFT2(pc);
task->loco=cab;
}
return true;
default:
break;
}
// check KILL ALL here, otherwise the next validation confuses ALL with a flag
if (p[0]==HASH_KEYWORD_KILL && p[1]==HASH_KEYWORD_ALL) {
while (loopTask) loopTask->kill(F("KILL ALL")); // destructor changes loopTask
return true;
}
// all other / commands take 1 parameter
if (paramCount!=2 ) return false;
switch (p[0]) {
case HASH_KEYWORD_KILL: // Kill taskid|ALL
{
if ( p[1]<0 || p[1]>=MAX_FLAGS) return false;
RMFT2 * task=loopTask;
while(task) {
if (task->taskId==p[1]) {
task->kill(F("KILL"));
return true;
}
task=task->next;
if (task==loopTask) break;
}
}
return false;
case HASH_KEYWORD_RESERVE: // force reserve a section
return setFlag(p[1],SECTION_FLAG);
case HASH_KEYWORD_FREE: // force free a section
return setFlag(p[1],0,SECTION_FLAG);
case HASH_KEYWORD_LATCH:
return setFlag(p[1], LATCH_FLAG);
case HASH_KEYWORD_UNLATCH:
return setFlag(p[1], 0, LATCH_FLAG);
case HASH_KEYWORD_RED:
doSignal(p[1],SIGNAL_RED);
return true;
case HASH_KEYWORD_AMBER:
doSignal(p[1],SIGNAL_AMBER);
return true;
case HASH_KEYWORD_GREEN:
doSignal(p[1],SIGNAL_GREEN);
return true;
default:
return false;
}
}

View File

@ -102,6 +102,14 @@ void exrailHalSetup() {
#define LCCX(senderid,eventid) | FEATURE_LCC #define LCCX(senderid,eventid) | FEATURE_LCC
#undef ONLCC #undef ONLCC
#define ONLCC(senderid,eventid) | FEATURE_LCC #define ONLCC(senderid,eventid) | FEATURE_LCC
#undef ROUTE_ACTIVE
#define ROUTE_ACTIVE(id) | FEATURE_ROUTESTATE
#undef ROUTE_INACTIVE
#define ROUTE_INACTIVE(id) | FEATURE_ROUTESTATE
#undef ROUTE_HIDDEN
#define ROUTE_HIDDEN(id) | FEATURE_ROUTESTATE
#undef ROUTE_CAPTION
#define ROUTE_CAPTION(id,caption) | FEATURE_ROUTESTATE
const byte RMFT2::compileFeatures = 0 const byte RMFT2::compileFeatures = 0
#include "myAutomation.h" #include "myAutomation.h"
@ -156,7 +164,7 @@ const int StringMacroTracker1=__COUNTER__;
#undef ROUTE_CAPTION #undef ROUTE_CAPTION
#define ROUTE_CAPTION(id,caption) \ #define ROUTE_CAPTION(id,caption) \
case (__COUNTER__ - StringMacroTracker1) : {\ case (__COUNTER__ - StringMacroTracker1) : {\
CommandDistributor::broadcastRouteCaption(id,F(caption));\ manageRouteCaption(id,F(caption));\
return;\ return;\
} }
#undef SERIAL #undef SERIAL