mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-12-25 05:31:24 +01:00
lookups(1)
Faster runtime lookups at the expense of some ram
This commit is contained in:
parent
d7fd9e1538
commit
4ea458b140
208
RMFT2.cpp
208
RMFT2.cpp
@ -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) {
|
for (progCounter=0;; SKIPOP) {
|
||||||
byte opcode=GET_OPCODE;
|
byte opcode=GET_OPCODE;
|
||||||
if (opcode==OPCODE_ENDEXRAIL) break;
|
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){
|
||||||
|
byte opcode=GET_OPCODE;
|
||||||
|
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) {
|
|
||||||
VPIN red=GET_OPERAND(0);
|
case OPCODE_SIGNAL: {
|
||||||
|
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);
|
|
||||||
byte greenpin=GET_OPERAND(2);
|
|
||||||
// If amberpin is zero, synthesise amber from red+green
|
// If amberpin is zero, synthesise amber from red+green
|
||||||
IODevice::write(redpin,red || (amber && (amberpin==0)));
|
IODevice::write(redpin,red || (amber && (amberpin==0)));
|
||||||
if (amberpin) IODevice::write(amberpin,amber);
|
if (amberpin) IODevice::write(amberpin,amber);
|
||||||
if (greenpin) IODevice::write(greenpin,green || (amber && (amberpin==0)));
|
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) {
|
||||||
|
17
RMFT2.h
17
RMFT2.h
@ -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,6 +108,10 @@ 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
|
||||||
|
Loading…
Reference in New Issue
Block a user