1+ /*
2+ * © 2023-2025, Barry Daniel
3+ * © 2025 Chris Harlow
4+ * All rights reserved.
5+ *
6+ * This file is part of CommandStation-EX
7+ *
8+ * This is free software: you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation, either version 3 of the License, or
11+ * (at your option) any later version.
12+ *
13+ * It is distributed in the hope that it will be useful,
14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+ * GNU General Public License for more details.
17+ *
18+ * You should have received a copy of the GNU General Public License
19+ * along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
20+ */
21+
22+ // sensorCAM parser.cpp devel. version 3.07 August 2025
23+ #include " DCCEXParser.h"
24+ #include " CamParser.h"
25+ #include " FSH.h"
26+
27+ // The CAMVPINS array will be filled by IO_EXSensorCam HAL drivers calling
28+ // the CamParser::addVpin() function.
29+ // The CAMBaseVpin is the one to be used when commands are given without a vpin.
30+ VPIN CamParser::CAMBaseVpin = 0 ; // no vpins yet known
31+ VPIN CamParser::CAMVPINS[] = {0 ,0 ,0 ,0 }; // determines max # CAM's
32+ int CamParser::vpcount=sizeof (CAMVPINS)/sizeof (CAMVPINS[0 ]);
33+
34+ void CamParser::parse (Print * stream, byte & opcode, byte & paramCount, int16_t p[]) {
35+ if (opcode!=' N' ) return ; // this is not for us.
36+ if (parseN (stream,paramCount,p)) opcode=0 ; // we have consumed this
37+ // If we fail, the caller will <X> the <N command.
38+ }
39+
40+ bool CamParser::parseN (Print * stream, byte paramCount, int16_t p[]) {
41+ (void )stream; // probably unused parameter
42+ if (CAMBaseVpin==0 ) CAMBaseVpin=CAMVPINS[0 ]; // default to CAM 1.
43+ VPIN vpin=CAMBaseVpin; // use current CAM selection
44+
45+ if (paramCount==0 ) {
46+ DIAG (F (" Cam base vpin:%d" ),CAMBaseVpin);
47+ for (auto i=0 ;i<vpcount;i++){
48+ if (CAMVPINS[i]==0 ) break ;
49+ DIAG (F (" EXSensorCam #%d vpin %d" ),i+1 ,CAMVPINS[i]);
50+ }
51+ return true ;
52+ }
53+ uint8_t camop=p[0 ]; // cam oprerator
54+ int param1=0 ;
55+ int16_t param3=9999 ; // =0 could invoke parameter changes. & -1 gives later errors
56+
57+ if (camop==' C' ){
58+ if (p[1 ]>=100 ) CAMBaseVpin=p[1 ];
59+ if (p[1 ]<=vpcount && p[1 ]>0 ) CAMBaseVpin=CAMVPINS[p[1 ]-1 ];
60+ DIAG (F (" CAM base Vpin: %c %d " ),p[0 ],CAMBaseVpin);
61+ return true ;
62+ }
63+
64+ if ((camop<=' a' ) && (camop>=' A' )){ // switch CAM# if p[1] or p[2] dictates (beware 'k')
65+ vpin=p[1 ];
66+ if (camop != ' A' )
67+ if (p[2 ] < vpcount*100 +99 ) { vpin=(p[1 ] > p[2 ]) ? p[1 ] : p[2 ] ; // get the larger.
68+ p[2 ]=p[2 ]%100 ; // strip off any CAM #
69+ }
70+ if ((vpin>=100 ) && (int (vpin)<=vpcount*100 +99 )) { // limits to CAM# 1 to vpcount
71+ CAMBaseVpin=CAMVPINS[vpin/100 -1 ];
72+ DIAG (F (" switching to CAM %d baseVpin:%d" ),vpin/100 ,CAMBaseVpin);
73+ p[1 ]=p[1 ]%100 ; // strip off any CAM #
74+ }
75+ vpin=CAMBaseVpin;
76+ }
77+
78+ if (CAMBaseVpin==0 ) return false ; // no cam defined
79+
80+ // send UPPER case to sensorCAM to flag binary data from a DCCEX-CS parser
81+ switch (paramCount) {
82+ case 1 : // <N ver> produces '^'
83+ if (STRCHR_P ((const char *)F (" EFGMQRVW^" ),camop) == nullptr ) return false ;
84+ if (camop==' Q' ) param3=10 ; // <NQ> for activation state of all 10 banks of sensors
85+ if (camop==' F' ) camop=' ]' ; // <NF> for Reset/Finish webCAM.
86+ break ; // F Coded as ']' else conflicts with <Nf %%>
87+
88+ case 2 : // <N camop p1>
89+ if (STRCHR_P ((const char *)F (" ABFHILMNOPQRSTUV" ),camop)==nullptr ) return false ;
90+ param1=p[1 ];
91+ break ;
92+
93+ case 3 : // <N vpin rowY colx > or <N cmd p1 p2>
94+ if (p[0 ]>=100 ) { // vpin - i.e. NOT 'A' through 'Z'
95+ if (p[1 ]>236 || p[1 ]<0 ) return false ; // row
96+ if (p[2 ]>316 || p[2 ]<0 ) return false ; // column
97+ camop=0x80 ; // special 'a' case for IO_SensorCAM
98+ vpin = p[0 ];
99+ }else if (STRCHR_P ((const char *)F (" IJMNT" ),camop) == nullptr ) return false ;
100+ camop=p[0 ];
101+ param1 = p[1 ];
102+ param3 = p[2 ];
103+ break ;
104+
105+ case 4 : // <N a id row col>
106+ if (camop!=' A' ) return false ; // must start with 'a'
107+ if (p[3 ]>316 || p[3 ]<0 ) return false ;
108+ if (p[2 ]>236 || p[2 ]<0 ) return false ;
109+ if (p[1 ]>97 || p[1 ]<0 ) return false ; // treat as bsNo.
110+ vpin = vpin + (p[1 ]/10 )*8 + p[1 ]%10 ; // translate p[1]
111+ camop=0x80 ; // special 'a' case for IO_SensorCAM
112+ param1=p[2 ]; // row
113+ param3=p[3 ]; // col
114+ break ;
115+
116+ default :
117+ return false ;
118+ }
119+ DIAG (F (" CamParser: %d %c %d %d" ),vpin,camop,param1,param3);
120+ IODevice::writeAnalogue (vpin,param1,camop,param3);
121+ return true ;
122+ }
123+
124+ void CamParser::addVpin (VPIN pin) {
125+ // called by IO_EXSensorCam starting up a camera on a vpin
126+ byte slot=255 ;
127+ for (auto i=0 ;i<vpcount && slot==255 ;i++) {
128+ if (CAMVPINS[i]==0 ) {
129+ slot=i;
130+ CAMVPINS[slot]=pin;
131+ }
132+ }
133+ if (slot==255 ) {
134+ DIAG (F (" No more than %d cameras supported" ),vpcount);
135+ return ;
136+ }
137+ if (slot==0 ) CAMBaseVpin=pin;
138+ DIAG (F (" CamParser Registered cam #%dvpin %d" ),slot+1 ,pin);
139+ // tell the DCCEXParser that we wish to filter commands
140+ DCCEXParser::setCamParserFilter (&parse);
141+ }
0 commit comments