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

lookups(1)

Faster runtime lookups at the expense of some ram
This commit is contained in:
Asbelos 2021-11-18 10:42:54 +00:00
parent d7fd9e1538
commit 4ea458b140
2 changed files with 171 additions and 68 deletions

220
RMFT2.cpp
View File

@ -16,6 +16,23 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>. * along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
*/ */
/* EXRAILPlus planned FEATURE additions
F1. DCC accessory packet opcodes (short and long form)
F2. ONAccessory catchers
F3. Turnout descriptions for Withrottle
F4. Oled announcements (depends on HAL)
F5. Withrottle roster info
F6. Multi-occupancy semaphore
F7. Self starting sequences
F8. Park/unpark
*/
/* EXRAILPlus planned TRANSPARENT additions
T1. RAM based fast lookup for sequences ON* event catchers and signals.
T2. Extend to >64k
*/
#include <Arduino.h> #include <Arduino.h>
#include "RMFT2.h" #include "RMFT2.h"
#include "DCC.h" #include "DCC.h"
@ -53,67 +70,162 @@ RMFT2 * RMFT2::pausingTask=NULL; // Task causing a PAUSE.
// and all others will have their locos stopped, then resumed after the pausing task resumes. // and all others will have their locos stopped, then resumed after the pausing task resumes.
byte RMFT2::flags[MAX_FLAGS]; byte RMFT2::flags[MAX_FLAGS];
LookList * RMFT2::sequenceLookup=NULL;
LookList * RMFT2::signalLookup=NULL;
LookList * RMFT2::onThrowLookup=NULL;
LookList * RMFT2::onCloseLookup=NULL;
#define GET_OPCODE GETFLASH(RMFT2::RouteCode+progCounter) #define GET_OPCODE GETFLASH(RMFT2::RouteCode+progCounter)
#define GET_OPERAND(n) GETFLASHW(RMFT2::RouteCode+progCounter+1+(n*3)) #define GET_OPERAND(n) GETFLASHW(RMFT2::RouteCode+progCounter+1+(n*3))
#define SKIPOP progCounter+=3 #define SKIPOP progCounter+=3
LookList::LookList(int16_t size) {
m_size=size;
m_loaded=0;
if (size) {
m_lookupArray=new int16_t[size];
m_resultArray=new int32_t[size];
}
}
void LookList::add(int16_t lookup, int32_t result) {
if (m_loaded==m_size) return; // and forget
m_lookupArray[m_loaded]=lookup;
m_resultArray[m_loaded]=result;
m_loaded++;
}
int32_t LookList::find(int16_t value) {
for (int16_t i=0;i<m_size;i++) {
if (m_lookupArray[i]==value) return m_resultArray[i];
}
return -1;
}
/* static */ void RMFT2::begin() { /* static */ void RMFT2::begin() {
DCCEXParser::setRMFTFilter(RMFT2::ComandFilter); DCCEXParser::setRMFTFilter(RMFT2::ComandFilter);
for (int f=0;f<MAX_FLAGS;f++) flags[f]=0; for (int f=0;f<MAX_FLAGS;f++) flags[f]=0;
int progCounter; int progCounter;
// first pass startup, define any turnouts or servos, set signals red and count size.
// counters to create lookup arrays
int sequenceCount=1; // to allow for seq 0 at start
int onThrowCount=0;
int onCloseCount=0;
int signalCount=0;
// first pass count sizes for fast lookup arrays
for (progCounter=0;; SKIPOP) {
byte opcode=GET_OPCODE;
if (opcode==OPCODE_ENDEXRAIL) break;
switch (opcode) {
case OPCODE_SIGNAL:
signalCount++;
break;
case OPCODE_ROUTE:
case OPCODE_AUTOMATION:
case OPCODE_SEQUENCE:
sequenceCount++;
break;
case OPCODE_ONTHROW:
onThrowCount++;
break;
case OPCODE_ONCLOSE:
onCloseCount++;
break;
default: // Ignore
break;
}
}
// create lookups
sequenceLookup=new LookList(sequenceCount);
signalLookup=new LookList(signalCount);
onThrowLookup=new LookList(onThrowCount);
onCloseLookup=new LookList(onCloseCount);
// Second pass startup, define any turnouts or servos, set signals red
// add signals, sequences onRoutines to the lookups
for (progCounter=0;; SKIPOP){ for (progCounter=0;; SKIPOP){
byte opcode=GET_OPCODE; byte opcode=GET_OPCODE;
if (opcode==OPCODE_ENDEXRAIL) break; if (opcode==OPCODE_ENDEXRAIL) break;
VPIN operand=GET_OPERAND(0);
switch (opcode) { switch (opcode) {
case OPCODE_AT: case OPCODE_AT:
case OPCODE_AFTER: case OPCODE_AFTER:
case OPCODE_IF: case OPCODE_IF:
case OPCODE_IFNOT: case OPCODE_IFNOT: {
int16_t pin = (int16_t)GET_OPERAND(0); int16_t pin = (int16_t)operand;
if (pin<0) pin = -pin; if (pin<0) pin = -pin;
IODevice::configureInput((VPIN)pin,true); IODevice::configureInput((VPIN)pin,true);
break;
} }
if (opcode==OPCODE_SIGNAL) { case OPCODE_SIGNAL: {
VPIN red=GET_OPERAND(0); VPIN red=operand;
VPIN amber=GET_OPERAND(1); VPIN amber=GET_OPERAND(1);
VPIN green=GET_OPERAND(2); VPIN green=GET_OPERAND(2);
IODevice::write(red,true); IODevice::write(red,true);
if (amber) IODevice::write(amber,false); if (amber) IODevice::write(amber,false);
IODevice::write(green,false); IODevice::write(green,false);
continue; signalLookup->add(red,progCounter);
break;
} }
if (opcode==OPCODE_TURNOUT) { case OPCODE_TURNOUT: {
VPIN id=GET_OPERAND(0); VPIN id=operand;
int addr=GET_OPERAND(1); int addr=GET_OPERAND(1);
byte subAddr=GET_OPERAND(2); byte subAddr=GET_OPERAND(2);
DCCTurnout::create(id,addr,subAddr); DCCTurnout::create(id,addr,subAddr);
continue; break;
} }
if (opcode==OPCODE_SERVOTURNOUT) { case OPCODE_SERVOTURNOUT: {
int16_t id=GET_OPERAND(0); VPIN id=operand;
VPIN pin=GET_OPERAND(1); VPIN pin=GET_OPERAND(1);
int activeAngle=GET_OPERAND(2); int activeAngle=GET_OPERAND(2);
int inactiveAngle=GET_OPERAND(3); int inactiveAngle=GET_OPERAND(3);
int profile=GET_OPERAND(4); int profile=GET_OPERAND(4);
ServoTurnout::create(id,pin,activeAngle,inactiveAngle,profile); ServoTurnout::create(id,pin,activeAngle,inactiveAngle,profile);
continue; break;
} }
if (opcode==OPCODE_PINTURNOUT) { case OPCODE_PINTURNOUT: {
int16_t id=GET_OPERAND(0); VPIN id=operand;
VPIN pin=GET_OPERAND(1); VPIN pin=GET_OPERAND(1);
VpinTurnout::create(id,pin); VpinTurnout::create(id,pin);
continue; break;
} }
// other opcodes are not needed on this pass
case OPCODE_ROUTE:
case OPCODE_AUTOMATION:
case OPCODE_SEQUENCE:
sequenceLookup->add(operand,progCounter);
break;
case OPCODE_ONTHROW:
onThrowLookup->add(operand,progCounter);
break;
case OPCODE_ONCLOSE:
onCloseLookup->add(operand,progCounter);
break;
default: // Ignore
break;
}
} }
sequenceLookup->add(0,0); // add default start sequence
SKIPOP; // include ENDROUTES opcode SKIPOP; // include ENDROUTES opcode
DIAG(F("EXRAIL %db, MAX_FLAGS=%d"), progCounter,MAX_FLAGS);
DIAG(F("EXRAIL %db, fl=%d seq=%d, sig=%d, onT=%d, onC=%d"),
progCounter,MAX_FLAGS,
sequenceCount, signalCount, onThrowCount, onCloseCount);
new RMFT2(0); // add the startup route new RMFT2(0); // add the startup route
} }
@ -199,7 +311,7 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) {
{ {
int route=(paramCount==2) ? p[1] : p[2]; int route=(paramCount==2) ? p[1] : p[2];
uint16_t cab=(paramCount==2)? 0 : p[1]; uint16_t cab=(paramCount==2)? 0 : p[1];
int pc=locateRouteStart(route); int pc=sequenceLookup->find(route);
if (pc<0) return false; if (pc<0) return false;
RMFT2* task=new RMFT2(pc); RMFT2* task=new RMFT2(pc);
task->loco=cab; task->loco=cab;
@ -313,28 +425,12 @@ RMFT2::~RMFT2() {
} }
void RMFT2::createNewTask(int route, uint16_t cab) { void RMFT2::createNewTask(int route, uint16_t cab) {
int pc=locateRouteStart(route); int pc=sequenceLookup->find(route);
if (pc<0) return; if (pc<0) return;
RMFT2* task=new RMFT2(pc); RMFT2* task=new RMFT2(pc);
task->loco=cab; task->loco=cab;
} }
int RMFT2::locateRouteStart(int16_t _route) {
if (_route==0) return 0; // Route 0 is always start of ROUTES for default startup
for (int progCounter=0;;SKIPOP) {
byte opcode=GET_OPCODE;
if (opcode==OPCODE_ENDEXRAIL) {
DIAG(F("RMFT2 sequence %d not found"), _route);
return -1;
}
if ((opcode==OPCODE_ROUTE || opcode==OPCODE_AUTOMATION || opcode==OPCODE_SEQUENCE)
&& _route==(int)GET_OPERAND(0)) return progCounter;
}
return -1;
}
void RMFT2::driveLoco(byte speed) { void RMFT2::driveLoco(byte speed) {
if (loco<=0) return; // Prevent broadcast! if (loco<=0) return; // Prevent broadcast!
if (diag) DIAG(F("EXRAIL drive %d %d %d"),loco,speed,forward^invert); if (diag) DIAG(F("EXRAIL drive %d %d %d"),loco,speed,forward^invert);
@ -567,7 +663,7 @@ void RMFT2::loop2() {
break; break;
case OPCODE_FOLLOW: case OPCODE_FOLLOW:
progCounter=locateRouteStart(operand); progCounter=sequenceLookup->find(operand);
if (progCounter<0) kill(F("FOLLOW unknown"), operand); if (progCounter<0) kill(F("FOLLOW unknown"), operand);
return; return;
@ -577,7 +673,7 @@ void RMFT2::loop2() {
return; return;
} }
callStack[stackDepth++]=progCounter+3; callStack[stackDepth++]=progCounter+3;
progCounter=locateRouteStart(operand); progCounter=sequenceLookup->find(operand);
if (progCounter<0) kill(F("CALL unknown"),operand); if (progCounter<0) kill(F("CALL unknown"),operand);
return; return;
@ -628,7 +724,7 @@ void RMFT2::loop2() {
case OPCODE_START: case OPCODE_START:
{ {
int newPc=locateRouteStart(operand); int newPc=sequenceLookup->find(operand);
if (newPc<0) break; if (newPc<0) break;
new RMFT2(newPc); new RMFT2(newPc);
} }
@ -636,7 +732,7 @@ void RMFT2::loop2() {
case OPCODE_SENDLOCO: // cab, route case OPCODE_SENDLOCO: // cab, route
{ {
int newPc=locateRouteStart(GET_OPERAND(1)); int newPc=sequenceLookup->find(GET_OPERAND(1));
if (newPc<0) break; if (newPc<0) break;
RMFT2* newtask=new RMFT2(newPc); // create new task RMFT2* newtask=new RMFT2(newPc); // create new task
newtask->loco=operand; newtask->loco=operand;
@ -716,22 +812,23 @@ void RMFT2::kill(const FSH * reason, int operand) {
/* static */ void RMFT2::doSignal(VPIN id,bool red, bool amber, bool green) { /* static */ void RMFT2::doSignal(VPIN id,bool red, bool amber, bool green) {
// CAUTION: hides class member progCounter // CAUTION: hides class member progCounter
for (int progCounter=0;; SKIPOP){ int progCounter=signalLookup->find(id);
byte opcode=GET_OPCODE; if (progCounter<0) return;
if (opcode==OPCODE_ENDEXRAIL) return; VPIN redpin=GET_OPERAND(0);
if (opcode!=OPCODE_SIGNAL) continue; if (redpin!=id) return; // something wrong in lookup
byte redpin=GET_OPERAND(0); VPIN amberpin=GET_OPERAND(1);
if (redpin!=id)continue; VPIN greenpin=GET_OPERAND(2);
byte amberpin=GET_OPERAND(1); // If amberpin is zero, synthesise amber from red+green
byte greenpin=GET_OPERAND(2); IODevice::write(redpin,red || (amber && (amberpin==0)));
// If amberpin is zero, synthesise amber from red+green if (amberpin) IODevice::write(amberpin,amber);
IODevice::write(redpin,red || (amber && (amberpin==0))); if (greenpin) IODevice::write(greenpin,green || (amber && (amberpin==0)));
if (amberpin) IODevice::write(amberpin,amber); }
if (greenpin) IODevice::write(greenpin,green || (amber && (amberpin==0)));
return;
}
}
void RMFT2::turnoutEvent(int16_t turnoutId, bool closed) { void RMFT2::turnoutEvent(int16_t turnoutId, bool closed) {
// Hunt for an ONTHROW/ONCLOSE for this turnout
// caution hides class progCounter;
int progCounter= (closed?onCloseLookup:onThrowLookup)->find(turnoutId);
if (progCounter<0) return;
// Check we dont already have a task running this turnout // Check we dont already have a task running this turnout
RMFT2 * task=loopTask; RMFT2 * task=loopTask;
@ -743,18 +840,9 @@ void RMFT2::kill(const FSH * reason, int operand) {
task=task->next; task=task->next;
if (task==loopTask) break; if (task==loopTask) break;
} }
// Hunt for an ONTHROW/ONCLOSE for this turnout
byte huntFor=closed ? OPCODE_ONCLOSE : OPCODE_ONTHROW ;
// caution hides class progCounter;
for (int progCounter=0;; SKIPOP){
byte opcode=GET_OPCODE;
if (opcode==OPCODE_ENDEXRAIL) return;
if (opcode!=huntFor) continue;
if (turnoutId!=(int16_t)GET_OPERAND(0)) continue;
task=new RMFT2(progCounter); // new task starts at this instruction
task->onTurnoutId=turnoutId; // flag for recursion detector task->onTurnoutId=turnoutId; // flag for recursion detector
return; task=new RMFT2(progCounter); // new task starts at this instruction
}
} }
void RMFT2::printMessage2(const FSH * msg) { void RMFT2::printMessage2(const FSH * msg) {

19
RMFT2.h
View File

@ -58,6 +58,18 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,
static const short MAX_FLAGS=256; static const short MAX_FLAGS=256;
#define FLAGOVERFLOW(x) x>=MAX_FLAGS #define FLAGOVERFLOW(x) x>=MAX_FLAGS
class LookList {
public:
LookList(int16_t size);
void add(int16_t lookup, int32_t result);
int32_t find(int16_t value);
private:
int16_t m_size;
int16_t m_loaded;
int16_t * m_lookupArray;
int32_t * m_resultArray;
};
class RMFT2 { class RMFT2 {
public: public:
static void begin(); static void begin();
@ -75,7 +87,6 @@ private:
static void streamFlags(Print* stream); static void streamFlags(Print* stream);
static void setFlag(VPIN id,byte onMask, byte OffMask=0); static void setFlag(VPIN id,byte onMask, byte OffMask=0);
static bool getFlag(VPIN id,byte mask); static bool getFlag(VPIN id,byte mask);
static int locateRouteStart(int16_t _route);
static int16_t progtrackLocoId; static int16_t progtrackLocoId;
static void doSignal(VPIN id,bool red, bool amber, bool green); 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 emitRouteDescription(Print * stream, char type, int id, const FSH * description);
@ -97,7 +108,11 @@ private:
static bool diag; static bool diag;
static const FLASH byte RouteCode[]; static const FLASH byte RouteCode[];
static byte flags[MAX_FLAGS]; static byte flags[MAX_FLAGS];
static LookList * sequenceLookup;
static LookList * signalLookup;
static LookList * onThrowLookup;
static LookList * onCloseLookup;
// Local variables - exist for each instance/task // Local variables - exist for each instance/task
RMFT2 *next; // loop chain RMFT2 *next; // loop chain
int progCounter; // Byte offset of next route opcode in ROUTES table int progCounter; // Byte offset of next route opcode in ROUTES table