1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2025-04-06 21:50:11 +02:00

doc comments in parser (1)

This commit is contained in:
Asbelos 2025-04-04 11:44:25 +01:00
parent 18dcbeff31
commit 91e8f89fe2
6 changed files with 446 additions and 273 deletions

View File

@ -113,80 +113,120 @@ If the condition is false an error is genarated, resulting in an <X> reply.
The REPLY( format, ...) macro sends a formatted string to the stream.
These macros are included into the DCCEXParser::execute function so
strea, ringStream and other DCCEXParser variables are available in context. */
stream, ringStream and other DCCEXParser variables are available in context. */
// helper macro to check track letter
#define CHECKTRACK CHECK(track>='A' && track<='H', Invalid track A..H)
// helper macro to hide command from documentation extractor
#define ZZ_nodoc ZZ
ZZBEGIN
ZZ(#) REPLY( "<# %d>\n", MAX_LOCOS)
ZZ(t,cab) CHECK(cab>0)
CommandDistributor::broadcastLoco(DCC::lookupSpeedTable(cab,false));
ZZ(t,cab,tspeed,direction) CHECK(setThrottle(cab,tspeed,direction))
ZZ(t,ignore,cab,tspeed,direction) CHECK(setThrottle(cab,tspeed,direction))
ZZ(#) // Request number of simultaneously supported locos
REPLY( "<# %d>\n", MAX_LOCOS)
ZZ(!) // Emergency stop all locos
DCC::estopAll();
ZZ(t,loco) // Request loco status
CHECK(loco>0)
CommandDistributor::broadcastLoco(DCC::lookupSpeedTable(loco,false));
ZZ(t,loco,tspeed,direction) // Set throttle speed(0..127) and direction (0=reverse, 1=fwd)
CHECK(setThrottle(loco,tspeed,direction))
ZZ(t,ignore,loco,tspeed,direction) // (Deprecated) Set throttle speed and direction
CHECK(setThrottle(loco,tspeed,direction))
// todo ZZ(f,cab,byte1) CHECK(handleFunctionGroup(cab,byte1))
// todo ZZ(f,cab,byte1,byte2) CHECK(handleFunctionGroup(cab,byte1,byte2))
ZZ(T) Turnout::printAll(stream); // will <X> if none found
ZZ(T,id) CHECK(Turnout::remove(id))
ZZ(T,id,X) auto tt=Turnout::get(id); CHECK(tt) tt->print(stream);
ZZ(T,id,T) Turnout::setClosed(id, false);
ZZ(T,id,C) Turnout::setClosed(id, true);
ZZ(T,id,value) Turnout::setClosed(id, value==0);
ZZ(T,id,SERVO,pin,low,high) CHECK(ServoTurnout::create(id, (VPIN)pin, (uint16_t)low, (uint16_t)high, 1))
ZZ(T,id,VPIN,pin) CHECK(VpinTurnout::create(id, pin))
ZZ(T,id,DCC,addr,subadd) CHECK(DCCTurnout::create(id, addr, subadd))
ZZ(T,id,DCC,nn) CHECK(DCCTurnout::create(id, (nn-1)/4+1, (nn-1)%4))
ZZ(T,id,addr,subadd) CHECK(DCCTurnout::create(id, addr, subadd))
ZZ(T,id,pin,low,high) CHECK(ServoTurnout::create(id, (VPIN)pin,low,high,1))
ZZ(S,id,pin,pullup) CHECK(Sensor::create(id,pin,pullup))
ZZ(S,id) CHECK(Sensor::remove(id))
ZZ(S) for (auto *tt = Sensor::firstSensor; tt; tt = tt->nextSensor) {
REPLY("<Q %d %d %d>\n", tt->data.snum, tt->data.pin, tt->data.pullUp)
}
ZZ(J,M) Stash::list(stream);
ZZ(J,M,stash_id) Stash::list(stream, stash_id);
ZZ(J,M,CLEAR,ALL) Stash::clearAll();
ZZ(J,M,CLEAR,stash_id) Stash::clear(stash_id);
ZZ(J,M,stashId,locoId) Stash::set(stashId,locoId);
ZZ(J,M,CLEAR,ANY,locoId) Stash::clearAny(locoId);
ZZ(J,C) REPLY("<jC %d>\n", CommandDistributor::retClockTime())
ZZ(J,C,mmmm,nn) CommandDistributor::setClockTime(mmmm, nn, 1);
ZZ(J,G) TrackManager::reportGauges(stream); // <g limit...limit>
ZZ(J,I) TrackManager::reportCurrent(stream); // <g limit...limit>
ZZ(J,L,display,row) TrackManager::reportCurrentLCD(display,row); // Track power status
ZZ(J,A) REPLY( "<jA>\n") // <JA> intercepted by EXRAIL// <JA> returns automations/routes
#ifdef EXRAIL_ACTIVE
ZZ(J,R) REPLY("<jR")
SENDFLASHLIST(stream,RMFT2::rosterIdList)
ZZ(T) // List all turnouts
Turnout::printAll(stream); // will <X> if none found
ZZ(T,id) // Delete turnout
CHECK(Turnout::remove(id))
ZZ(T,id,X) // List turnout details
auto tt=Turnout::get(id); CHECK(tt) tt->print(stream);
ZZ(T,id,T) // Throw Turnout
Turnout::setClosed(id, false);
ZZ(T,id,C) // Close turnout#
Turnout::setClosed(id, true);
ZZ(T,id,value) // Close (value=0) ot Throw turnout
Turnout::setClosed(id, value==0);
ZZ(T,id,SERVO,vpin,closedValue,thrownValue) // Create Servo turnout
CHECK(ServoTurnout::create(id, (VPIN)vpin, (uint16_t)closedValue, (uint16_t)thrownValue, 1))
ZZ(T,id,VPIN,vpin) // Create pin turnout
CHECK(VpinTurnout::create(id, vpin))
ZZ(T,id,DCC,addr,subadd) // Create DCC turnout
CHECK(DCCTurnout::create(id, addr, subadd))
ZZ(T,id,DCC,linearAddr) // Create DCC turnout
CHECK(DCCTurnout::create(id, (linearAddr-1)/4+1, (linearAddr-1)%4))
ZZ(T,id,addr,subadd) // Create DCC turnout
CHECK(DCCTurnout::create(id, addr, subadd))
ZZ(T,id,vpin,closedValue,thrownValue) // Create SERVO turnout
CHECK(ServoTurnout::create(id, (VPIN)vpin, (uint16_t)closedValue, (uint16_t)thrownValue, 1))
ZZ(S,id,vpin,pullup) // Create Sensor
CHECK(Sensor::create(id,vpin,pullup))
ZZ(S,id) // Delete sensor
CHECK(Sensor::remove(id))
ZZ(S) // List sensors
for (auto *tt = Sensor::firstSensor; tt; tt = tt->nextSensor) {
REPLY("<Q %d %d %d>\n", tt->data.snum, tt->data.pin, tt->data.pullUp)
}
ZZ(J,M) // List stash values
Stash::list(stream);
ZZ(J,M,stash_id) // get stash value
Stash::list(stream, stash_id);
ZZ(J,M,CLEAR,ALL) // Clear all stash values
Stash::clearAll();
ZZ(J,M,CLEAR,stash_id) // Clear given stash
Stash::clear(stash_id);
ZZ(J,M,stashId,locoId) // Set stash value
Stash::set(stashId,locoId);
ZZ(J,M,CLEAR,ANY,locoId) // Clear all stash entries that contain locoId
Stash::clearAny(locoId);
ZZ(J,C) // get fastclock time
REPLY("<jC %d>\n", CommandDistributor::retClockTime())
ZZ(J,C,mmmm,nn) // Set fastclock time
CommandDistributor::setClockTime(mmmm, nn, 1);
ZZ(J,G) // FReport gauge limits
TrackManager::reportGauges(stream);
ZZ(J,I) // Report currents
TrackManager::reportCurrent(stream);
ZZ(J,L,display,row) // Direct current displays to LCS/OLED
TrackManager::reportCurrentLCD(display,row); // Track power status
ZZ(J,A) // List Routes
REPLY( "<jA>\n")
ZZ(J,R) // List Roster
REPLY("<jR")
#ifdef EXRAIL_ACTIVE
SENDFLASHLIST(stream,RMFT2::rosterIdList)
#endif
REPLY(">\n");
ZZ(J,R,id) auto rosterName= RMFT2::getRosterName(id);
if (!rosterName) rosterName=F("");
auto functionNames= RMFT2::getRosterFunctions(id);
if (!functionNames) functionNames=RMFT2::getRosterFunctions(0);
if (!functionNames) functionNames=F("");
REPLY("<jR %d \"%S\" \"%S\">\n",id, rosterName, functionNames)
#else
ZZ(J,R) REPLY("<jR>\n") // <JR> returns empty roster list
#endif
ZZ(J,T) // <JT> returns turnout list
REPLY("<jT")
for ( auto t=Turnout::first(); t; t=t->next()) if (!t->isHidden()) REPLY(" %d",t->getId())
REPLY(">\n");
ZZ(J,T,id) auto t=Turnout::get(id);
if (!t || t->isHidden()) REPLY("<jT %d X>\n",id)
else {
const FSH *tdesc=nullptr;
#ifdef EXRAIL_ACTIVE
tdesc = RMFT2::getTurnoutDescription(id);
ZZ(J,R,id) // Get roster for loco
auto rosterName= RMFT2::getRosterName(id);
if (!rosterName) rosterName=F("");
auto functionNames= RMFT2::getRosterFunctions(id);
if (!functionNames) functionNames=RMFT2::getRosterFunctions(0);
if (!functionNames) functionNames=F("");
REPLY("<jR %d \"%S\" \"%S\">\n",id, rosterName, functionNames)
#endif
if (!tdesc) tdesc = F("");
REPLY("<jT %d %c \"%S\">\n",id,t->isThrown()?'T':'C',tdesc)
}
ZZ(z,vpin) // <z vpin | -vpin>
if (vpin>0) IODevice::write(vpin,HIGH);
else IODevice::write(-vpin,LOW);
ZZ(z,vpin,analog,profile,duration) IODevice::writeAnalogue(vpin,analog,profile,duration);
ZZ(z,vpin,analog,profile) IODevice::writeAnalogue(vpin,analog,profile,0);
ZZ(z,vpin,analog) IODevice::writeAnalogue(vpin,analog,0,0);
ZZ(J,T) // Get turnout list
REPLY("<jT")
for ( auto t=Turnout::first(); t; t=t->next()) if (!t->isHidden()) REPLY(" %d",t->getId())
REPLY(">\n");
ZZ(J,T,id) // Get turnout state and description
auto t=Turnout::get(id);
if (!t || t->isHidden()) { REPLY("<jT %d X>\n",id) return true; }
const FSH *tdesc=nullptr;
#ifdef EXRAIL_ACTIVE
tdesc = RMFT2::getTurnoutDescription(id);
#endif
if (!tdesc) tdesc = F("");
REPLY("<jT %d %c \"%S\">\n",id,t->isThrown()?'T':'C',tdesc)
ZZ(z,vpin) // Set pin. HIGH iv vpin positive, LOW if vpin negative
IODevice::write(vpin,(vpin>0)?HIGH:LOW);
ZZ(z,vpin,analog,profile,duration) // Change analog value over duration (Fade or servo move)
IODevice::writeAnalogue(vpin,analog,profile,duration);
ZZ(z,vpin,analog,profile) // Write analog device using profile number (Fade or servo movement)
IODevice::writeAnalogue(vpin,analog,profile,0);
ZZ(z,vpin,analog) // Write analog device value
IODevice::writeAnalogue(vpin,analog,0,0);
// ==========================
// Turntable - no support if no HAL
@ -198,238 +238,335 @@ ZZ(z,vpin,analog) IODevice::writeAnalogue(vpin,analog,0,0);
// <I id ADD position value> - add position
// <I id EXTT i2caddress vpin home> - create EXTT
ZZ(I) return Turntable::printAll(stream);
ZZ(I,id) // <I id> broadcast type and current position
auto tto = Turntable::get(id);
CHECK(tto)
REPLY("<I %d %d>\n", tto->isEXTT(), tto->getPosition())
ZZ(I,id,position) // <I id position> - rotate a DCC turntable
auto tto = Turntable::get(id);
CHECK(tto)
CHECK(!tto->isEXTT())
ZZ(I) // List all turntables
return Turntable::printAll(stream);
ZZ(I,id) // Broadcast turntable type and current position
auto tto = Turntable::get(id);
CHECK(tto,Turntable not found)
REPLY("<I %d %d>\n", tto->isEXTT(), tto->getPosition())
ZZ(I,id,position) // Rotate a DCC turntable
auto tto = Turntable::get(id);
CHECK(tto,Turntable not found)
CHECK(!tto->isEXTT(),Turntable type incorrect)
CHECK(tto->setPosition(id,position))
ZZ(I,id,DCC,home)
auto tto = Turntable::get(id);
CHECK(tto)
ZZ(I,id,DCC,home) // Create DCC turntable
CHECK(home >=0 && home <= 3600)
auto tto = Turntable::get(id);
CHECK(!tto,Turntable already exists)
CHECK(DCCTurntable::create(id))
tto = Turntable::get(id);
CHECK(tto)
tto->addPosition(0, 0, home);
REPLY("<I>\n")
ZZ(I,id,position,activity)
ZZ(I,id,position,activity) // Rotate an EXTT turntable
auto tto = Turntable::get(id);
CHECK(tto)
CHECK(tto->isEXTT())
CHECK(tto,Turntable not found)
CHECK(tto->isEXTT(), Turntable wrong type)
CHECK(tto->setPosition(id, position,activity))
ZZ(I,id,EXTT,vpin,home) // <I id EXTT vpin home> create an EXTT turntable
ZZ(I,id,EXTT,vpin,home) // Create an EXTT turntable
auto tto = Turntable::get(id);
CHECK(!tto && home >= 0 && home <= 3600)
CHECK(!tto,Turntable already exists)
CHECK(home >= 0 && home <= 3600)
CHECK(EXTTTurntable::create(id, (VPIN)vpin))
tto = Turntable::get(id);
tto->addPosition(0, 0, home);
REPLY("<I>\n")
ZZ(I,id,ADD,position,value,angle) // Add turntable position
auto tto = Turntable::get(id);
CHECK(tto,Turntable not found)
CHECK(position <= 48 && angle >=0 && angle <= 3600)
tto->addPosition(id,value,angle);
REPLY("<I>\n")
ZZ(I,id,ADD,position,value,angle) // <I id ADD position value angle> add a position
auto tto = Turntable::get(id);
// tto must exist, no more than 48 positions, angle 0 - 3600
CHECK(tto && position <= 48 && angle >=0 && angle <= 3600)
tto->addPosition(id,value,angle);
REPLY("<I>\n")
ZZ(Q) // List all sensors
Sensor::printAll(stream);
ZZ(Q) Sensor::printAll(stream);
ZZ(s) // STATUS <s>
REPLY("<iDCC-EX V-%S / %S / %S G-%S>\n", F(VERSION), F(ARDUINO_TYPE), DCC::getMotorShieldName(), F(GITHUB_SHA))
ZZ(s) // Command station status
REPLY("<iDCC-EX V-" VERSION " / " ARDUINO_TYPE " / %S G-" GITHUB_SHA ">\n", DCC::getMotorShieldName())
CommandDistributor::broadcastPower(); // <s> is the only "get power status" command we have
Turnout::printAll(stream); //send all Turnout states
Sensor::printAll(stream); //send all Sensor states
#ifndef DISABLE_EEPROM
ZZ(E) // STORE EPROM <E>
ZZ(E) // STORE EPROM
EEStore::store();
REPLY("<e %d %d %d>\n", EEStore::eeStore->data.nTurnouts, EEStore::eeStore->data.nSensors, EEStore::eeStore->data.nOutputs)
ZZ(e) // CLEAR EPROM <e>
ZZ(e) // CLEAR EPROM
EEStore::clear();
REPLY("<O>\n")
#endif
ZZ(Z,id,active) auto o = Output::get(id);
CHECK(o)
o->activate(active);
REPLY("<Y %d %d>\n", id,active)
ZZ(Z) // List Output definitions
bool gotone = false;
for (auto *tt = Output::firstOutput; tt ; tt = tt->nextOutput) {
gotone = true;
REPLY("<Y %d %d %d %d>\n",tt->data.id, tt->data.pin, tt->data.flags, tt->data.active)
}
CHECK(gotone,No Outputs found)
ZZ(Z,id,pin,iflag) // <Z ID PIN IFLAG>
CHECK(id > 0 && iflag >= 0 && iflag <= 7 )
CHECK(Output::create(id,pin,iflag, 1))
REPLY("<O>\n")
ZZ(Z,id) CHECK(Output::remove(id))
REPLY("<O>\n")
ZZ(Z,id,pin,iflag) // Create Output
CHECK(id > 0 && iflag >= 0 && iflag <= 7 )
CHECK(Output::create(id,pin,iflag, 1))
REPLY("<O>\n")
ZZ(Z,id,active) // Set output
auto o = Output::get(id);
CHECK(o,Output not found)
o->activate(active);
REPLY("<Y %d %d>\n", id,active)
ZZ(Z,id) // Delete output
CHECK(Output::remove(id))
REPLY("<O>\n")
ZZ(Z) // <Z> list Output definitions
bool gotone = false;
for (auto *tt = Output::firstOutput; tt ; tt = tt->nextOutput){
gotone = true;
REPLY("<Y %d %d %d %d>\n",
tt->data.id, tt->data.pin, tt->data.flags, tt->data.active)
}
CHECK(gotone)
ZZ(D,ACK,ON) Diag::ACK = true;
ZZ(D,ACK,OFF) Diag::ACK = false;
ZZ(D,CABS) DCC::displayCabList(stream);
ZZ(D,RAM) DIAG(F("Free memory=%d"), DCCTimer::getMinimumFreeMemory());
ZZ(D,CMD,ON) Diag::CMD = true;
ZZ(D,CMD,OFF) Diag::CMD = false;
ZZ(D,RAILCOM,ON) Diag::RAILCOM = true;
ZZ(D,RAILCOM,OFF) Diag::RAILCOM = false;
ZZ(D,WIFI,ON) Diag::WIFI = true;
ZZ(D,WIFI,OFF) Diag::WIFI = false;
ZZ(D,ETHERNET,ON) Diag::ETHERNET = true;
ZZ(D,ETHERNET,OFF) Diag::ETHERNET = false;
ZZ(D,WIT,ON) Diag::WITHROTTLE = true;
ZZ(D,WIT,OFF) Diag::WITHROTTLE = false;
ZZ(D,LCN,ON) Diag::LCN = true;
ZZ(D,LCN,OFF) Diag::LCN = false;
ZZ(D,WEBSOCKET,ON) Diag::WEBSOCKET = true;
ZZ(D,WEBSOCKET,OFF) Diag::WEBSOCKET = false;
ZZ(D,ACK,ON) // Enable PROG track diagnostics
Diag::ACK = true;
ZZ(D,ACK,OFF) // Disable PROG track diagnostics
Diag::ACK = false;
ZZ(D,CABS) // Diagnostic display loco state table
DCC::displayCabList(stream);
ZZ(D,RAM) // Diagnostic display free RAM
DIAG(F("Free memory=%d"), DCCTimer::getMinimumFreeMemory());
ZZ(D,CMD,ON) // Enable command input diagnostics
Diag::CMD = true;
ZZ(D,CMD,OFF) // Disable command input diagnostics
Diag::CMD = false;
ZZ(D,RAILCOM,ON) // Enable Railcom diagnostics
Diag::RAILCOM = true;
ZZ(D,RAILCOM,OFF) // DIsable Railcom diagnostics
Diag::RAILCOM = false;
ZZ(D,WIFI,ON) // Enable Wifi diagnostics
Diag::WIFI = true;
ZZ(D,WIFI,OFF) // Disable Wifi diagnostics
Diag::WIFI = false;
ZZ(D,ETHERNET,ON) // Enable Ethernet diagnostics
Diag::ETHERNET = true;
ZZ(D,ETHERNET,OFF) // Disabel Ethernet diagnostics
Diag::ETHERNET = false;
ZZ(D,WIT,ON) // Enable Withrottle diagnostics
Diag::WITHROTTLE = true;
ZZ(D,WIT,OFF) // Disable Withrottle diagnostics
Diag::WITHROTTLE = false;
ZZ(D,LCN,ON) // Enable LCN Diagnostics
Diag::LCN = true;
ZZ(D,LCN,OFF) // Disabel LCN diagnostics
Diag::LCN = false;
ZZ(D,WEBSOCKET,ON) // Enable Websocket diagnostics
Diag::WEBSOCKET = true;
ZZ(D,WEBSOCKET,OFF) // Disable wensocket diagnostics
Diag::WEBSOCKET = false;
#ifndef DISABLE_EEPROM
ZZ(D,EEPROM,numentries) EEStore::dump(numentries);
ZZ(D,EEPROM,numentries) // Dump EEPROM contents
EEStore::dump(numentries);
#endif
ZZ(D,ANOUT,vpin,position) IODevice::writeAnalogue(vpin,position,0);
ZZ(D,ANOUT,vpin,position,profile) IODevice::writeAnalogue(vpin,position,profile);
ZZ(D,SERVO,vpin,position) IODevice::writeAnalogue(vpin,position,0);
ZZ(D,SERVO,vpin,position,profile) IODevice::writeAnalogue(vpin,position,profile);
ZZ(D,ANOUT,vpin,position) // see <z vpin position>
IODevice::writeAnalogue(vpin,position,0);
ZZ(D,ANOUT,vpin,position,profile) // see <z vpin position profile>
IODevice::writeAnalogue(vpin,position,profile);
ZZ(D,SERVO,vpin,position) // Test servo
IODevice::writeAnalogue(vpin,position,0);
ZZ(D,SERVO,vpin,position,profile) // Test servo
IODevice::writeAnalogue(vpin,position,profile);
ZZ(D,ANIN,vpin)// <D ANIN vpin> Display analogue input value
ZZ(D,ANIN,vpin) // Display analogue input value
DIAG(F("VPIN=%u value=%d"), vpin, IODevice::readAnalogue(vpin));
ZZ(D,HAL,SHOW) IODevice::DumpAll();
ZZ(D,HAL,RESET) IODevice::reset();
ZZ(D,TT,vpin,steps) IODevice::writeAnalogue(vpin,steps,0);
ZZ(D,TT,vpin,steps,activity) IODevice::writeAnalogue(vpin,steps,activity);
ZZ(D,HAL,SHOW) // Show HAL devices table
IODevice::DumpAll();
ZZ(D,HAL,RESET) // Reset all HAL devices
IODevice::reset();
ZZ(D,TT,vpin,steps) // Test turntable
IODevice::writeAnalogue(vpin,steps,0);
ZZ(D,TT,vpin,steps,activity) // Test turntable
IODevice::writeAnalogue(vpin,steps,activity);
ZZ(C,PROGBOOST) TrackManager::progTrackBoosted=true;
ZZ(C,RESET) DCCTimer::reset();
ZZ(C,SPEED28) DCC::setGlobalSpeedsteps(28); DIAG(F("28 Speedsteps"));
ZZ(C,SPEED128) DCC::setGlobalSpeedsteps(128); DIAG(F("128 Speedsteps"));
ZZ(C,RAILCOM,ON) DIAG(F("Railcom %S"),DCCWaveform::setRailcom(true,false)?F("ON"):F("OFF"));
ZZ(C,RAILCOM,OFF) DIAG(F("Railcom OFF")); DCCWaveform::setRailcom(false,false);
ZZ(C,RAILCOM,DEBUG) DIAG(F("Railcom %S"), DCCWaveform::setRailcom(true,true)?F("ON"):F("OFF"));
ZZ(C,PROGBOOST) // Configute PROG track boost
TrackManager::progTrackBoosted=true;
ZZ(C,RESET) // Reset and restart command station
DCCTimer::reset();
ZZ(C,SPEED28) // Set all DCC speed commands as 28 step to old decoders
DCC::setGlobalSpeedsteps(28); DIAG(F("28 Speedsteps"));
ZZ(C,SPEED128) // Set all DCC speed commands to 128 step (default)
DCC::setGlobalSpeedsteps(128); DIAG(F("128 Speedsteps"));
ZZ(C,RAILCOM,ON) // Enable Railcom cutout
DIAG(F("Railcom %S"),DCCWaveform::setRailcom(true,false)?F("ON"):F("OFF"));
ZZ(C,RAILCOM,OFF) // Disable Railcom cutout
DIAG(F("Railcom OFF")); DCCWaveform::setRailcom(false,false);
ZZ(C,RAILCOM,DEBUG) // Enable Railcom cutout for easy scope reading test
DIAG(F("Railcom %S"), DCCWaveform::setRailcom(true,true)?F("ON"):F("OFF"));
#ifndef DISABLE_PROG
ZZ(D,ACK,LIMIT,value) DCCACK::setAckLimit(value); LCD(1, F("Ack Limit=%dmA"), value);
ZZ(D,ACK,MIN,value,MS) DCCACK::setMinAckPulseDuration(value*1000L); LCD(1, F("Ack Min=%dmS"), value);
ZZ(D,ACK,MIN,value) DCCACK::setMinAckPulseDuration(value); LCD(1, F("Ack Min=%duS"), value);
ZZ(D,ACK,MAX,value,MS) DCCACK::setMaxAckPulseDuration(value*1000L); LCD(1, F("Ack Max=%dmS"), value);
ZZ(D,ACK,MAX,value) DCCACK::setMaxAckPulseDuration(value); LCD(1, F("Ack Max=%duS"), value);
ZZ(D,ACK,RETRY,value) DCCACK::setAckRetry(value); LCD(1, F("Ack Retry=%d"), value);
ZZ(D,ACK,LIMIT,value) // Set ACK detection limit mA
DCCACK::setAckLimit(value); LCD(1, F("Ack Limit=%dmA"), value);
ZZ(D,ACK,MIN,value,MS) // Set ACK minimum duration mS
DCCACK::setMinAckPulseDuration(value*1000L); LCD(1, F("Ack Min=%dmS"), value);
ZZ(D,ACK,MIN,value) // Set ACK minimum duration uS
DCCACK::setMinAckPulseDuration(value); LCD(1, F("Ack Min=%duS"), value);
ZZ(D,ACK,MAX,value,MS) // Set ACK maximum duration mS
DCCACK::setMaxAckPulseDuration(value*1000L); LCD(1, F("Ack Max=%dmS"), value);
ZZ(D,ACK,MAX,value) // Set ACK maximum duration uS
DCCACK::setMaxAckPulseDuration(value); LCD(1, F("Ack Max=%duS"), value);
ZZ(D,ACK,RETRY,value) // Set ACK retry count
DCCACK::setAckRetry(value); LCD(1, F("Ack Retry=%d"), value);
#endif
#if defined(ARDUINO_ARCH_ESP32)
// currently this only works on ESP32
ZZ(C,WIFI,marker1,ssid,marker2,password)
// <C WIFI SSID PASSWORD>
CHECK(marker1==0x7777 && marker2==0x7777)
WifiESP::setup((const char*)(com + ssid), (const char*)(com + password), WIFI_HOSTNAME, IP_PORT, WIFI_CHANNEL, WIFI_FORCE_AP);
// Dirty definition tricks because the executed check needs quote separation markers
// that should be invisible to the doc extractor.
// The equivalent documentation will be extracted from the commented line below
// and the matchedFormat is hand modified to the correct format which includes quotes.
// (documented version) ZZ(C,WIFI,"ssid","password") // reconfigure stored wifi credentials
ZZ_nodoc(C,WIFI,ssid,password)
CHECK(false, ssid and password must be in "quotes")
ZZ_nodoc(C,WIFI,marker1,ssid,marker2,password)
DCCEXParser::matchedCommandFormat=F("C,WIFI,\"ssid\",\"password\""); // for error reporting
CHECK(marker1==0x7777 && marker2==0x7777, ssid and password must be in "quotes")
WifiESP::setup((const char*)(com + ssid), (const char*)(com + password), WIFI_HOSTNAME, IP_PORT, WIFI_CHANNEL, WIFI_FORCE_AP);
#endif
ZZ(o,vpin) IODevice::write(abs(vpin),vpin>0);
ZZ(o,vpin,count) IODevice::writeRange(abs(vpin),vpin>0,count);
ZZ(o,vpin,r,g,b) CHECK(r>-0 && r<=0xff) CHECK(g>-0 && g<=0xff) CHECK(b>-0 && b<=0xff)
IODevice::writeAnalogueRange(abs(vpin),vpin>0,r<<8 | g,b,1);
ZZ(o,vpin,r,g,b,count) CHECK(r>-0 && r<=0xff) CHECK(g>-0 && g<=0xff) CHECK(b>-0 && b<=0xff)
IODevice::writeAnalogueRange(abs(vpin),vpin>0,r<<8 | g,b,count);
ZZ(o,vpin) // Set neopixel on(vpin>0) or off(vpin<0)
IODevice::write(abs(vpin),vpin>0);
ZZ(o,vpin,count) // Set multiple neopixels on(vpin>0) or off(vpin<0)
IODevice::writeRange(abs(vpin),vpin>0,count);
ZZ(o,vpin,r,g,b) // Set neopixel colour
CHECK(r>=0 && r<=0xff && g>=0 && g<=0xff && b>=0 && b<=0xff, r,g,b values range 0..255)
IODevice::writeAnalogueRange(abs(vpin),vpin>0,r<<8 | g,b,1);
ZZ(o,vpin,r,g,b,count) // Set multiple neopixels colour
CHECK(r>=0 && r<=0xff && g>=0 && g<=0xff && b>=0 && b<=0xff, r,g,b values range 0..255)
IODevice::writeAnalogueRange(abs(vpin),vpin>0,r<<8 | g,b,count);
ZZ(1) TrackManager::setTrackPower(TRACK_ALL, POWERMODE::ON);
ZZ(1,MAIN) TrackManager::setTrackPower(TRACK_MODE_MAIN, POWERMODE::ON);
ZZ(1) // Power ON all tracks
TrackManager::setTrackPower(TRACK_ALL, POWERMODE::ON);
ZZ(1,MAIN) // Power on MAIN track
TrackManager::setTrackPower(TRACK_MODE_MAIN, POWERMODE::ON);
#ifndef DISABLE_PROG
ZZ(1,PROG) TrackManager::setJoin(false); TrackManager::setTrackPower(TRACK_MODE_PROG, POWERMODE::ON);
ZZ(1,JOIN) TrackManager::setJoin(true); TrackManager::setTrackPower(TRACK_MODE_MAIN|TRACK_MODE_PROG, POWERMODE::ON);
ZZ(1,PROG) // Power on PROG track
TrackManager::setJoin(false); TrackManager::setTrackPower(TRACK_MODE_PROG, POWERMODE::ON);
ZZ(1,JOIN) // JOIN prog track to MAIN and power
TrackManager::setJoin(true); TrackManager::setTrackPower(TRACK_MODE_MAIN|TRACK_MODE_PROG, POWERMODE::ON);
#endif
ZZ(1,letter) CHECK(letter>='A' && letter<='H') TrackManager::setTrackPower(POWERMODE::ON, (byte)letter-'A');
ZZ(1,track) // Power on given track
CHECKTRACK
TrackManager::setTrackPower(POWERMODE::ON, (byte)track-'A');
ZZ(0) TrackManager::setJoin(false); TrackManager::setTrackPower(TRACK_ALL, POWERMODE::OFF);
ZZ(0,MAIN)TrackManager::setJoin(false); TrackManager::setTrackPower(TRACK_MODE_MAIN, POWERMODE::OFF);
ZZ(0,PROG) TrackManager::setJoin(false); TrackManager::progTrackBoosted=false;
// todo move to TrackManager Prog track boost mode will not outlive prog track off
TrackManager::setTrackPower(TRACK_MODE_PROG, POWERMODE::OFF);
ZZ(0,letter) CHECK(letter>='A' && letter <='H')
TrackManager::setJoin(false);
TrackManager::setTrackPower(POWERMODE::OFF, (byte)letter-'a');
ZZ(0) // Power off all tracks
TrackManager::setJoin(false);
TrackManager::setTrackPower(TRACK_ALL, POWERMODE::OFF);
ZZ(0,MAIN) // Power off MAIN track
TrackManager::setJoin(false);
TrackManager::setTrackPower(TRACK_MODE_MAIN, POWERMODE::OFF);
ZZ(0,PROG) // Power off PROG track
TrackManager::setJoin(false);
TrackManager::progTrackBoosted=false;
// todo move to TrackManager Prog track boost mode will not outlive prog track off
TrackManager::setTrackPower(TRACK_MODE_PROG, POWERMODE::OFF);
ZZ(0,track) // Power off given track
CHECKTRACK
TrackManager::setJoin(false);
TrackManager::setTrackPower(POWERMODE::OFF, (byte)track-'a');
ZZ(!) DCC::estopAll(); // this broadcasts speed 1(estop) and sets all reminders to speed 1.
ZZ(c) TrackManager::reportObsoleteCurrent(stream);
ZZ(c) // Report main track currect (Deprecated)
TrackManager::reportObsoleteCurrent(stream);
ZZ(a,address,subaddress,activate) DCC::setAccessory(address, subaddress,activate ^ accessoryCommandReverse);
ZZ(a,address,subaddress,activate,onoff) CHECK(onoff>=0 && onoff<-2)
DCC::setAccessory(address, subaddress,activate ^ accessoryCommandReverse ,onoff);
ZZ(a,linearaddress,activate)
DCC::setAccessory((linearaddress - 1) / 4 + 1,(linearaddress - 1) % 4 ,activate ^ accessoryCommandReverse);
ZZ(A,address,value) DCC::setExtendedAccessory(address,value);
ZZ(a,address,subaddress,activate) // Send DCC accessory command
CHECK(activate==0 || activate ==1, invalid activate 0..1 )
DCC::setAccessory(address, subaddress,activate ^ accessoryCommandReverse);
ZZ(a,address,subaddress,activate,onoff) // Send DCC accessory command with onoff control (TODO.. numbers)
CHECK(activate==0 || activate ==1, invalid activate 0..1 )
CHECK(onoff>=0 && onoff<=2,invalid onoff 0..2 )
DCC::setAccessory(address, subaddress,activate ^ accessoryCommandReverse ,onoff);
ZZ(a,linearaddress,activate) // send dcc accessory command
CHECK(activate==0 || activate ==1, invalid activate 0..1 )
DCC::setAccessory((linearaddress - 1) / 4 + 1,(linearaddress - 1) % 4 ,activate ^ accessoryCommandReverse);
ZZ(A,address,value) // Send DCC extended accessory (Aspect) command
DCC::setExtendedAccessory(address,value);
ZZ(w,cab,cv,value) DCC::writeCVByteMain(cab,cv,value);
ZZ(r,cab,cv)
CHECK(DCCWaveform::isRailcom())
EXPECT_CALLBACK
DCC::readCVByteMain(cab,cv,callback_r);
ZZ(b,cab,cv,bit,value) DCC::writeCVBitMain(cab,cv,bit,value);
ZZ(m,LINEAR) DCC::linearAcceleration=true;
ZZ(m,POWER) DCC::linearAcceleration=false;
ZZ(m,cab,momentum) CHECK(DCC::setMomentum(cab,momentum,momentum))
ZZ(m,cab,momentum,braking) CHECK(DCC::setMomentum(cab,momentum,braking))
ZZ(w,loco,cv,value) // POM write cv on main track
DCC::writeCVByteMain(loco,cv,value);
ZZ(r,loco,cv) // POM read cv on main track
CHECK(DCCWaveform::isRailcom(),Railcom not active)
EXPECT_CALLBACK
DCC::readCVByteMain(loco,cv,callback_r);
ZZ(b,loco,cv,bit,value) // POM write cv bit on main track
// todo check values
DCC::writeCVBitMain(loco,cv,bit,value);
ZZ(m,LINEAR) // Set Momentum algorithm to linear acceleration
DCC::linearAcceleration=true;
ZZ(m,POWER) // Set momentum algortithm to very based on difference between current speed and throttle seting
DCC::linearAcceleration=false;
ZZ(m,loco,momentum) // set momentum for loco (accel and braking)
CHECK(DCC::setMomentum(loco,momentum,momentum))
ZZ(m,loco,accelerating,braking) // set momentum for loco
CHECK(DCC::setMomentum(loco,accelerating,braking))
ZZ(W,cv,value,ignore1,ignore2) EXPECT_CALLBACK DCC::writeCVByte(cv,value, callback_W4);
ZZ(W,cab) EXPECT_CALLBACK DCC::setLocoId(cab,callback_Wloco);
ZZ(W,CONSIST,cab,REVERSE) EXPECT_CALLBACK DCC::setConsistId(cab,true,callback_Wconsist);
ZZ(W,CONSIST,cab) EXPECT_CALLBACK DCC::setConsistId(cab,false,callback_Wconsist);
ZZ(W,cv,value) EXPECT_CALLBACK DCC::writeCVByte(cv,value, callback_W);
ZZ(W,cv,value,bit) EXPECT_CALLBACK DCC::writeCVBit(cv,value,bit,callback_W);
ZZ(V,cv,value) EXPECT_CALLBACK DCC::verifyCVByte(cv,value, callback_Vbyte);
ZZ(V,cv,bit,value) EXPECT_CALLBACK DCC::verifyCVBit(cv,bit,value,callback_Vbit);
// todo check cv values on prog track commands and reorder for more sensible doco.
ZZ(W,cv,value,ignore1,ignore2) // (Deprecated) Write cv value on PROG track
EXPECT_CALLBACK DCC::writeCVByte(cv,value, callback_W4);
ZZ(W,cab) // Write loco address on PROG track
EXPECT_CALLBACK DCC::setLocoId(cab,callback_Wloco);
ZZ(W,CONSIST,cab,REVERSE) // Write consist address and reverse flag on PROG track
EXPECT_CALLBACK DCC::setConsistId(cab,true,callback_Wconsist);
ZZ(W,CONSIST,cab) // write consist address on PROG track
EXPECT_CALLBACK DCC::setConsistId(cab,false,callback_Wconsist);
ZZ(W,cv,value) // Write cv value on PROG track
EXPECT_CALLBACK DCC::writeCVByte(cv,value, callback_W);
ZZ(W,cv,value,bit) // Write cv bit on prog track
EXPECT_CALLBACK DCC::writeCVBit(cv,value,bit,callback_W);
ZZ(V,cv,value) // Fast read cv with expected value
EXPECT_CALLBACK DCC::verifyCVByte(cv,value, callback_Vbyte);
ZZ(V,cv,bit,value) // Fast read bit with expected value
EXPECT_CALLBACK DCC::verifyCVBit(cv,bit,value,callback_Vbit);
ZZ(B,cv,bit,value) EXPECT_CALLBACK DCC::writeCVBit(cv,bit,value,callback_B);
ZZ(R,cv,ignore1,ignore2) EXPECT_CALLBACK DCC::readCV(cv,callback_R);
ZZ(R,cv) EXPECT_CALLBACK DCC::verifyCVByte(cv, 0, callback_Vbyte);
ZZ(R) EXPECT_CALLBACK DCC::getLocoId(callback_Rloco);
ZZ(B,cv,bit,value) // Write cv bit
EXPECT_CALLBACK DCC::writeCVBit(cv,bit,value,callback_B);
ZZ(R,cv,ignore1,ignore2) // (Deprecated) read cv
EXPECT_CALLBACK DCC::readCV(cv,callback_R);
ZZ(R,cv) // Read cv
EXPECT_CALLBACK DCC::verifyCVByte(cv, 0, callback_Vbyte);
ZZ(R) // Read driveable loco id (may be long, short or consist)
EXPECT_CALLBACK DCC::getLocoId(callback_Rloco);
#ifndef DISABLE_VDPY
ZZ(@) CommandDistributor::setVirtualLCDSerial(stream);
ZZ_nodoc(@) CommandDistributor::setVirtualLCDSerial(stream);
REPLY( "<@ 0 0 \"DCC-EX v" VERSION "\">\n<@ 0 1 \"Lic GPLv3\">\n")
#endif
ZZ(-) DCC::forgetAllLocos();
ZZ(-,cab) DCC::forgetLoco(cab);
ZZ(F,cab,DCCFREQ,value) CHECK(value>=0 && value<=3) DCC::setDCFreq(cab,value);
ZZ(F,cab,function,value) CHECK(value==0 || value==1) DCC::setFn(cab,function,value);
ZZ(-) // Clear loco state and reminder table
DCC::forgetAllLocos();
ZZ(-,loco) // remove loco state amnd reminders
DCC::forgetLoco(loco);
ZZ(F,loco,DCCFREQ,value) // Set DC frequencey for loco
CHECK(value>=0 && value<=3) DCC::setDCFreq(loco,value);
ZZ(F,loco,function,value) // Set loco function ON/OFF
CHECK(value==0 || value==1) DCC::setFn(loco,function,value);
// ZZ(M,ignore,d0,d1,d2,d3,d4,d5) // Send up to 5 byte DCC packet on MAIN track (all d values in hex)
ZZ_nodoc(M,ignore,d0,d1,d2,d3,d4,d5) byte packet[]={(byte)d0,(byte)d1,(byte)d2,(byte)d3,(byte)d4,(byte)d5}; DCCWaveform::mainTrack.schedulePacket(packet,sizeof(packet),3);
ZZ_nodoc(M,ignore,d0,d1,d2,d3,d4) byte packet[]={(byte)d0,(byte)d1,(byte)d2,(byte)d3,(byte)d4}; DCCWaveform::mainTrack.schedulePacket(packet,sizeof(packet),3);
ZZ_nodoc(M,ignore,d0,d1,d2,d3) byte packet[]={(byte)d0,(byte)d1,(byte)d2,(byte)d3}; DCCWaveform::mainTrack.schedulePacket(packet,sizeof(packet),3);
ZZ_nodoc(M,ignore,d0,d1,d2) byte packet[]={(byte)d0,(byte)d1,(byte)d2}; DCCWaveform::mainTrack.schedulePacket(packet,sizeof(packet),3);
ZZ_nodoc(M,ignore,d0,d1) byte packet[]={(byte)d0,(byte)d1}; DCCWaveform::mainTrack.schedulePacket(packet,sizeof(packet),3);
// ZZ(P,ignore,d0,d1,d2,d3,d4,d5) // Send up to 5 byte DCC packet on PROG track (all d values in hex)
ZZ_nodoc(P,ignore,d0,d1,d2,d3,d4,d5) byte packet[]={(byte)d0,(byte)d1,(byte)d2,(byte)d3,(byte)d4,(byte)d5}; DCCWaveform::progTrack.schedulePacket(packet,sizeof(packet),3);
ZZ_nodoc(P,ignore,d0,d1,d2,d3,d4) byte packet[]={(byte)d0,(byte)d1,(byte)d2,(byte)d3,(byte)d4}; DCCWaveform::progTrack.schedulePacket(packet,sizeof(packet),3);
ZZ_nodoc(P,ignore,d0,d1,d2,d3) byte packet[]={(byte)d0,(byte)d1,(byte)d2,(byte)d3}; DCCWaveform::progTrack.schedulePacket(packet,sizeof(packet),3);
ZZ_nodoc(P,ignore,d0,d1,d2) byte packet[]={(byte)d0,(byte)d1,(byte)d2}; DCCWaveform::progTrack.schedulePacket(packet,sizeof(packet),3);
ZZ_nodoc(P,ignore,d0,d1) byte packet[]={(byte)d0,(byte)d1}; DCCWaveform::progTrack.schedulePacket(packet,sizeof(packet),3);
ZZ(M,ignore,d0,d1,d2,d3,d4,d5) byte packet[]={(byte)d0,(byte)d1,(byte)d2,(byte)d3,(byte)d4,(byte)d5}; DCCWaveform::mainTrack.schedulePacket(packet,sizeof(packet),3);
ZZ(M,ignore,d0,d1,d2,d3,d4) byte packet[]={(byte)d0,(byte)d1,(byte)d2,(byte)d3,(byte)d4}; DCCWaveform::mainTrack.schedulePacket(packet,sizeof(packet),3);
ZZ(M,ignore,d0,d1,d2,d3) byte packet[]={(byte)d0,(byte)d1,(byte)d2,(byte)d3}; DCCWaveform::mainTrack.schedulePacket(packet,sizeof(packet),3);
ZZ(M,ignore,d0,d1,d2) byte packet[]={(byte)d0,(byte)d1,(byte)d2}; DCCWaveform::mainTrack.schedulePacket(packet,sizeof(packet),3);
ZZ(M,ignore,d0,d1) byte packet[]={(byte)d0,(byte)d1}; DCCWaveform::mainTrack.schedulePacket(packet,sizeof(packet),3);
ZZ(P,ignore,d0,d1,d2,d3,d4,d5) byte packet[]={(byte)d0,(byte)d1,(byte)d2,(byte)d3,(byte)d4,(byte)d5}; DCCWaveform::progTrack.schedulePacket(packet,sizeof(packet),3);
ZZ(P,ignore,d0,d1,d2,d3,d4) byte packet[]={(byte)d0,(byte)d1,(byte)d2,(byte)d3,(byte)d4}; DCCWaveform::progTrack.schedulePacket(packet,sizeof(packet),3);
ZZ(P,ignore,d0,d1,d2,d3) byte packet[]={(byte)d0,(byte)d1,(byte)d2,(byte)d3}; DCCWaveform::progTrack.schedulePacket(packet,sizeof(packet),3);
ZZ(P,ignore,d0,d1,d2) byte packet[]={(byte)d0,(byte)d1,(byte)d2}; DCCWaveform::progTrack.schedulePacket(packet,sizeof(packet),3);
ZZ(P,ignore,d0,d1) byte packet[]={(byte)d0,(byte)d1}; DCCWaveform::progTrack.schedulePacket(packet,sizeof(packet),3);
ZZ(J,O) REPLY("<jO")
ZZ(J,O) // List turntable IDs
REPLY("<jO")
for (auto tto=Turntable::first(); tto; tto=tto->next()) if (!tto->isHidden()) REPLY(" %d",tto->getId())
REPLY(">\n")
ZZ(J,O,id) auto tto=Turntable::get(id);
ZZ(J,O,id) // List turntable state
auto tto=Turntable::get(id);
if (!tto || tto->isHidden()) {REPLY("<jO %d X>\n", id) return true;}
const FSH *todesc = nullptr;
#ifdef EXRAIL_ACTIVE
@ -438,7 +575,8 @@ ZZ(J,O,id) auto tto=Turntable::get(id);
if (todesc == nullptr) todesc = F("");
REPLY("<jO %d %d %d %d \"%S\">\n", id, tto->isEXTT(), tto->getPosition(), tto->getPositionCount(), todesc)
ZZ(J,P,id) auto tto=Turntable::get(id);
ZZ(J,P,id) // list turntable positions
auto tto=Turntable::get(id);
if (!tto || tto->isHidden()) {REPLY("<jP %d X>\n", id) return true;}
auto posCount = tto->getPositionCount();
if (posCount==0) {REPLY("<jP X>\n") return true;}
@ -453,24 +591,38 @@ ZZ(J,P,id) auto tto=Turntable::get(id);
}
// Track manager
ZZ(=) TrackManager::list(stream);
ZZ(=,track,MAIN) CHECK(track>='A' && track<='H') CHECK(TrackManager::setTrackMode(track,TRACK_MODE_MAIN))
ZZ(=,track,MAIN_INV) CHECK(track>='A' && track<='H') CHECK(TrackManager::setTrackMode(track,TRACK_MODE_MAIN_INV))
ZZ(=,track,MAIN_AUTO) CHECK(track>='A' && track<='H') CHECK(TrackManager::setTrackMode(track,TRACK_MODE_MAIN_AUTO))
ZZ(=,track,PROG) CHECK(track>='A' && track<='H') CHECK(TrackManager::setTrackMode(track,TRACK_MODE_PROG))
ZZ(=,track,OFF) CHECK(track>='A' && track<='H') CHECK(TrackManager::setTrackMode(track,TRACK_MODE_NONE))
ZZ(=,track,NONE) CHECK(track>='A' && track<='H') CHECK(TrackManager::setTrackMode(track,TRACK_MODE_NONE))
ZZ(=,track,EXT) CHECK(track>='A' && track<='H') CHECK(TrackManager::setTrackMode(track,TRACK_MODE_EXT))
ZZ(=) // list track manager states
TrackManager::list(stream);
ZZ(=,track,MAIN) // Set track to MAIN
CHECKTRACK CHECK(TrackManager::setTrackMode(track,TRACK_MODE_MAIN))
ZZ(=,track,MAIN_INV) // Set track to MAIN inverted polatity
CHECKTRACK CHECK(TrackManager::setTrackMode(track,TRACK_MODE_MAIN_INV))
ZZ(=,track,MAIN_AUTO) // Set track to MAIN with auto reversing
CHECKTRACK CHECK(TrackManager::setTrackMode(track,TRACK_MODE_MAIN_AUTO))
ZZ(=,track,PROG) // Set track to PROG
CHECKTRACK CHECK(TrackManager::setTrackMode(track,TRACK_MODE_PROG))
ZZ(=,track,OFF) // Set track power OFF
CHECKTRACK CHECK(TrackManager::setTrackMode(track,TRACK_MODE_NONE))
ZZ(=,track,NONE) // Set track no output
CHECKTRACK CHECK(TrackManager::setTrackMode(track,TRACK_MODE_NONE))
ZZ(=,track,EXT) // Set track to use external sync
CHECKTRACK CHECK(TrackManager::setTrackMode(track,TRACK_MODE_EXT))
#ifdef BOOSTER_INPUT
ZZ(=,track,BOOST) CHECK(TRACK_MODE_BOOST) CHECK(track>='A' && track<='H') CHECK(TrackManager::setTrackMode(track,TRACK_MODE_BOOST))
ZZ(=,track,BOOST_INV) CHECK(TRACK_MODE_BOOST_INV) CHECK(track>='A' && track<='H') CHECK(TrackManager::setTrackMode(track,TRACK_MODE_BOOST_INV))
ZZ(=,track,BOOST_AUTO) CHECK(TRACK_MODE_BOOST_AUTO) CHECK(track>='A' && track<='H') CHECK(TrackManager::setTrackMode(track,TRACK_MODE_BOOST_AUTO))
ZZ_nodoc(=,track,BOOST) CHECKTRACK CHECK(TrackManager::setTrackMode(track,TRACK_MODE_BOOST))
ZZ_nodoc(=,track,BOOST_INV) CHECKTRACK CHECK(TrackManager::setTrackMode(track,TRACK_MODE_BOOST_INV))
ZZ_nodoc(=,track,BOOST_AUTO) CHECKTRACK) CHECK(TrackManager::setTrackMode(track,TRACK_MODE_BOOST_AUTO))
#endif
ZZ(=,track,AUTO) CHECK(track>='A' && track<='H') CHECK(TrackManager::orTrackMode(track, TRACK_MODIFIER_AUTO))
ZZ(=,track,INV) CHECK(track>='A' && track<='H') CHECK(TrackManager::orTrackMode(track, TRACK_MODIFIER_INV))
ZZ(=,track,DC,locoid) CHECK(track>='A' && track<='H') CHECK(TrackManager::setTrackMode(track, TRACK_MODE_DC, locoid))
ZZ(=,track,DC_INV,locoid) CHECK(track>='A' && track<='H') CHECK(TrackManager::setTrackMode(track, TRACK_MODE_DC_INV, locoid))
ZZ(=,track,DCX,locoid) CHECK(track>='A' && track<='H') CHECK(TrackManager::setTrackMode(track, TRACK_MODE_DC_INV, locoid))
ZZ(=,track,AUTO) // Update track to auto reverse
CHECKTRACK CHECK(TrackManager::orTrackMode(track, TRACK_MODIFIER_AUTO))
ZZ(=,track,INV) // Update track to inverse polarity
CHECKTRACK CHECK(TrackManager::orTrackMode(track, TRACK_MODIFIER_INV))
ZZ(=,track,DC,locoid) // Set track to DC
CHECKTRACK CHECK(TrackManager::setTrackMode(track, TRACK_MODE_DC, locoid))
ZZ(=,track,DC_INV,locoid) // Set track to DC with inverted polarity
CHECKTRACK CHECK(TrackManager::setTrackMode(track, TRACK_MODE_DC_INV, locoid))
ZZ(=,track,DCX,locoid) // Set track to DC with inverted polarity
CHECKTRACK CHECK(TrackManager::setTrackMode(track, TRACK_MODE_DC_INV, locoid))
ZZEND

View File

@ -314,7 +314,9 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
// Because of check above we are now inside byte size
params = splitnum;
matchedCommandFormat = nullptr;
checkFailedFormat = nullptr;
if (filterCallback)
filterCallback(stream, opcode, params, p);
if (filterRMFTCallback && opcode!='\0')
@ -324,21 +326,25 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
if (opcode=='\0') return; // filterCallback asked us to ignore
matchedCommandFormat = F("none");
checkFailedFormat = matchedCommandFormat;
if (execute(com,stream, opcode, params, p, ringStream)) return;
// TODO magnificent diagnostics
StringFormatter::send(stream, F("<X>\n"));
DIAG(F("Command format <%<> failed CHECK(%S)"), matchedCommandFormat, checkFailedFormat);
if (opcode >= ' ' && opcode <= '~') {
DIAG(F("Opcode=%c params=%d"), opcode, params);
for (int i = 0; i < params; i++)
DIAG(F("p[%d]=%d (0x%x)"), i, p[i], p[i]);
} else {
DIAG(F("Unprintable %x"), opcode);
}
StringFormatter::send(stream, F("<X>\n")); // respond to caller with error
// Extended diagnostics
StringFormatter::send(USB_SERIAL, F("<*"));
if (matchedCommandFormat) {
// A command format was matched but a check failed
StringFormatter::send(USB_SERIAL,F(" Command format <%<> failed CHECK(%S)\n <"), matchedCommandFormat, checkFailedFormat);
}
else {
// No command format matched, so we have no idea what the command was
StringFormatter::send(USB_SERIAL,F(" Unrecognized command: <"));
}
if (opcode >= ' ' && opcode <= '~') StringFormatter::send(USB_SERIAL,F( "%c"), opcode);
else StringFormatter::send(USB_SERIAL,F("0x%x"), opcode);
for (int i = 0; i < params; i++) StringFormatter::send(USB_SERIAL,F(" %d"),p[i]);
StringFormatter::send(USB_SERIAL,F("> *>\n"));
}
bool DCCEXParser::setThrottle(int16_t cab,int16_t tspeed,int16_t direction) {

View File

@ -28,7 +28,8 @@
#define ZZBEGIN if (false) {
#define ZZEND return true; } return false;
#define CHECK(x) if (!(x)) { DCCEXParser::checkFailedFormat=F(#x); return false;}
//#define CHECK(x) if (!(x)) { DCCEXParser::checkFailedFormat=F(#x); return false;}
#define CHECK(x,...) if (!(x)) { DCCEXParser::checkFailedFormat=#__VA_ARGS__[0]?F(#__VA_ARGS__):F(#x); return false;}
#define REPLY(format,...) StringFormatter::send(stream,F(format), ##__VA_ARGS__);
#define EXPECT_CALLBACK CHECK(stashCallback(stream, p, ringStream))

View File

@ -170,14 +170,12 @@ bool RMFT2::parseCommands(Print * stream, byte opcode, byte params, int16_t p[])
ZZ(A,address,aspect) // Aspect may intercept or be left for normal parse
return signalAspectEvent(address,aspect);
ZZ(L) //LCC adapter introducing self
CHECK(streamLCC(stream))
CHECK(streamLCC(stream),no LCC/CBUS events)
ZZ(L,eventid) // loop through all possible sent/waited events
CHECK(eventid>=0 && eventid<countLCCLookup)
startNonRecursiveTask(F("LCC"),eventid,onLCCLookup[eventid]);
ZZ(J,A) REPLY("<jA")
routeLookup->stream(stream);
REPLY(">\n")
ZZ(J,A) REPLY("<jA") routeLookup->stream(stream); REPLY(">\n")
ZZ(J,A,id) REPLY("<jA %d %c \"%S\">\n",id, getRouteType(id), getRouteDescription(id));
if (compileFeatures & FEATURE_ROUTESTATE) {
// Send any non-default button states or captions
@ -213,12 +211,12 @@ bool RMFT2::parseCommands(Print * stream, byte opcode, byte params, int16_t p[])
}
ZZ(/,START,route)
auto pc=routeLookup->find(route);
CHECK(pc>=0)
CHECK(pc>=0,route not found)
new RMFT2(pc,0); // no cab for route start
ZZ(/,START,cab,route)
auto pc=routeLookup->find(route);
CHECK(pc>=0)
CHECK(pc>=0, route not found)
new RMFT2(pc,cab); // no cab for route start
ZZ(/,KILL,ALL) while (loopTask) loopTask->kill(F("KILL ALL")); // destructor changes loopTask
@ -236,10 +234,10 @@ bool RMFT2::parseCommands(Print * stream, byte opcode, byte params, int16_t p[])
if (task==loopTask) break;
}
CHECK(found);
ZZ(/,RESERVE,section) CHECK(setFlag(section,SECTION_FLAG))
ZZ(/,FREE,section) CHECK(setFlag(section,0,SECTION_FLAG))
ZZ(/,LATCH,latch) CHECK(setFlag(latch,LATCH_FLAG))
ZZ(/,UNLATCH,latch) CHECK(setFlag(latch,0,LATCH_FLAG))
ZZ(/,RESERVE,section) CHECK(setFlag(section,SECTION_FLAG),invalid section)
ZZ(/,FREE,section) CHECK(setFlag(section,0,SECTION_FLAG),invalid section)
ZZ(/,LATCH,latch) CHECK(setFlag(latch,LATCH_FLAG),invalid section)
ZZ(/,UNLATCH,latch) CHECK(setFlag(latch,0,LATCH_FLAG),invalid section)
ZZ(/,RED,signal) doSignal(signal,SIGNAL_RED);
ZZ(/,AMBER,signal) doSignal(signal,SIGNAL_AMBER);
ZZ(/,GREEN,signal) doSignal(signal,SIGNAL_GREEN);

View File

@ -0,0 +1,15 @@
#!/usr/bin/awk -f
{
# Match the pattern ZZ(something) // comments
if ($0 ~ /ZZ\([^)]*\)\s*\/\/.*/) {
# Extract "something" and "comments"
match($0, /ZZ\(([^)]*)\)\s*\/\/\s*(.*)/, arr);
something = arr[1];
comments = arr[2];
# Replace commas in "something" with spaces
gsub(/,/, " ", something);
# Print in the new format as a JS call
printf "ZZ('%s','%s');\n", something, comments;
}
}

View File

@ -199,7 +199,8 @@ void TrackManager::setDCSignal(int16_t cab, byte speedbyte) {
}
bool TrackManager::orTrackMode(byte trackToSet, TRACK_MODE mode) {
return setTrackMode(trackToSet, track[trackToSet]->getMode() | mode);
if (trackToSet>='A' && trackToSet<='H') trackToSet-='A';
return setTrackMode(trackToSet, track[trackToSet]->getMode() | mode);
}
bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr) {