From 18dcbeff31cbbecde86f6b645d76fef8c819ebb5 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Wed, 2 Apr 2025 20:40:39 +0100 Subject: [PATCH] compiles --- CamCommands.h | 111 ++++++++++++ DCCEXParser.h | 4 +- DCCEXParserMacros.h | 4 +- EXRAIL2.h | 5 + EXRAIL2Parser.cpp | 419 +++++++++++++++++--------------------------- TrackManager.cpp | 2 +- 6 files changed, 283 insertions(+), 262 deletions(-) create mode 100644 CamCommands.h diff --git a/CamCommands.h b/CamCommands.h new file mode 100644 index 0000000..585f7e3 --- /dev/null +++ b/CamCommands.h @@ -0,0 +1,111 @@ +/* + * © 2023-2025, Barry Daniel + * © 2025 Chris Harlow + * All rights reserved. + * + * This file is part of CommandStation-EX + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . + */ + +//sensorCAM parser.cpp version 3.06 Jan 2025 +#include "DCCEXParser.h" +#include "CamParser.h" +#include "FSH.h" + +void camsend(byte camop,int16_t param1,int16_t param3) { + DIAG(F("CamParser: %d %c %d %d"),CAMBaseVpin,camop,param1,param3); + IODevice::writeAnalogue(CAMBaseVpin,param1,camop,param3); +} + + +// The CAMVPINS array will be filled by IO_EXSensorCam HAL drivers calling +// the CamParser::addVpin() function. +// The CAMBaseVpin is the one to be used when commands are given without a vpin. +VPIN CamParser::CAMBaseVpin = 0; // no vpins yet known +VPIN CamParser::CAMVPINS[] = {0,0,0,0}; // determines max # CAM's +int CamParser::vpcount=sizeof(CAMVPINS)/sizeof(CAMVPINS[0]); + + + ZZ(N) // lists current base vpin and others available + DIAG(F("Cam base vpin:%d"),CAMBaseVpin); + for (auto i=0;i=100 || (pin=0)) + CAMBaseVpin=(pin>=100? pin:CAMVPINS[pin]; + DIAG(F("CAM base Vpin:%d "),pin,CAMBaseVpin); + + ZZ(N,camop,p1) //send camop p1 + CHECK(STRCHR_P((const char *)F("ABFHILMNOPQRSTUV"),camop)) + camsend(camop,p1,999); + + ZZ(N,I,p1,p2) //send camop p1 p2 + camsend('I',p1,p2); + ZZ(N,J,p1,p2) //send camop p1 p2 + camsend('J',p1,p2); + ZZ(N,M,p1,p2) //send camop p1 p2 + camsend('M',p1,p2); + ZZ(N,N,p1,p2) //send camop p1 p2 + camsend('N',p1,p2); + ZZ(N,T,p1,p2) //send camop p1 p2 + camsend('T',p1,p2); + ZZ(N,vpin,rowY,colX) //send 0x80 row col + auto hold=CAMBaseVpin; + CAMBaseVpin=vpin; + camsend(0x80,rowY,colX); + CAMBaseVpin=hold; + + ZZ(N,A,id,row,col) + CHECK(col<=316 && col>=0) + CHECK(row<=236 && row>=0) + CHECK(id<=97 && id >=0) + auto hold=CAMBaseVpin; + CAMBaseVpin=CAMBaseVpin + (id/10)*8 + id%10; //translate from pseudo octal + camsend(0x80,row,col) + CAMBaseVpin=hold; + +void CamParser::addVpin(VPIN pin) { + // called by IO_EXSensorCam starting up a camera on a vpin + byte slot=255; + for (auto i=0;i commands to do the following: // - Implement RMFT specific commands/diagnostics // - Reject/modify JMRI commands that would interfere with RMFT processing void RMFT2::ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]) { - (void)stream; // avoid compiler warning if we don't access this parameter - - switch(opcode) { - - case 'D': - if (p[0]=="EXRAIL"_hk) { // - diag = paramCount==2 && (p[1]=="ON"_hk || p[1]==1); - opcode=0; - } - break; - - case '/': // New EXRAIL command - if (parseSlash(stream,paramCount,p)) opcode=0; - break; - - case 'A': // - if (paramCount!=2) break; - // Ask exrail if this is just changing the aspect on a - // predefined DCCX_SIGNAL. Because this will handle all - // the IFRED and ONRED type issues at the same time. - if (signalAspectEvent(p[0],p[1])) opcode=0; // all done - break; - - case 'L': - // This entire code block is compiled out if LLC macros not used - if (!(compileFeatures & FEATURE_LCC)) return; - static int lccProgCounter=0; - static int lccEventIndex=0; - - if (paramCount==0) { // LCC adapter introducing self - LCCSerial=stream; // now we know where to send events we raise - opcode=0; // flag command as intercepted - - // loop through all possible sent/waited events - for (int progCounter=lccProgCounter;; SKIPOP) { - byte exrailOpcode=GET_OPCODE; - switch (exrailOpcode) { - case OPCODE_ENDEXRAIL: - stream->print(F("\n")); // ready to roll - lccProgCounter=0; // allow a second pass - lccEventIndex=0; - return; - - case OPCODE_LCC: - StringFormatter::send(stream,F("\n"),getOperand(progCounter,0)); - SKIPOP; - lccProgCounter=progCounter; - return; - - case OPCODE_LCCX: // long form LCC - StringFormatter::send(stream,F("\n"), - getOperand(progCounter,1), - getOperand(progCounter,2), - getOperand(progCounter,3), - getOperand(progCounter,0) - ); - SKIPOP;SKIPOP;SKIPOP;SKIPOP; - lccProgCounter=progCounter; - return; - - case OPCODE_ACON: // CBUS ACON - case OPCODE_ACOF: // CBUS ACOF - StringFormatter::send(stream,F("\n"), - exrailOpcode==OPCODE_ACOF?'1':'0', - getOperand(progCounter,0),getOperand(progCounter,1)); - SKIPOP;SKIPOP; - lccProgCounter=progCounter; - return; - - // we stream the hex events we wish to listen to - // and at the same time build the event index looku. - - case OPCODE_ONLCC: - StringFormatter::send(stream,F("\n"), - lccEventIndex, - getOperand(progCounter,1), - getOperand(progCounter,2), - getOperand(progCounter,3), - getOperand(progCounter,0) - ); - SKIPOP;SKIPOP;SKIPOP;SKIPOP; - // start on handler at next - onLCCLookup[lccEventIndex]=progCounter; - lccEventIndex++; - lccProgCounter=progCounter; - return; - - case OPCODE_ONACON: - case OPCODE_ONACOF: - StringFormatter::send(stream,F("\n"), - lccEventIndex, - exrailOpcode==OPCODE_ONACOF?'1':'0', - getOperand(progCounter,0),getOperand(progCounter,1) - ); - SKIPOP;SKIPOP; - // start on handler at next - onLCCLookup[lccEventIndex]=progCounter; - lccEventIndex++; - lccProgCounter=progCounter; - return; - - default: - break; - } - } - } - if (paramCount==1) { // LCC event arrived from adapter - int16_t eventid=p[0]; - bool reject = eventid<0 || eventid>=countLCCLookup; - if (!reject) { - startNonRecursiveTask(F("LCC"),eventid,onLCCLookup[eventid]); - opcode=0; - } - } - break; - - case 'J': // throttle info commands - if (paramCount<1) return; - switch(p[0]) { - case "A"_hk: // returns automations/routes - if (paramCount==1) {// - StringFormatter::send(stream, F("stream(stream); - StringFormatter::send(stream, F(">\n")); - opcode=0; - return; - } - if (paramCount==2) { // - int16_t id=p[1]; - StringFormatter::send(stream,F("\n"), - id, getRouteType(id), getRouteDescription(id)); - - if (compileFeatures & FEATURE_ROUTESTATE) { - // Send any non-default button states or captions - int16_t statePos=routeLookup->findPosition(id); - if (statePos>=0) { - if (routeStateArray[statePos]) - StringFormatter::send(stream,F("\n"), id, routeStateArray[statePos]); - if (routeCaptionArray[statePos]) - StringFormatter::send(stream,F("\n"), id,routeCaptionArray[statePos]); - } - } - opcode=0; - return; - } - break; - - - case 'K': // Block enter - case 'k': // Block exit - if (paramCount!=2) break; - blockEvent(p[0],p[1],opcode=='K'); - opcode=0; - break; - - default: // other commands pass through - break; - } -} + if (parseCommands(stream,opcode,paramCount,p)) opcode='\0'; // command was handled by parseCommands() } -bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { - - if (paramCount==0) { // STATUS - StringFormatter::send(stream, F("<* EXRAIL STATUS")); - RMFT2 * task=loopTask; +void RMFT2::streamStatus(Print * stream) { + REPLY("<* EXRAIL STATUS") + auto task=loopTask; while(task) { if ((compileFeatures & FEATURE_BLINK) && (task->blinkState==blink_high || task->blinkState==blink_low)) { - StringFormatter::send(stream,F("\nID=%d,PC=%d,BLINK=%d"), - (int)(task->taskId),task->progCounter,task->blinkPin - ); + REPLY("\nID=%d,PC=%d,BLINK=%d",(int)(task->taskId),task->progCounter,task->blinkPin) } else { - StringFormatter::send(stream,F("\nID=%d,PC=%d,LOCO=%d %c"), - (int)(task->taskId),task->progCounter,task->loco, - task->invert?'I':' ' - ); + REPLY("\nID=%d,PC=%d,LOCO=%d %c",(int)(task->taskId),task->progCounter,task->loco,task->invert?'I':' ') } task=task->next; if (task==loopTask) break; @@ -221,9 +58,9 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { for (int id=0;id\n")); - return true; - } - switch (p[0]) { - case "PAUSE"_hk: // - if (paramCount!=1) return false; - { // pause all tasks + REPLY(" *>\n") +} +// +bool RMFT2::streamLCC(Print * stream) { + if (!(compileFeatures & FEATURE_LCC)) return false; + // This function is called to stream the LCC commands to the LCC adapter. + LCCSerial=stream; // now we know where to send events we raise + + static int lccProgCounter=0; + static int lccEventIndex=0; +for (int progCounter=lccProgCounter;; SKIPOP) { + byte exrailOpcode=GET_OPCODE; + switch (exrailOpcode) { + case OPCODE_ENDEXRAIL: + stream->print(F("\n")); // ready to roll + lccProgCounter=0; // allow a second pass + lccEventIndex=0; + return true; + + case OPCODE_LCC: + StringFormatter::send(stream,F("\n"),getOperand(progCounter,0)); + SKIPOP; + lccProgCounter=progCounter; + return true; + + case OPCODE_LCCX: // long form LCC + StringFormatter::send(stream,F("\n"), + getOperand(progCounter,1), + getOperand(progCounter,2), + getOperand(progCounter,3), + getOperand(progCounter,0) + ); + SKIPOP;SKIPOP;SKIPOP;SKIPOP; + lccProgCounter=progCounter; + return true; + + case OPCODE_ACON: // CBUS ACON + case OPCODE_ACOF: // CBUS ACOF + StringFormatter::send(stream,F("\n"), + exrailOpcode==OPCODE_ACOF?'1':'0', + getOperand(progCounter,0),getOperand(progCounter,1)); + SKIPOP;SKIPOP; + lccProgCounter=progCounter; + return true; + +// we stream the hex events we wish to listen to +// and at the same time build the event index looku. + + case OPCODE_ONLCC: + StringFormatter::send(stream,F("\n"), + lccEventIndex, + getOperand(progCounter,1), + getOperand(progCounter,2), + getOperand(progCounter,3), + getOperand(progCounter,0) + ); + SKIPOP;SKIPOP;SKIPOP;SKIPOP; + // start on handler at next + onLCCLookup[lccEventIndex]=progCounter; + lccEventIndex++; + lccProgCounter=progCounter; + return true; + + case OPCODE_ONACON: + case OPCODE_ONACOF: + StringFormatter::send(stream,F("\n"), + lccEventIndex, + exrailOpcode==OPCODE_ONACOF?'1':'0', + getOperand(progCounter,0),getOperand(progCounter,1) + ); + SKIPOP;SKIPOP; + // start on handler at next + onLCCLookup[lccEventIndex]=progCounter; + lccEventIndex++; + lccProgCounter=progCounter; + return true; + + default: + break; + } +} +return true; +} + +bool RMFT2::parseCommands(Print * stream, byte opcode, byte params, int16_t p[]) { + + ZZBEGIN + ZZ(D,EXRAIL,ON) diag=1; // - turn on diagnostics + ZZ(D,EXRAIL,OFF) diag=0; // - turn off diagnostics + ZZ(D,EXRAIL,onoff) diag=onoff; + 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)) + ZZ(L,eventid) // loop through all possible sent/waited events + CHECK(eventid>=0 && eventidstream(stream); + REPLY(">\n") + ZZ(J,A,id) REPLY("\n",id, getRouteType(id), getRouteDescription(id)); + if (compileFeatures & FEATURE_ROUTESTATE) { + // Send any non-default button states or captions + int16_t statePos=routeLookup->findPosition(id); + if (statePos>=0) { + if (routeStateArray[statePos]) + REPLY("\n", id, routeStateArray[statePos]); + if (routeCaptionArray[statePos]) + REPLY("\n", id,routeCaptionArray[statePos]); + } + } + ZZ(K,blockid,loco) blockEvent(blockid,loco,true); + ZZ(k,blockid,loco) blockEvent(blockid,loco,false); + ZZ(/) streamStatus(stream); + ZZ(/,PAUSE) + // 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; - - case "RESUME"_hk: // - if (paramCount!=1) return false; - pausingTask=NULL; - { // resume all tasks + DCC::estopAll(); // pause all locos on the track + pausingTask=(RMFT2 *)1; // Impossible task address + + ZZ(/,RESUME) + pausingTask=NULL; RMFT2 * task=loopTask; while(task) { task->resume(); task=task->next; if (task==loopTask) break; } - } - return true; - - - case "START"_hk: // - if (paramCount<2 || paramCount>3) return false; - { - int route=(paramCount==2) ? p[1] : p[2]; - uint16_t cab=(paramCount==2)? 0 : p[1]; - int pc=routeLookup->find(route); - if (pc<0) return false; - new RMFT2(pc,cab); - } - return true; - - default: - break; - } + ZZ(/,START,route) + auto pc=routeLookup->find(route); + CHECK(pc>=0) + new RMFT2(pc,0); // no cab for route start - // check KILL ALL here, otherwise the next validation confuses ALL with a flag - if (p[0]=="KILL"_hk && p[1]=="ALL"_hk) { - while (loopTask) loopTask->kill(F("KILL ALL")); // destructor changes loopTask - return true; - } - - // all other / commands take 1 parameter - if (paramCount!=2 ) return false; - - switch (p[0]) { - case "KILL"_hk: // Kill taskid|ALL - { - if ( p[1]<0 || p[1]>=MAX_FLAGS) return false; - RMFT2 * task=loopTask; - while(task) { - if (task->taskId==p[1]) { + ZZ(/,START,cab,route) + auto pc=routeLookup->find(route); + CHECK(pc>=0) + new RMFT2(pc,cab); // no cab for route start + + ZZ(/,KILL,ALL) while (loopTask) loopTask->kill(F("KILL ALL")); // destructor changes loopTask + ZZ(/,KILL,taskid) + CHECK(taskid>=0 && taskidtaskId==taskid) { + found=true; task->kill(F("KILL")); - return true; + break; } task=task->next; if (task==loopTask) break; } - } - return false; - - case "RESERVE"_hk: // force reserve a section - return setFlag(p[1],SECTION_FLAG); - - case "FREE"_hk: // force free a section - return setFlag(p[1],0,SECTION_FLAG); - - case "LATCH"_hk: - return setFlag(p[1], LATCH_FLAG); - - case "UNLATCH"_hk: - return setFlag(p[1], 0, LATCH_FLAG); - - case "RED"_hk: - doSignal(p[1],SIGNAL_RED); - return true; - - case "AMBER"_hk: - doSignal(p[1],SIGNAL_AMBER); - return true; - - case "GREEN"_hk: - doSignal(p[1],SIGNAL_GREEN); - return true; - - default: - return false; - } + 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(/,RED,signal) doSignal(signal,SIGNAL_RED); + ZZ(/,AMBER,signal) doSignal(signal,SIGNAL_AMBER); + ZZ(/,GREEN,signal) doSignal(signal,SIGNAL_GREEN); +ZZEND } diff --git a/TrackManager.cpp b/TrackManager.cpp index 0bf3673..0a4f60f 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -199,7 +199,7 @@ void TrackManager::setDCSignal(int16_t cab, byte speedbyte) { } bool TrackManager::orTrackMode(byte trackToSet, TRACK_MODE mode) { - setTrackMode(trackToSet, track[trackToSet]->getMode() | mode); + return setTrackMode(trackToSet, track[trackToSet]->getMode() | mode); } bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr) {