mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-23 08:06:13 +01:00
Merge branch 'ThrottleInfo' into TrackManager
This commit is contained in:
commit
1b0d700009
5
DCC.cpp
5
DCC.cpp
|
@ -630,7 +630,7 @@ byte DCC::cv2(int cv) {
|
||||||
return lowByte(cv);
|
return lowByte(cv);
|
||||||
}
|
}
|
||||||
|
|
||||||
int DCC::lookupSpeedTable(int locoId) {
|
int DCC::lookupSpeedTable(int locoId, bool autoCreate) {
|
||||||
// determine speed reg for this loco
|
// determine speed reg for this loco
|
||||||
int firstEmpty = MAX_LOCOS;
|
int firstEmpty = MAX_LOCOS;
|
||||||
int reg;
|
int reg;
|
||||||
|
@ -638,6 +638,9 @@ int DCC::lookupSpeedTable(int locoId) {
|
||||||
if (speedTable[reg].loco == locoId) break;
|
if (speedTable[reg].loco == locoId) break;
|
||||||
if (speedTable[reg].loco == 0 && firstEmpty == MAX_LOCOS) firstEmpty = reg;
|
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) reg = firstEmpty;
|
||||||
if (reg >= MAX_LOCOS) {
|
if (reg >= MAX_LOCOS) {
|
||||||
DIAG(F("Too many locos"));
|
DIAG(F("Too many locos"));
|
||||||
|
|
9
DCC.h
9
DCC.h
|
@ -84,7 +84,6 @@ public:
|
||||||
static void forgetLoco(int cab); // removes any speed reminders for this loco
|
static void forgetLoco(int cab); // removes any speed reminders for this loco
|
||||||
static void forgetAllLocos(); // removes all speed reminders
|
static void forgetAllLocos(); // removes all speed reminders
|
||||||
static void displayCabList(Print *stream);
|
static void displayCabList(Print *stream);
|
||||||
|
|
||||||
static FSH *getMotorShieldName();
|
static FSH *getMotorShieldName();
|
||||||
static inline void setGlobalSpeedsteps(byte s) {
|
static inline void setGlobalSpeedsteps(byte s) {
|
||||||
globalSpeedsteps = s;
|
globalSpeedsteps = s;
|
||||||
|
@ -98,8 +97,8 @@ public:
|
||||||
unsigned long functions;
|
unsigned long functions;
|
||||||
};
|
};
|
||||||
static LOCO speedTable[MAX_LOCOS];
|
static LOCO speedTable[MAX_LOCOS];
|
||||||
static byte cv1(byte opcode, int cv);
|
static int lookupSpeedTable(int locoId, bool autoCreate=true);
|
||||||
static byte cv2(int cv);
|
|
||||||
private:
|
private:
|
||||||
static byte loopStatus;
|
static byte loopStatus;
|
||||||
static void setThrottle2(uint16_t cab, uint8_t speedCode);
|
static void setThrottle2(uint16_t cab, uint8_t speedCode);
|
||||||
|
@ -110,8 +109,8 @@ private:
|
||||||
static FSH *shieldName;
|
static FSH *shieldName;
|
||||||
static byte globalSpeedsteps;
|
static byte globalSpeedsteps;
|
||||||
|
|
||||||
|
static byte cv1(byte opcode, int cv);
|
||||||
static int lookupSpeedTable(int locoId);
|
static byte cv2(int cv);
|
||||||
static void issueReminders();
|
static void issueReminders();
|
||||||
static void callback(int value);
|
static void callback(int value);
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
#include "DIAG.h"
|
#include "DIAG.h"
|
||||||
#include "TrackManager.h"
|
#include "TrackManager.h"
|
||||||
#include "DCCTimer.h"
|
#include "DCCTimer.h"
|
||||||
|
#include "EXRAIL2.h"
|
||||||
#ifdef HAS_AVR_WDT
|
#ifdef HAS_AVR_WDT
|
||||||
#include <avr/wdt.h>
|
#include <avr/wdt.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -68,8 +69,10 @@ const int16_t HASH_KEYWORD_SPEED28 = -17064;
|
||||||
const int16_t HASH_KEYWORD_SPEED128 = 25816;
|
const int16_t HASH_KEYWORD_SPEED128 = 25816;
|
||||||
const int16_t HASH_KEYWORD_SERVO=27709;
|
const int16_t HASH_KEYWORD_SERVO=27709;
|
||||||
const int16_t HASH_KEYWORD_VPIN=-415;
|
const int16_t HASH_KEYWORD_VPIN=-415;
|
||||||
const int16_t HASH_KEYWORD_C=67;
|
const int16_t HASH_KEYWORD_A='A';
|
||||||
const int16_t HASH_KEYWORD_T=84;
|
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_LCN = 15137;
|
||||||
const int16_t HASH_KEYWORD_HAL = 10853;
|
const int16_t HASH_KEYWORD_HAL = 10853;
|
||||||
const int16_t HASH_KEYWORD_SHOW = -21309;
|
const int16_t HASH_KEYWORD_SHOW = -21309;
|
||||||
|
@ -208,6 +211,19 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream)
|
||||||
return; // filterCallback asked us to ignore
|
return; // filterCallback asked us to ignore
|
||||||
case 't': // THROTTLE <t [REGISTER] CAB SPEED DIRECTION>
|
case 't': // THROTTLE <t [REGISTER] CAB SPEED DIRECTION>
|
||||||
{
|
{
|
||||||
|
if (params==1) { // <t cab> display state
|
||||||
|
|
||||||
|
int16_t slot=DCC::lookupSpeedTable(p[0],false);
|
||||||
|
if (slot>=0) {
|
||||||
|
DCC::LOCO * sp=&DCC::speedTable[slot];
|
||||||
|
StringFormatter::send(stream,F("<l %d %d %d %l>\n"),
|
||||||
|
sp->loco,slot,sp->speedCode,sp->functions);
|
||||||
|
}
|
||||||
|
else // send dummy state speed 0 fwd no functions.
|
||||||
|
StringFormatter::send(stream,F("<l %d -1 128 0>\n"),p[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int16_t cab;
|
int16_t cab;
|
||||||
int16_t tspeed;
|
int16_t tspeed;
|
||||||
int16_t direction;
|
int16_t direction;
|
||||||
|
@ -495,6 +511,7 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream)
|
||||||
DCC::setFn(p[0], p[1], p[2] == 1);
|
DCC::setFn(p[0], p[1], p[2] == 1);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
#if WIFI_ON
|
||||||
case '+': // Complex Wifi interface command (not usual parse)
|
case '+': // Complex Wifi interface command (not usual parse)
|
||||||
if (atCommandCallback && !ringStream) {
|
if (atCommandCallback && !ringStream) {
|
||||||
TrackManager::setPower(POWERMODE::OFF);
|
TrackManager::setPower(POWERMODE::OFF);
|
||||||
|
@ -502,6 +519,69 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
case 'J' : // throttle info access
|
||||||
|
{
|
||||||
|
if ((params<1) | (params>2)) break; // <J>
|
||||||
|
int16_t id=(params==2)?p[1]:0;
|
||||||
|
switch(p[0]) {
|
||||||
|
case HASH_KEYWORD_A: // <JA> returns automations/routes
|
||||||
|
StringFormatter::send(stream, F("<jA"));
|
||||||
|
if (params==1) {// <JA>
|
||||||
|
#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;
|
||||||
|
case HASH_KEYWORD_R: // <JR> returns rosters
|
||||||
|
StringFormatter::send(stream, F("<jR"));
|
||||||
|
#ifdef EXRAIL_ACTIVE
|
||||||
|
if (params==1) sendFlashList(stream,RMFT2::rosterIdList);
|
||||||
|
else StringFormatter::send(stream,F(" %d \"%S\" \"%S\""),
|
||||||
|
id, RMFT2::getRosterName(id), RMFT2::getRosterFunctions(id));
|
||||||
|
#endif
|
||||||
|
StringFormatter::send(stream, F(">\n"));
|
||||||
|
return;
|
||||||
|
case HASH_KEYWORD_T: // <JT> returns turnout list
|
||||||
|
StringFormatter::send(stream, F("<jT"));
|
||||||
|
if (params==1) { // <JT>
|
||||||
|
for ( Turnout * t=Turnout::first(); t; t=t->next()) {
|
||||||
|
if (t->isHidden()) continue;
|
||||||
|
StringFormatter::send(stream, F(" %d"),t->getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else { // <JT id>
|
||||||
|
Turnout * t=Turnout::get(id);
|
||||||
|
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)
|
||||||
|
#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 <X>
|
default: //anything else will diagnose and drop out to <X>
|
||||||
DIAG(F("Opcode=%c params=%d"), opcode, params);
|
DIAG(F("Opcode=%c params=%d"), opcode, params);
|
||||||
|
@ -515,6 +595,14 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream)
|
||||||
StringFormatter::send(stream, F("<X>\n"));
|
StringFormatter::send(stream, F("<X>\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DCCEXParser::sendFlashList(Print * stream,const int16_t flashList[]) {
|
||||||
|
for (int16_t i=0;;i++) {
|
||||||
|
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[])
|
bool DCCEXParser::parseZ(Print *stream, int16_t params, int16_t p[])
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,7 @@ struct DCCEXParser
|
||||||
static FILTER_CALLBACK filterRMFTCallback;
|
static FILTER_CALLBACK filterRMFTCallback;
|
||||||
static AT_COMMAND_CALLBACK atCommandCallback;
|
static AT_COMMAND_CALLBACK atCommandCallback;
|
||||||
static void funcmap(int16_t cab, byte value, byte fstart, byte fstop);
|
static void funcmap(int16_t cab, byte value, byte fstart, byte fstop);
|
||||||
|
static void sendFlashList(Print * stream,const int16_t flashList[]);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
43
EXRAIL2.cpp
43
EXRAIL2.cpp
|
@ -40,7 +40,6 @@
|
||||||
T2. Extend to >64k
|
T2. Extend to >64k
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "EXRAIL2.h"
|
#include "EXRAIL2.h"
|
||||||
#include "DCC.h"
|
#include "DCC.h"
|
||||||
|
@ -197,7 +196,7 @@ int16_t LookList::find(int16_t value) {
|
||||||
VPIN id=operand;
|
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);
|
setTurnoutHiddenState(DCCTurnout::create(id,addr,subAddr));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,14 +206,14 @@ int16_t LookList::find(int16_t value) {
|
||||||
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);
|
setTurnoutHiddenState(ServoTurnout::create(id,pin,activeAngle,inactiveAngle,profile));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case OPCODE_PINTURNOUT: {
|
case OPCODE_PINTURNOUT: {
|
||||||
VPIN id=operand;
|
VPIN id=operand;
|
||||||
VPIN pin=GET_OPERAND(1);
|
VPIN pin=GET_OPERAND(1);
|
||||||
VpinTurnout::create(id,pin);
|
setTurnoutHiddenState(VpinTurnout::create(id,pin));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,6 +257,23 @@ int16_t LookList::find(int16_t value) {
|
||||||
new RMFT2(0); // add the startup route
|
new RMFT2(0); // add the startup route
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RMFT2::setTurnoutHiddenState(Turnout * t) {
|
||||||
|
t->setHidden(GETFLASH(getTurnoutDescription(t->getId()))==0x01);
|
||||||
|
}
|
||||||
|
|
||||||
|
char RMFT2::getRouteType(int16_t id) {
|
||||||
|
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++) {
|
||||||
|
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:
|
// This filter intercepts <> commands to do the following:
|
||||||
// - Implement RMFT specific commands/diagnostics
|
// - Implement RMFT specific commands/diagnostics
|
||||||
// - Reject/modify JMRI commands that would interfere with RMFT processing
|
// - Reject/modify JMRI commands that would interfere with RMFT processing
|
||||||
|
@ -347,13 +363,6 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case HASH_KEYWORD_ROUTES: // </ ROUTES > JMRI withrottle support
|
|
||||||
if (paramCount>1) return false;
|
|
||||||
StringFormatter::send(stream,F("</ROUTES "));
|
|
||||||
emitWithrottleRouteList(stream);
|
|
||||||
StringFormatter::send(stream,F(">"));
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -408,11 +417,7 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) {
|
||||||
// Automations are given a state to set the button to "handoff" which implies
|
// Automations are given a state to set the button to "handoff" which implies
|
||||||
// handing over the loco to the automation.
|
// handing over the loco to the automation.
|
||||||
// Routes are given "Set" buttons and do not cause the loco to be handed over.
|
// 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) {
|
RMFT2::RMFT2(int progCtr) {
|
||||||
|
@ -1049,8 +1054,4 @@ void RMFT2::printMessage2(const FSH * msg) {
|
||||||
DIAG(F("EXRAIL(%d) %S"),loco,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');
|
|
||||||
}
|
|
||||||
|
|
22
EXRAIL2.h
22
EXRAIL2.h
|
@ -22,6 +22,7 @@
|
||||||
#define EXRAIL2_H
|
#define EXRAIL2_H
|
||||||
#include "FSH.h"
|
#include "FSH.h"
|
||||||
#include "IODevice.h"
|
#include "IODevice.h"
|
||||||
|
#include "Turnouts.h"
|
||||||
|
|
||||||
// The following are the operation codes (or instructions) for a kind of virtual machine.
|
// 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.
|
// Each instruction is normally 3 bytes long with an operation code followed by a parameter.
|
||||||
|
@ -87,17 +88,23 @@ class LookList {
|
||||||
RMFT2(int route, uint16_t cab);
|
RMFT2(int route, uint16_t cab);
|
||||||
~RMFT2();
|
~RMFT2();
|
||||||
static void readLocoCallback(int16_t cv);
|
static void readLocoCallback(int16_t cv);
|
||||||
static void emitWithrottleRouteList(Print* stream);
|
|
||||||
static void createNewTask(int route, uint16_t cab);
|
static void createNewTask(int route, uint16_t cab);
|
||||||
static void turnoutEvent(int16_t id, bool closed);
|
static void turnoutEvent(int16_t id, bool closed);
|
||||||
static void activateEvent(int16_t addr, bool active);
|
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 SERVO_SIGNAL_FLAG=0x4000;
|
||||||
static const int16_t ACTIVE_HIGH_SIGNAL_FLAG=0x2000;
|
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 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);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]);
|
static void ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]);
|
||||||
static bool parseSlash(Print * stream, byte & paramCount, int16_t p[]) ;
|
static bool parseSlash(Print * stream, byte & paramCount, int16_t p[]) ;
|
||||||
|
@ -106,9 +113,7 @@ private:
|
||||||
static bool getFlag(VPIN id,byte mask);
|
static bool getFlag(VPIN id,byte mask);
|
||||||
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 setTurnoutHiddenState(Turnout * t);
|
||||||
static void emitWithrottleDescriptions(Print * stream);
|
|
||||||
|
|
||||||
static RMFT2 * loopTask;
|
static RMFT2 * loopTask;
|
||||||
static RMFT2 * pausingTask;
|
static RMFT2 * pausingTask;
|
||||||
void delayMe(long millisecs);
|
void delayMe(long millisecs);
|
||||||
|
@ -132,6 +137,7 @@ private:
|
||||||
static LookList * onActivateLookup;
|
static LookList * onActivateLookup;
|
||||||
static LookList * onDeactivateLookup;
|
static LookList * onDeactivateLookup;
|
||||||
|
|
||||||
|
|
||||||
// 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
|
||||||
|
|
|
@ -132,7 +132,7 @@
|
||||||
#define ATGTE(sensor_id,value)
|
#define ATGTE(sensor_id,value)
|
||||||
#define ATLT(sensor_id,value)
|
#define ATLT(sensor_id,value)
|
||||||
#define ATTIMEOUT(sensor_id,timeout_ms)
|
#define ATTIMEOUT(sensor_id,timeout_ms)
|
||||||
#define AUTOMATION(id, description)
|
#define AUTOMATION(id,description)
|
||||||
#define AUTOSTART
|
#define AUTOSTART
|
||||||
#define BROADCAST(msg)
|
#define BROADCAST(msg)
|
||||||
#define CALL(route)
|
#define CALL(route)
|
||||||
|
@ -193,7 +193,7 @@
|
||||||
#define RESUME
|
#define RESUME
|
||||||
#define RETURN
|
#define RETURN
|
||||||
#define REV(speed)
|
#define REV(speed)
|
||||||
#define ROUTE(id, description)
|
#define ROUTE(id,description)
|
||||||
#define ROSTER(cab,name,funcmap...)
|
#define ROSTER(cab,name,funcmap...)
|
||||||
#define SENDLOCO(cab,route)
|
#define SENDLOCO(cab,route)
|
||||||
#define SEQUENCE(id)
|
#define SEQUENCE(id)
|
||||||
|
|
|
@ -49,24 +49,47 @@
|
||||||
|
|
||||||
// CAUTION: The macros below are multiple passed over myAutomation.h
|
// 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 "\x01"
|
||||||
|
|
||||||
// Pass 1 Implements aliases
|
// Pass 1 Implements aliases
|
||||||
#include "EXRAIL2MacroReset.h"
|
#include "EXRAIL2MacroReset.h"
|
||||||
#undef ALIAS
|
#undef ALIAS
|
||||||
#define ALIAS(name,value...) const int name= 1##value##0 ==10 ? -__COUNTER__ : value##0/10;
|
#define ALIAS(name,value...) const int name= 1##value##0 ==10 ? -__COUNTER__ : value##0/10;
|
||||||
#include "myAutomation.h"
|
#include "myAutomation.h"
|
||||||
|
|
||||||
// Pass 2 convert descriptions to withrottle format emitter function
|
// Pass 2 create throttle route list
|
||||||
#include "EXRAIL2MacroReset.h"
|
#include "EXRAIL2MacroReset.h"
|
||||||
#undef ROUTE
|
#undef ROUTE
|
||||||
#define ROUTE(id, description) emitRouteDescription(stream,'R',id,F(description));
|
#define ROUTE(id, description) id,
|
||||||
#undef AUTOMATION
|
const int16_t FLASH RMFT2::routeIdList[]= {
|
||||||
#define AUTOMATION(id, description) emitRouteDescription(stream,'A',id,F(description));
|
|
||||||
void RMFT2::emitWithrottleDescriptions(Print * stream) {
|
|
||||||
(void)stream;
|
|
||||||
#include "myAutomation.h"
|
#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
|
||||||
|
#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 F("");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass 3... Create Text sending functions
|
// Pass 4... Create Text sending functions
|
||||||
#include "EXRAIL2MacroReset.h"
|
#include "EXRAIL2MacroReset.h"
|
||||||
const int StringMacroTracker1=__COUNTER__;
|
const int StringMacroTracker1=__COUNTER__;
|
||||||
#undef BROADCAST
|
#undef BROADCAST
|
||||||
|
@ -96,57 +119,62 @@ void RMFT2::printMessage(uint16_t id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Pass 4: Turnout descriptions (optional)
|
// Pass 5: Turnout descriptions (optional)
|
||||||
#include "EXRAIL2MacroReset.h"
|
#include "EXRAIL2MacroReset.h"
|
||||||
#undef TURNOUT
|
#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
|
#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
|
#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
|
#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 * RMFT2::getTurnoutDescription(int16_t turnoutid) {
|
||||||
const FSH * desc=F("");
|
|
||||||
switch (turnoutid) {
|
switch (turnoutid) {
|
||||||
#include "myAutomation.h"
|
#include "myAutomation.h"
|
||||||
default: break;
|
default:break;
|
||||||
}
|
}
|
||||||
if (GETFLASH(desc)=='\0') desc=F("%d");
|
return NULL;
|
||||||
StringFormatter::send(stream,desc,turnoutid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass 5: Roster names (count)
|
// Pass 6: Roster IDs (count)
|
||||||
#include "EXRAIL2MacroReset.h"
|
#include "EXRAIL2MacroReset.h"
|
||||||
#undef ROSTER
|
#undef ROSTER
|
||||||
#define ROSTER(cabid,name,funcmap...) +1
|
#define ROSTER(cabid,name,funcmap...) +1
|
||||||
|
|
||||||
const byte RMFT2::rosterNameCount=0
|
const byte RMFT2::rosterNameCount=0
|
||||||
#include "myAutomation.h"
|
#include "myAutomation.h"
|
||||||
;
|
;
|
||||||
|
|
||||||
// Pass 6: Roster names emitter
|
// Pass 6: Roster IDs
|
||||||
#include "EXRAIL2MacroReset.h"
|
#include "EXRAIL2MacroReset.h"
|
||||||
#undef ROSTER
|
#undef ROSTER
|
||||||
#define ROSTER(cabid,name,funcmap...) StringFormatter::send(stream,(FSH *)format,F(name),cabid,cabid<128?'S':'L');
|
#define ROSTER(cabid,name,funcmap...) cabid,
|
||||||
void RMFT2::emitWithrottleRoster(Print * stream) {
|
const int16_t FLASH RMFT2::rosterIdList[]={
|
||||||
static const char format[] FLASH ="]\\[%S}|{%d}|{%c";
|
|
||||||
(void)format;
|
|
||||||
StringFormatter::send(stream,F("RL%d"), rosterNameCount);
|
|
||||||
#include "myAutomation.h"
|
#include "myAutomation.h"
|
||||||
stream->write('\n');
|
0};
|
||||||
|
|
||||||
|
// Pass 7: Roster names getter
|
||||||
|
#include "EXRAIL2MacroReset.h"
|
||||||
|
#undef ROSTER
|
||||||
|
#define ROSTER(cabid,name,funcmap...) case cabid: return F(name);
|
||||||
|
const FSH * RMFT2::getRosterName(int16_t id) {
|
||||||
|
switch(id) {
|
||||||
|
#include "myAutomation.h"
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
return F("");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass 7: functions getter
|
// Pass to get roster functions
|
||||||
#include "EXRAIL2MacroReset.h"
|
|
||||||
#undef ROSTER
|
#undef ROSTER
|
||||||
#define ROSTER(cabid,name,funcmap...) case cabid: return F("" funcmap);
|
#define ROSTER(cabid,name,funcmap...) case cabid: return F("" funcmap);
|
||||||
const FSH * RMFT2::getRosterFunctions(int16_t cabid) {
|
const FSH * RMFT2::getRosterFunctions(int16_t id) {
|
||||||
switch(cabid) {
|
switch(id) {
|
||||||
#include "myAutomation.h"
|
#include "myAutomation.h"
|
||||||
default: return NULL;
|
default: break;
|
||||||
}
|
}
|
||||||
|
return F("");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass 8 Signal definitions
|
// Pass 8 Signal definitions
|
||||||
|
|
75
Release_Notes/ThrottleAssists.md
Normal file
75
Release_Notes/ThrottleAssists.md
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
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 ```<H>``` 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.
|
||||||
|
|
||||||
|
```<JT>``` 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 ```<jT 1 17 22 19>```
|
||||||
|
|
||||||
|
```<JT 17>`` requests info on turnout 17.
|
||||||
|
e.g. response ```<jT 17 T "Coal yard exit">``` or ```<jT 17 C "Coal yard exit">```
|
||||||
|
(T=thrown, C=closed)
|
||||||
|
or ```<jT 17 C "">``` indicating turnout description not given.
|
||||||
|
or ```<jT 17 X>``` 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)
|
||||||
|
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.
|
||||||
|
|
||||||
|
```<JA>``` Returns a list of Automations/Routes
|
||||||
|
e.g. ```<jA 13 16 23>```
|
||||||
|
Indicates route/automation ids.
|
||||||
|
Information on each route needs to be obtained by
|
||||||
|
```<JA 13>```
|
||||||
|
returns e.g. ```<jA 13 R "description">``` for a route
|
||||||
|
or ```<jA 13 A "description">``` for an automation.
|
||||||
|
or ```<jA 13 X>``` 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.
|
||||||
|
Thus a route can be triggered by sending in for example ```</START 13>```.
|
||||||
|
|
||||||
|
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. ```</START 13 3>```
|
||||||
|
|
||||||
|
|
||||||
|
Roster Information:
|
||||||
|
The ```<JR>``` command requests a list of cab ids from the roster.
|
||||||
|
e.g. responding ```<jR 3 200 6336>```
|
||||||
|
or <jR> for none.
|
||||||
|
|
||||||
|
Each Roster entry had a name and function map obtained by:
|
||||||
|
```<JR 200>``` reply like ```<jR 200 "Thomas" "whistle/*bell/squeal/panic">
|
||||||
|
|
||||||
|
Refer to EXRAIL ROSTER command for function map format.
|
||||||
|
|
||||||
|
|
||||||
|
Obtaining throttle status.
|
||||||
|
```<t cabid>``` Requests a deliberate update on the cab speed/functions in the same format as the cab broadcast.
|
||||||
|
```<l cabid slot speedbyte functionMap>```
|
||||||
|
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
|
||||||
|
|
||||||
|
```<f cab func1 func2>``` Use ```<F cab function 1/0>```
|
||||||
|
```<t slot cab speed dir>``` Just drop the slot number
|
||||||
|
```<T commands>``` other than ```<T id 0/1>```
|
||||||
|
```<s>```
|
||||||
|
```<c>```
|
||||||
|
|
||||||
|
|
||||||
|
|
12
Turnouts.h
12
Turnouts.h
|
@ -3,7 +3,7 @@
|
||||||
* © 2021 M Steve Todd
|
* © 2021 M Steve Todd
|
||||||
* © 2021 Fred Decker
|
* © 2021 Fred Decker
|
||||||
* © 2020-2021 Harald Barth
|
* © 2020-2021 Harald Barth
|
||||||
* © 2020-2021 Chris Harlow
|
* © 2020-2022 Chris Harlow
|
||||||
* © 2013-2016 Gregg E. Berman
|
* © 2013-2016 Gregg E. Berman
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
|
@ -61,7 +61,8 @@ protected:
|
||||||
struct {
|
struct {
|
||||||
bool closed : 1;
|
bool closed : 1;
|
||||||
bool _rfu: 2;
|
bool _rfu: 2;
|
||||||
uint8_t turnoutType : 5;
|
bool hidden: 1;
|
||||||
|
uint8_t turnoutType : 4;
|
||||||
};
|
};
|
||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
};
|
};
|
||||||
|
@ -83,6 +84,7 @@ protected:
|
||||||
_turnoutData.id = id;
|
_turnoutData.id = id;
|
||||||
_turnoutData.turnoutType = turnoutType;
|
_turnoutData.turnoutType = turnoutType;
|
||||||
_turnoutData.closed = closed;
|
_turnoutData.closed = closed;
|
||||||
|
_turnoutData.hidden=false;
|
||||||
add(this);
|
add(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,11 +106,11 @@ protected:
|
||||||
* Static functions
|
* Static functions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static Turnout *get(uint16_t id);
|
|
||||||
|
|
||||||
static void add(Turnout *tt);
|
static void add(Turnout *tt);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static Turnout *get(uint16_t id);
|
||||||
/*
|
/*
|
||||||
* Static data
|
* Static data
|
||||||
*/
|
*/
|
||||||
|
@ -120,6 +122,8 @@ public:
|
||||||
*/
|
*/
|
||||||
inline bool isClosed() { return _turnoutData.closed; };
|
inline bool isClosed() { return _turnoutData.closed; };
|
||||||
inline bool isThrown() { 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 bool isType(uint8_t type) { return _turnoutData.turnoutType == type; }
|
||||||
inline uint16_t getId() { return _turnoutData.id; }
|
inline uint16_t getId() { return _turnoutData.id; }
|
||||||
inline Turnout *next() { return _nextTurnout; }
|
inline Turnout *next() { return _nextTurnout; }
|
||||||
|
@ -169,7 +173,7 @@ public:
|
||||||
#endif
|
#endif
|
||||||
static void printAll(Print *stream) {
|
static void printAll(Print *stream) {
|
||||||
for (Turnout *tt = _firstTurnout; tt != 0; tt = tt->_nextTurnout)
|
for (Turnout *tt = _firstTurnout; tt != 0; tt = tt->_nextTurnout)
|
||||||
StringFormatter::send(stream, F("<H %d %d>\n"),tt->getId(), tt->isThrown());
|
if (!tt->isHidden()) StringFormatter::send(stream, F("<H %d %d>\n"),tt->getId(), tt->isThrown());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -121,14 +121,17 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) {
|
||||||
if (turnoutListHash != Turnout::turnoutlistHash) {
|
if (turnoutListHash != Turnout::turnoutlistHash) {
|
||||||
StringFormatter::send(stream,F("PTL"));
|
StringFormatter::send(stream,F("PTL"));
|
||||||
for(Turnout *tt=Turnout::first();tt!=NULL;tt=tt->next()){
|
for(Turnout *tt=Turnout::first();tt!=NULL;tt=tt->next()){
|
||||||
|
if (tt->isHidden()) continue;
|
||||||
int id=tt->getId();
|
int id=tt->getId();
|
||||||
StringFormatter::send(stream,F("]\\[%d}|{"), id);
|
const FSH * tdesc=NULL;
|
||||||
#ifdef EXRAIL_ACTIVE
|
#ifdef EXRAIL_ACTIVE
|
||||||
RMFT2::emitTurnoutDescription(stream,id);
|
tdesc=RMFT2::getTurnoutDescription(id);
|
||||||
#else
|
#endif
|
||||||
StringFormatter::send(stream,F("%d"), id);
|
char tchar=Turnout::isClosed(id)?'2':'4';
|
||||||
#endif
|
if (tdesc==NULL) // turnout with no description
|
||||||
StringFormatter::send(stream,F("}|{%c"), Turnout::isClosed(id)?'2':'4');
|
StringFormatter::send(stream,F("]\\[%d}|{T%d}|{T%c"), id,id,tchar);
|
||||||
|
else
|
||||||
|
StringFormatter::send(stream,F("]\\[%d}|{%S}|{%c"), id,tdesc,tchar);
|
||||||
}
|
}
|
||||||
StringFormatter::send(stream,F("\n"));
|
StringFormatter::send(stream,F("\n"));
|
||||||
turnoutListHash = Turnout::turnoutlistHash; // keep a copy of hash for later comparison
|
turnoutListHash = Turnout::turnoutlistHash; // keep a copy of hash for later comparison
|
||||||
|
@ -138,7 +141,18 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) {
|
||||||
// Send EX-RAIL routes list if not already sent (but not at same time as turnouts above)
|
// Send EX-RAIL routes list if not already sent (but not at same time as turnouts above)
|
||||||
exRailSent=true;
|
exRailSent=true;
|
||||||
#ifdef EXRAIL_ACTIVE
|
#ifdef EXRAIL_ACTIVE
|
||||||
RMFT2::emitWithrottleRouteList(stream);
|
StringFormatter::send(stream,F("PRT]\\[Routes}|{Route]\\[Set}|{2]\\[Handoff}|{4\nPRL"));
|
||||||
|
for (byte pass=0;pass<2;pass++) {
|
||||||
|
// 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"),
|
||||||
|
pass?'A':'R',id,desc, pass?'4':'2');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StringFormatter::send(stream,F("\n"));
|
||||||
#endif
|
#endif
|
||||||
// allow heartbeat to slow down once all metadata sent
|
// allow heartbeat to slow down once all metadata sent
|
||||||
StringFormatter::send(stream,F("*%d\n"),HEARTBEAT_SECONDS);
|
StringFormatter::send(stream,F("*%d\n"),HEARTBEAT_SECONDS);
|
||||||
|
@ -211,8 +225,16 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) {
|
||||||
StringFormatter::send(stream,F("PTT]\\[Turnouts}|{Turnout]\\[THROW}|{2]\\[CLOSE}|{4\n"));
|
StringFormatter::send(stream,F("PTT]\\[Turnouts}|{Turnout]\\[THROW}|{2]\\[CLOSE}|{4\n"));
|
||||||
StringFormatter::send(stream,F("PPA%x\n"),TrackManager::getMainPower()==POWERMODE::ON);
|
StringFormatter::send(stream,F("PPA%x\n"),TrackManager::getMainPower()==POWERMODE::ON);
|
||||||
#ifdef EXRAIL_ACTIVE
|
#ifdef EXRAIL_ACTIVE
|
||||||
RMFT2::emitWithrottleRoster(stream);
|
StringFormatter::send(stream,F("RL%d"), RMFT2::rosterNameCount);
|
||||||
|
for (int16_t r=0;r<RMFT2::rosterNameCount;r++) {
|
||||||
|
int16_t cabid=GETFLASHW(RMFT2::rosterIdList+r);
|
||||||
|
StringFormatter::send(stream,F("]\\[%S}|{%d}|{%c"),
|
||||||
|
RMFT2::getRosterName(cabid),cabid,cabid<128?'S':'L');
|
||||||
|
}
|
||||||
|
stream->write('\n'); // end roster
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// set heartbeat to 1 second because we need to sync the metadata
|
// set heartbeat to 1 second because we need to sync the metadata
|
||||||
StringFormatter::send(stream,F("*1\n"));
|
StringFormatter::send(stream,F("*1\n"));
|
||||||
initSent = true;
|
initSent = true;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user