diff --git a/DCCEXCommands.h b/DCCEXCommands.h index 88b3723..55f45e6 100644 --- a/DCCEXCommands.h +++ b/DCCEXCommands.h @@ -115,11 +115,7 @@ The REPLY( format, ...) macro sends a formatted string to the stream. These macros are included into the DCCEXParser::execute function so 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) -#define CHECKCV(cv) CHECK(cv>0 && cv<=255, Invalid cv 1..255) -#define CHECKCVVALUE(value) CHECK(value>=0 && value<=255, Invalid cv value 0..255) - + ZZBEGIN ZZ(#) // Request number of simultaneously supported locos @@ -127,7 +123,6 @@ ZZ(#) // Request number of simultaneously supported 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)) @@ -468,9 +463,7 @@ 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,track) // Power on given track - CHECKTRACK TrackManager::setTrackPower(POWERMODE::ON, (byte)track-'A'); - ZZ(0) // Power off all tracks TrackManager::setJoin(false); TrackManager::setTrackPower(TRACK_ALL, POWERMODE::OFF); @@ -483,7 +476,6 @@ ZZ(0,PROG) // Power off PROG track // 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'); @@ -504,16 +496,13 @@ ZZ(A,address,value) // Send DCC extended accessory (Aspect) command DCC::setExtendedAccessory(address,value); ZZ(w,loco,cv,value) // POM write cv on main track - CHECKCV(cv) CHECKCVVALUE(value) DCC::writeCVByteMain(loco,cv,value); ZZ(r,loco,cv) // POM read cv on main track - CHECKCV(cv) 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 - CHECKCV(cv) CHECK(value==0 || value==1) CHECK(bit>=0 && bit<=7,Invalid bit 0..7) - DCC::writeCVBitMain(loco,cv,bit,value); +ZZ(b,loco,cv,bit,bitvalue) // POM write cv bit on main track + DCC::writeCVBitMain(loco,cv,bit,bitvalue); ZZ(m,LINEAR) // Set Momentum algorithm to linear acceleration DCC::linearAcceleration=true; @@ -526,35 +515,26 @@ ZZ(m,loco,accelerating,braking) // set momentum for loco // todo reorder for more sensible doco. ZZ(W,cv,value,ignore1,ignore2) // (Deprecated) Write cv value on PROG track - CHECKCV(cv) CHECKCVVALUE(value) EXPECT_CALLBACK DCC::writeCVByte(cv,value, callback_W); -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,loco) // Write loco address on PROG track + EXPECT_CALLBACK DCC::setLocoId(loco,callback_Wloco); +ZZ(W,CONSIST,loco,REVERSE) // Write consist address and reverse flag on PROG track + EXPECT_CALLBACK DCC::setConsistId(loco,true,callback_Wconsist); +ZZ(W,CONSIST,loco) // write consist address on PROG track + EXPECT_CALLBACK DCC::setConsistId(loco,false,callback_Wconsist); ZZ(W,cv,value) // Write cv value on PROG track - CHECKCV(cv) CHECKCVVALUE(value) EXPECT_CALLBACK DCC::writeCVByte(cv,value, callback_W); -ZZ(W,cv,value,bit) // Write cv bit on prog track - CHECKCV(cv) CHECK(value==0 || value==1) CHECK(bit>=0 && bit<=7,Invalid bit 0..7) - EXPECT_CALLBACK DCC::writeCVBit(cv,value,bit,callback_W); +ZZ(W,cv,bitvalue,bit) // Write cv bit on prog track + EXPECT_CALLBACK DCC::writeCVBit(cv,bitvalue,bit,callback_W); ZZ(V,cv,value) // Fast read cv with expected value - CHECKCV(cv) CHECKCVVALUE(value) EXPECT_CALLBACK DCC::verifyCVByte(cv,value, callback_Vbyte); -ZZ(V,cv,bit,value) // Fast read bit with expected value - CHECKCV(cv) CHECK(value==0 || value==1) CHECK(bit>=0 && bit<=7,Invalid bit 0..7) - EXPECT_CALLBACK DCC::verifyCVBit(cv,bit,value,callback_Vbit); - -ZZ(B,cv,bit,value) // Write cv bit - CHECKCV(cv) CHECK(value==0 || value==1) CHECK(bit>=0 && bit<=7,Invalid bit 0..7) - EXPECT_CALLBACK DCC::writeCVBit(cv,bit,value,callback_B); -ZZ(R,cv,ignore1,ignore2) // (Deprecated) read cv - CHECKCV(cv) +ZZ(V,cv,bit,bitvalue) // Fast read bit with expected value + EXPECT_CALLBACK DCC::verifyCVBit(cv,bit,bitvalue,callback_Vbit); +ZZ(B,cv,bit,bitvalue) // Write cv bit + EXPECT_CALLBACK DCC::writeCVBit(cv,bit,bitvalue,callback_B); +ZZ(R,cv,ignore1,ignore2) // (Deprecated) read cv value on PROG track EXPECT_CALLBACK DCC::readCV(cv,callback_R); ZZ(R,cv) // Read cv - CHECKCV(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); @@ -568,10 +548,10 @@ 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(F,loco,DCCFREQ,freqvalue) // Set DC frequencey for loco + CHECK(freqvalue>=0 && freqvalue<=3) DCC::setDCFreq(loco,freqvalue); +ZZ(F,loco,function,fvalue) // Set loco function ON/OFF + CHECK(fvalue==0 || fvalue==1) DCC::setFn(loco,function,fvalue); // 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); @@ -620,34 +600,34 @@ ZZ(J,P,id) // list turntable positions ZZ(=) // list track manager states TrackManager::list(stream); ZZ(=,track,MAIN) // Set track to MAIN - CHECKTRACK CHECK(TrackManager::setTrackMode(track,TRACK_MODE_MAIN)) + 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)) + 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)) + CHECK(TrackManager::setTrackMode(track,TRACK_MODE_MAIN_AUTO)) ZZ(=,track,PROG) // Set track to PROG - CHECKTRACK CHECK(TrackManager::setTrackMode(track,TRACK_MODE_PROG)) + CHECK(TrackManager::setTrackMode(track,TRACK_MODE_PROG)) ZZ(=,track,OFF) // Set track power OFF - CHECKTRACK CHECK(TrackManager::setTrackMode(track,TRACK_MODE_NONE)) + CHECK(TrackManager::setTrackMode(track,TRACK_MODE_NONE)) ZZ(=,track,NONE) // Set track no output - CHECKTRACK CHECK(TrackManager::setTrackMode(track,TRACK_MODE_NONE)) + CHECK(TrackManager::setTrackMode(track,TRACK_MODE_NONE)) ZZ(=,track,EXT) // Set track to use external sync - CHECKTRACK CHECK(TrackManager::setTrackMode(track,TRACK_MODE_EXT)) + CHECK(TrackManager::setTrackMode(track,TRACK_MODE_EXT)) #ifdef BOOSTER_INPUT -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)) +ZZ_nodoc(=,track,BOOST) CHECK(TrackManager::setTrackMode(track,TRACK_MODE_BOOST)) +ZZ_nodoc(=,track,BOOST_INV) CHECK(TrackManager::setTrackMode(track,TRACK_MODE_BOOST_INV)) +ZZ_nodoc(=,track,BOOST_AUTO) CHECK(TrackManager::setTrackMode(track,TRACK_MODE_BOOST_AUTO)) #endif ZZ(=,track,AUTO) // Update track to auto reverse - CHECKTRACK CHECK(TrackManager::orTrackMode(track, TRACK_MODIFIER_AUTO)) + CHECK(TrackManager::orTrackMode(track, TRACK_MODIFIER_AUTO)) ZZ(=,track,INV) // Update track to inverse polarity - CHECKTRACK CHECK(TrackManager::orTrackMode(track, TRACK_MODIFIER_INV)) + CHECK(TrackManager::orTrackMode(track, TRACK_MODIFIER_INV)) ZZ(=,track,DC,locoid) // Set track to DC - CHECKTRACK CHECK(TrackManager::setTrackMode(track, TRACK_MODE_DC, locoid)) + 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)) + 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)) + CHECK(TrackManager::setTrackMode(track, TRACK_MODE_DC_INV, locoid)) ZZEND diff --git a/DCCEXParserMacros.h b/DCCEXParserMacros.h index 5e2a4af..0f80ca8 100644 --- a/DCCEXParserMacros.h +++ b/DCCEXParserMacros.h @@ -24,14 +24,49 @@ #define Z8(op,_1,_2,_3,_4,_5,_6,_7) ZPREP(op,7) ZZZ(0,_1) ZZZ(1,_2) ZZZ(2,_3) ZZZ(3,_4) ZZZ(4,_5) ZZZ(5,_6) ZZZ(6,_7) #define ZRIP(count) CONCAT(Z,count) -#define ZZ(...) ZRIP(FOR_EACH_NARG(__VA_ARGS__))(__VA_ARGS__) DCCEXParser::matchedCommandFormat = F( #__VA_ARGS__); + +#define ZC1(op) +#define ZC2(op,_1) ZZCHK(0,_1) +#define ZC3(op,_1,_2) ZZCHK(0,_1) ZZCHK(1,_2) +#define ZC4(op,_1,_2,_3) ZZCHK(0,_1) ZZCHK(1,_2) ZZCHK(2,_3) +#define ZC5(op,_1,_2,_3,_4) ZZCHK(0,_1) ZZCHK(1,_2) ZZCHK(2,_3) ZZCHK(3,_4) +#define ZC6(op,_1,_2,_3,_4,_5) ZZCHK(0,_1) ZZCHK(1,_2) ZZCHK(2,_3) ZZCHK(3,_4) ZZCHK(4,_5) +#define ZC7(op,_1,_2,_3,_4,_5,_6) ZZCHK(0,_1) ZZCHK(1,_2) ZZCHK(2,_3) ZZCHK(3,_4) ZZCHK(4,_5) ZZCHK(5,_6) +#define ZC8(op,_1,_2,_3,_4,_5,_6,_7) ZZCHK(0,_1) ZZCHK(1,_2) ZZCHK(2,_3) ZZCHK(3,_4) ZZCHK(4,_5) ZZCHK(5,_6) ZZCHK(6,_7) +#define ZCRIP(count) CONCAT(ZC,count) + +#define ZZ(...) \ + ZRIP(FOR_EACH_NARG(__VA_ARGS__))(__VA_ARGS__) \ + DCCEXParser::matchedCommandFormat = F( #__VA_ARGS__); \ + ZCRIP(FOR_EACH_NARG(__VA_ARGS__))(__VA_ARGS__) + + #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=#__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)) // helper macro to hide command from documentation extractor #define ZZ_nodoc ZZ +#define ZCHECK(_checkname,_index,_pname,_min,_max) \ + if (CONCAT(#_pname,_hk) == CONCAT(#_checkname,_hk) \ + && (p[_index]<_min || p[_index]>_max)) CHECK(false,_checkname _min .. _max) + +// Automatic range checks based on name of inserted parameter +#define ZZCHK(_index,_pname)\ +ZCHECK(loco,_index,_pname,0,10239) \ +ZCHECK(track,_index,_pname,'A','H') \ +ZCHECK(cv,_index,_pname,1,255) \ +ZCHECK(value,_index,_pname,0,255) \ +ZCHECK(bit,_index,_pname,0,7) \ +ZCHECK(bitvalue,_index,_pname,0,1) \ +ZCHECK(crapvalue,_index,_pname,99,100) \ + + + + + + +