1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2025-01-28 05:03:07 +01:00

EXRAIL upgrades

ESTOPALL, XPOM
ONBLOCKENTER, ONBLOCKEXIT
Bug fixes for direction/speed sync
This commit is contained in:
Asbelos 2025-01-12 13:24:12 +00:00
parent f031add7a0
commit 2503158e32
7 changed files with 118 additions and 68 deletions

View File

@ -64,8 +64,8 @@ Once a new OPCODE is decided upon, update this list.
I, Turntable object command, control, and broadcast
j, Throttle responses
J, Throttle queries
k, Reserved for future use - Potentially Railcom
K, Reserved for future use - Potentially Railcom
k, Block exit (Railcom)
K, Block enter (Railcom)
l, Loco speedbyte/function map broadcast
L, Reserved for LCC interface (implemented in EXRAIL)
m, message to throttles (broadcast output)

View File

@ -88,6 +88,8 @@ LookList * RMFT2::onClockLookup=NULL;
LookList * RMFT2::onRotateLookup=NULL;
#endif
LookList * RMFT2::onOverloadLookup=NULL;
LookList * RMFT2::onBlockEnterLookup=NULL;
LookList * RMFT2::onBlockExitLookup=NULL;
byte * RMFT2::routeStateArray=nullptr;
const FSH * * RMFT2::routeCaptionArray=nullptr;
int16_t * RMFT2::stashArray=nullptr;
@ -132,11 +134,11 @@ int16_t LookList::find(int16_t value) {
void LookList::chain(LookList * chain) {
m_chain=chain;
}
void LookList::handleEvent(const FSH* reason,int16_t id) {
void LookList::handleEvent(const FSH* reason,int16_t id, int16_t loco) {
// New feature... create multiple ONhandlers
for (int i=0;i<m_size;i++)
if (m_lookupArray[i]==id)
RMFT2::startNonRecursiveTask(reason,id,m_resultArray[i]);
RMFT2::startNonRecursiveTask(reason,id,m_resultArray[i],loco);
}
@ -204,6 +206,12 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) {
onRotateLookup=LookListLoader(OPCODE_ONROTATE);
#endif
onOverloadLookup=LookListLoader(OPCODE_ONOVERLOAD);
if (compileFeatures & FEATURE_BLOCK) {
onBlockEnterLookup=LookListLoader(OPCODE_ONBLOCKENTER);
onBlockExitLookup=LookListLoader(OPCODE_ONBLOCKEXIT);
}
// onLCCLookup is not the same so not loaded here.
// Second pass startup, define any turnouts or servos, set signals red
@ -380,7 +388,7 @@ char RMFT2::getRouteType(int16_t id) {
}
RMFT2::RMFT2(int progCtr) {
RMFT2::RMFT2(int progCtr, int16_t _loco) {
progCounter=progCtr;
// get an unused task id from the flags table
@ -393,9 +401,7 @@ RMFT2::RMFT2(int progCtr) {
}
}
delayTime=0;
loco=0;
speedo=0;
forward=true;
loco=_loco;
invert=false;
blinkState=not_blink_task;
stackDepth=0;
@ -413,7 +419,10 @@ RMFT2::RMFT2(int progCtr) {
RMFT2::~RMFT2() {
driveLoco(1); // ESTOP my loco if any
// estop my loco if this is not an ONevent
// (prevents DONE stopping loco at the end of an
// ONBLOCKENTER or ONBLOCKEXIT )
if (loco>0 && this->onEventStartPosition==-1) DCC::setThrottle(loco,1,DCC::getThrottleDirection(loco));
setFlag(taskId,0,TASK_FLAG); // we are no longer using this id
if (next==this)
loopTask=NULL;
@ -429,23 +438,9 @@ RMFT2::~RMFT2() {
void RMFT2::createNewTask(int route, uint16_t cab) {
int pc=routeLookup->find(route);
if (pc<0) return;
RMFT2* task=new RMFT2(pc);
task->loco=cab;
new RMFT2(pc,cab);
}
void RMFT2::driveLoco(byte speed) {
if (loco<=0) return; // Prevent broadcast!
//if (diag) DIAG(F("EXRAIL drive %d %d %d"),loco,speed,forward^invert);
/* TODO.....
power on appropriate track if DC or main if dcc
if (TrackManager::getMainPowerMode()==POWERMODE::OFF) {
TrackManager::setMainPower(POWERMODE::ON);
}
**********/
DCC::setThrottle(loco,speed, forward^invert);
speedo=speed;
}
bool RMFT2::readSensor(uint16_t sensorId) {
// Exrail operands are unsigned but we need the signed version as inserted by the macros.
@ -500,7 +495,7 @@ bool RMFT2::skipIfBlock() {
if (cv & LONG_ADDR_MARKER) { // maker bit indicates long addr
progtrackLocoId = cv ^ LONG_ADDR_MARKER; // remove marker bit to get real long addr
if (progtrackLocoId <= HIGHEST_SHORT_ADDR ) { // out of range for long addr
DIAG(F("Long addr %d <= %d unsupported"), progtrackLocoId, HIGHEST_SHORT_ADDR);
DIAG(F("Long addr %d <= %d unsupported\n"), progtrackLocoId, HIGHEST_SHORT_ADDR);
progtrackLocoId = -1;
}
} else {
@ -508,6 +503,15 @@ bool RMFT2::skipIfBlock() {
}
}
void RMFT2::pause() {
if (loco)
pauseSpeed=DCC::getThrottleSpeedByte(loco);
}
void RMFT2::resume() {
if (loco)
DCC::setThrottle(loco,pauseSpeed & 0x7f, pauseSpeed & 0x80);
}
void RMFT2::loop() {
if (compileFeatures & FEATURE_SENSOR)
EXRAILSensor::checkAll();
@ -572,24 +576,25 @@ void RMFT2::loop2() {
#endif
case OPCODE_REV:
forward = false;
driveLoco(operand);
if (loco) DCC::setThrottle(loco,operand,invert);
break;
case OPCODE_FWD:
forward = true;
driveLoco(operand);
break;
if (loco) DCC::setThrottle(loco,operand,!invert);
break;
case OPCODE_SPEED:
forward=DCC::getThrottleDirection(loco)^invert;
driveLoco(operand);
if (loco) DCC::setThrottle(loco,operand,DCC::getThrottleDirection(loco));
break;
case OPCODE_MOMENTUM:
DCC::setMomentum(loco,operand,getOperand(1));
break;
case OPCODE_ESTOPALL:
DCC::setThrottle(0,1,1); // all locos speed=1
break;
case OPCODE_FORGET:
if (loco!=0) {
DCC::forgetLoco(loco);
@ -599,12 +604,11 @@ void RMFT2::loop2() {
case OPCODE_INVERT_DIRECTION:
invert= !invert;
driveLoco(speedo);
break;
case OPCODE_RESERVE:
if (getFlag(operand,SECTION_FLAG)) {
driveLoco(0);
if (loco) DCC::setThrottle(loco,1,DCC::getThrottleDirection(loco));
delayMe(500);
return;
}
@ -711,6 +715,10 @@ void RMFT2::loop2() {
if (loco) DCC::writeCVByteMain(loco, operand, getOperand(1));
break;
case OPCODE_XPOM:
DCC::writeCVByteMain(operand, getOperand(1), getOperand(2));
break;
case OPCODE_POWEROFF:
TrackManager::setPower(POWERMODE::OFF);
TrackManager::setJoin(false);
@ -747,8 +755,8 @@ void RMFT2::loop2() {
case OPCODE_RESUME:
pausingTask=NULL;
driveLoco(speedo);
for (RMFT2 * t=next; t!=this;t=t->next) if (t->loco >0) t->driveLoco(t->speedo);
resume();
for (RMFT2 * t=next; t!=this;t=t->next) t->resume();
break;
case OPCODE_IF: // do next operand if sensor set
@ -859,8 +867,7 @@ void RMFT2::loop2() {
case OPCODE_DRIVE:
{
byte analogSpeed=IODevice::readAnalogue(operand) *127 / 1024;
if (speedo!=analogSpeed) driveLoco(analogSpeed);
// Non functional but reserved
break;
}
@ -958,8 +965,6 @@ void RMFT2::loop2() {
// which is intended so it can be checked
// from within EXRAIL
loco=progtrackLocoId;
speedo=0;
forward=true;
invert=false;
break;
#endif
@ -981,16 +986,13 @@ void RMFT2::loop2() {
{
int newPc=routeLookup->find(getOperand(1));
if (newPc<0) break;
RMFT2* newtask=new RMFT2(newPc); // create new task
newtask->loco=operand;
new RMFT2(newPc,operand); // create new task
}
break;
case OPCODE_SETLOCO:
{
loco=operand;
speedo=0;
forward=true;
invert=false;
}
break;
@ -1124,7 +1126,8 @@ void RMFT2::loop2() {
case OPCODE_ONROTATE:
#endif
case OPCODE_ONOVERLOAD:
case OPCODE_ONBLOCKENTER:
case OPCODE_ONBLOCKEXIT:
break;
default:
@ -1316,6 +1319,14 @@ void RMFT2::activateEvent(int16_t addr, bool activate) {
else onDeactivateLookup->handleEvent(F("DEACTIVATE"),addr);
}
void RMFT2::blockEvent(int16_t block, int16_t loco, bool entering) {
if (compileFeatures & FEATURE_BLOCK) {
// Hunt for an ONBLOCKENTER/ONBLOCKEXIT for this accessory
if (entering) onBlockEnterLookup->handleEvent(F("BLOCKENTER"),block,loco);
else onBlockExitLookup->handleEvent(F("BLOCKEXIT"),block,loco);
}
}
void RMFT2::changeEvent(int16_t vpin, bool change) {
// Hunt for an ONCHANGE for this sensor
if (change) onChangeLookup->handleEvent(F("CHANGE"),vpin);
@ -1371,11 +1382,11 @@ void RMFT2::killBlinkOnVpin(VPIN pin, uint16_t count) {
}
}
void RMFT2::startNonRecursiveTask(const FSH* reason, int16_t id,int pc) {
void RMFT2::startNonRecursiveTask(const FSH* reason, int16_t id,int pc, uint16_t loco) {
// Check we dont already have a task running this handler
RMFT2 * task=loopTask;
while(task) {
if (task->onEventStartPosition==pc) {
if (task->onEventStartPosition==pc && task->loco==loco) {
DIAG(F("Recursive ON%S(%d)"),reason, id);
return;
}
@ -1383,7 +1394,7 @@ void RMFT2::startNonRecursiveTask(const FSH* reason, int16_t id,int pc) {
if (task==loopTask) break;
}
task=new RMFT2(pc); // new task starts at this instruction
task=new RMFT2(pc,loco); // new task starts at this instruction
task->onEventStartPosition=pc; // flag for recursion detector
}

View File

@ -77,8 +77,10 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,OPCODE_TOGGLE_TURNOUT,
OPCODE_ROUTE_ACTIVE,OPCODE_ROUTE_INACTIVE,OPCODE_ROUTE_HIDDEN,
OPCODE_ROUTE_DISABLED,
OPCODE_STASH,OPCODE_CLEAR_STASH,OPCODE_CLEAR_ALL_STASH,OPCODE_PICKUP_STASH,
OPCODE_ONBUTTON,OPCODE_ONSENSOR,
OPCODE_ONBUTTON,OPCODE_ONSENSOR,
OPCODE_NEOPIXEL,
OPCODE_ONBLOCKENTER,OPCODE_ONBLOCKEXIT,
OPCODE_ESTOPALL,OPCODE_XPOM,
// OPcodes below this point are skip-nesting IF operations
// placed here so that they may be skipped as a group
// see skipIfBlock()
@ -138,6 +140,7 @@ enum SignalType {
static const byte FEATURE_STASH = 0x08;
static const byte FEATURE_BLINK = 0x04;
static const byte FEATURE_SENSOR = 0x02;
static const byte FEATURE_BLOCK = 0x01;
// Flag bits for status of hardware and TPL
@ -164,7 +167,7 @@ class LookList {
int16_t findPosition(int16_t value); // finds index
int16_t size();
void stream(Print * _stream);
void handleEvent(const FSH* reason,int16_t id);
void handleEvent(const FSH* reason,int16_t id, int16_t loco=0);
private:
int16_t m_size;
@ -178,8 +181,7 @@ class LookList {
public:
static void begin();
static void loop();
RMFT2(int progCounter);
RMFT2(int route, uint16_t cab);
RMFT2(int progCounter, int16_t cab=0);
~RMFT2();
static void readLocoCallback(int16_t cv);
static void createNewTask(int route, uint16_t cab);
@ -189,6 +191,7 @@ class LookList {
static void clockEvent(int16_t clocktime, bool change);
static void rotateEvent(int16_t id, bool change);
static void powerEvent(int16_t track, bool overload);
static void blockEvent(int16_t block, int16_t loco, bool entering);
static bool signalAspectEvent(int16_t address, byte aspect );
// Throttle Info Access functions built by exrail macros
static const byte rosterNameCount;
@ -202,7 +205,7 @@ class LookList {
static const FSH * getRosterFunctions(int16_t id);
static const FSH * getTurntableDescription(int16_t id);
static const FSH * getTurntablePositionDescription(int16_t turntableId, uint8_t positionId);
static void startNonRecursiveTask(const FSH* reason, int16_t id,int pc);
static void startNonRecursiveTask(const FSH* reason, int16_t id,int pc, uint16_t loco=0);
static bool readSensor(uint16_t sensorId);
static bool isSignal(int16_t id,char rag);
static SIGNAL_DEFINITION getSignalSlot(int16_t slotno);
@ -226,7 +229,6 @@ private:
static RMFT2 * loopTask;
static RMFT2 * pausingTask;
void delayMe(long millisecs);
void driveLoco(byte speedo);
bool skipIfBlock();
bool readLoco();
void loop2();
@ -235,6 +237,8 @@ private:
void printMessage2(const FSH * msg);
void thrungeString(uint32_t strfar, thrunger mode, byte id=0);
uint16_t getOperand(byte n);
void pause();
void resume();
static bool diag;
static const HIGHFLASH3 byte RouteCode[];
@ -256,6 +260,9 @@ private:
static LookList * onRotateLookup;
#endif
static LookList * onOverloadLookup;
static LookList * onBlockEnterLookup;
static LookList * onBlockExitLookup;
static const int countLCCLookup;
static int onLCCLookup[];
@ -280,9 +287,8 @@ private:
byte taskId;
BlinkState blinkState; // includes AT_TIMEOUT flag.
uint16_t loco;
bool forward;
bool invert;
byte speedo;
byte pauseSpeed;
int onEventStartPosition;
byte stackDepth;
int callStack[MAX_STACK_DEPTH];

View File

@ -61,6 +61,7 @@
#undef ENDIF
#undef ENDTASK
#undef ESTOP
#undef ESTOPALL
#undef EXRAIL
#undef EXTT_TURNTABLE
#undef FADE
@ -112,6 +113,8 @@
#undef ONACTIVATE
#undef ONACTIVATEL
#undef ONAMBER
#undef ONBLOCKENTER
#undef ONBLOCKEXIT
#undef ONDEACTIVATE
#undef ONDEACTIVATEL
#undef ONCLOSE
@ -196,6 +199,7 @@
#undef XFOFF
#undef XFON
#undef XFTOGGLE
#undef XPOM
#undef XREV
#undef XFWD
@ -236,6 +240,7 @@
#define ENDIF
#define ENDTASK
#define ESTOP
#define ESTOPALL
#define EXRAIL
#define EXTT_TURNTABLE(id,vpin,home,description...)
#define FADE(pin,value,ms)
@ -286,6 +291,8 @@
#define ONACTIVATE(addr,subaddr)
#define ONACTIVATEL(linear)
#define ONAMBER(signal_id)
#define ONBLOCKENTER(blockid)
#define ONBLOCKEXIT(blockid)
#define ONTIME(value)
#define ONCLOCKTIME(hours,mins)
#define ONCLOCKMINS(mins)
@ -372,5 +379,5 @@
#define XFTOGGLE(cab,func)
#define XFWD(cab,speed)
#define XREV(cab,speed)
#define XPOM(cab,cv,value)
#endif

View File

@ -210,6 +210,15 @@ void RMFT2::ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16
default:
break;
}
break;
case 'K': // <K blockid loco> Block enter
case 'k': // <k blockid loco> Block exit
if (paramCount!=2) break;
blockEvent(p[0],p[1],opcode=='K');
opcode=0;
break;
default: // other commands pass through
break;
}
@ -228,11 +237,9 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) {
);
}
else {
StringFormatter::send(stream,F("\nID=%d,PC=%d,LOCO=%d%c,SPEED=%d%c"),
StringFormatter::send(stream,F("\nID=%d,PC=%d,LOCO=%d %c"),
(int)(task->taskId),task->progCounter,task->loco,
task->invert?'I':' ',
task->speedo,
task->forward?'F':'R'
task->invert?'I':' '
);
}
task=task->next;
@ -276,6 +283,14 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) {
switch (p[0]) {
case "PAUSE"_hk: // </ PAUSE>
if (paramCount!=1) return false;
{ // pause all tasks
RMFT2 * task=loopTask;
while(task) {
task->pause();
task=task->next;
if (task==loopTask) break;
}
}
DCC::estopAll(); // pause all locos on the track
pausingTask=(RMFT2 *)1; // Impossible task address
return true;
@ -283,12 +298,12 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) {
case "RESUME"_hk: // </ RESUME>
if (paramCount!=1) return false;
pausingTask=NULL;
{
{ // resume all tasks
RMFT2 * task=loopTask;
while(task) {
if (task->loco) task->driveLoco(task->speedo);
task=task->next;
if (task==loopTask) break;
task->resume();
task=task->next;
if (task==loopTask) break;
}
}
return true;
@ -301,8 +316,7 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) {
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;
new RMFT2(pc,cab);
}
return true;

View File

@ -231,6 +231,10 @@ bool exrailHalSetup() {
#define ONBUTTON(vpin) | FEATURE_SENSOR
#undef ONSENSOR
#define ONSENSOR(vpin) | FEATURE_SENSOR
#undef ONBLOCKENTER
#define ONBLOCKENTER(blockid) | FEATURE_BLOCK
#undef ONBLOCKEXIT
#define ONBLOCKEXIT(blockid) | FEATURE_BLOCK
const byte RMFT2::compileFeatures = 0
#include "myAutomation.h"
@ -513,6 +517,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
#define ENDIF OPCODE_ENDIF,0,0,
#define ENDTASK OPCODE_ENDTASK,0,0,
#define ESTOP OPCODE_SPEED,V(1),
#define ESTOPALL OPCODE_ESTOPALL,0,0,
#define EXRAIL
#ifndef IO_NO_HAL
#define EXTT_TURNTABLE(id,vpin,home,description...) OPCODE_EXTTTURNTABLE,V(id),OPCODE_PAD,V(vpin),OPCODE_PAD,V(home),
@ -576,6 +581,8 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
#define ONACTIVATE(addr,subaddr) OPCODE_ONACTIVATE,V(addr<<2|subaddr),
#define ONACTIVATEL(linear) OPCODE_ONACTIVATE,V(linear+3),
#define ONAMBER(signal_id) OPCODE_ONAMBER,V(signal_id),
#define ONBLOCKENTER(block_id) OPCODE_ONBLOCKENTER,V(block_id),
#define ONBLOCKEXIT(block_id) OPCODE_ONBLOCKEXIT,V(block_id),
#define ONCLOSE(turnout_id) OPCODE_ONCLOSE,V(turnout_id),
#define ONLCC(sender,event) OPCODE_ONLCC,V(event),\
OPCODE_PAD,V((((uint64_t)sender)>>32)&0xFFFF),\
@ -669,6 +676,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
#define XFTOGGLE(cab,func) OPCODE_XFTOGGLE,V(cab),OPCODE_PAD,V(func),
#define XFWD(cab,speed) OPCODE_XFWD,V(cab),OPCODE_PAD,V(speed),
#define XREV(cab,speed) OPCODE_XREV,V(cab),OPCODE_PAD,V(speed),
#define XPOM(cab,cv,value) OPCODE_XPOM,V(cab),OPCODE_PAD,V(cv),OPCODE_PAD,V(value),
// Build RouteCode
const int StringMacroTracker2=__COUNTER__;

View File

@ -3,7 +3,11 @@
#include "StringFormatter.h"
#define VERSION "5.5.2"
#define VERSION "5.5.3"
// 5.5.3 - EXRAIL ESTOPALL,XPOM,
// - Bugfix RESERVE to cause ESTOP.(was STOP)
// - Correct direction sync after manual throttle change.
// - plus ONBLOCKENTER/EXIT in preparation for Railcom
// 5.5.2 - DS1307 Real Time clock
// 5.5.1 - Momentum
// 5.5.0 - New version on devel