Open FFBoard
Open source force feedback firmware
CmdParser.cpp
Go to the documentation of this file.
1/*
2 * cmdparser.cpp
3 *
4 * Created on: 31.01.2020
5 * Author: Yannick
6 */
7
8#include <CmdParser.h>
9#include "ErrorHandler.h"
10#include "CommandHandler.h"
12#include "critical.hpp"
13
18CmdParser::CmdParser(const uint32_t bufferMaxCapacity) :
19 bufferMaxCapacity(std::min<size_t>(CMDPARSER_MAX_VALID_CAPACITY,bufferMaxCapacity)),
20 ringbuffer(RingBufferWrapper(std::min<size_t>(CMDPARSER_MAX_VALID_CAPACITY,bufferMaxCapacity)))
21{
23}
24
26}
27
30}
31
32
37bool CmdParser::add(char* Buf, uint32_t *Len){
38
39 if(clearBufferTimeout && HAL_GetTick() - lastAddTime > clearBufferTimeout ){
40 clear();
41 }
42
43 bool flag = false;
44 for(uint32_t i=0;i<*Len;i++){
45 // Replace end markers
46 char c = *(Buf+i);
47 if(c == '\n' || c == '\r' || c == ';'|| c == ' '){
48 *(Buf+i) = (uint8_t)';';
49 flag = true;
50 }
51 }
52
53 if(ringbuffer.freeSpace() > *Len){
54 ringbuffer.appendMultiple((uint8_t*)Buf, *Len);
55 lastAddTime = HAL_GetTick();
56 }else{
57 if(flag){
58 flag = false;
59 clear();
60 }
61 }
62
63 return flag;
64}
65
71void CmdParser::setClearBufferTimeout(uint32_t timeout){
72 this->clearBufferTimeout = timeout;
73}
74
77 return bufferMaxCapacity;
78 }
79
80 return std::max<int32_t>(ringbuffer.freeSpace(),0);
81}
82
83
84// Format: cls.instance.cmd<=|?|!><val?>
85bool CmdParser::parse(std::vector<ParsedCommand>& commands){
86
87 bool found = false;
88
89 uint32_t bufferlen = std::min<size_t>(CMDPARSER_MAX_VALID_CAPACITY,ringbuffer.length()); // Fixed bound for array
90 if(bufferlen==0 || bufferlen > bufferMaxCapacity){
91 return false;
92 }
93 char buf[bufferlen];
94 ringbuffer.peekMultiple((uint8_t*)buf, bufferlen); // Copy new portion out of rinbuffer
95
96 uint32_t pos = 0;
97 uint32_t lpos = 0;
98
99 while(pos<bufferlen){
100 if(buf[pos] == ';'){ // find end marker
101 if(lpos+2<pos){
102
103 //tokens.emplace_back(buf+lpos,buf+pos);
104 std::string word(buf+lpos,buf+pos);
105 // Begin parsing
106
107 ParsedCommand cmd;
108 //cmd.rawcmd = word;
109 uint8_t cmd_start = 0;
110
111 uint32_t point1 = word.find('.', 0);
112 uint32_t point2 = word.find('.', point1+1); // if has unique instance char
113
114
115 // cmdstart = <cls>.
116 std::string clsname;
117 if(point1 != std::string::npos){
118 cmd_start = point1+1;
119 clsname = word.substr(0, point1);
120 // Get the class id from the command handler list
121 //cmd.classId = CommandHandler::getClassIdFromName(word.substr(0, point1));
122 }
123 // cmdstart = <cls>.x.
124 if(point2 != std::string::npos){
125 //cmd.prefix = word[point1+1];
126 cmd.instance = word[point1+1] >= '0' ? word[point1+1] - '0' : 0;
127 cmd_start = point2+1; // after second point
128 }
129
130 std::string cmdstring;
131 if(word.back() == '?'){ // <cmd>?
132 cmd.type = CMDtype::get;
133 cmdstring = word.substr(cmd_start, word.length()-cmd_start - 1);
134
135 }else if(word.back() == '!'){
136 cmdstring = word.substr(cmd_start, word.length()-cmd_start - 1);
137 cmd.type = CMDtype::info;
138
139 }else if(word.back() == '='){
140 cmdstring = word.substr(cmd_start, word.length()-cmd_start);
141
142 cmd.type = CMDtype::err;
143
144 }else{
145 uint32_t peq = word.find('=', 0); // set
146 uint32_t pqm = word.find('?', 0); // read with var
147
148 // <cmd>\n
149 if(pqm == std::string::npos && peq == std::string::npos){
150 cmdstring = word.substr(cmd_start, word.length()-cmd_start);
151 cmd.type = CMDtype::get;
152
153 }else{ // More complex
154
155 // Check if conversion is even possible
156 bool validPqm = (pqm != std::string::npos && (std::isdigit(word[pqm+1]) || (std::isdigit(word[pqm+2]) && (word[pqm+1] == '-' || word[pqm+1] == '+')) || ( std::isxdigit(word[pqm+2]) && word[pqm+1] == 'x')));
157 bool validPeq = (peq != std::string::npos && (std::isdigit(word[peq+1]) || (std::isdigit(word[peq+2]) && (word[peq+1] == '-' || word[peq+1] == '+')) || ( std::isxdigit(word[peq+2]) && word[peq+1] == 'x')));
158
159 if(validPqm && validPeq && peq < pqm && (abs(pqm - peq) > 1)){ // <cmd>=<int>?<int>
160 // Dual
161 int64_t val;
162 int64_t val2;
163 if(word[pqm+1] == 'x'){
164 val2 = (int64_t)std::strtoll(&word[pqm+2],0,16);
165 }else{
166 val2 = (int64_t)std::strtoll(&word[pqm+1],0,10);
167 }
168
169 if(word[peq+1] == 'x'){
170 val = (int64_t)std::strtoll(word.substr(peq+2, pqm-peq).c_str(),0,16);
171 }else{
172 val = (int64_t)std::strtoll(word.substr(peq+1, pqm-peq).c_str(),0,10);
173 }
174
175 cmdstring = word.substr(cmd_start, peq-cmd_start);
176 cmd.type = CMDtype::setat;
177 cmd.val = val;
178 cmd.adr = val2;
179
180 }else if(validPqm){ // <cmd>?<int>
181 int64_t val;
182 if(word[pqm+1] == 'x'){
183 val = (int64_t)std::strtoll(&word[pqm+2],0,16);
184 }else{
185 val = (int64_t)std::strtoll(&word[pqm+1],0,10);
186 }
187
188 cmd.val = val;
189 cmd.type = CMDtype::getat;
190 cmdstring = word.substr(cmd_start, pqm-cmd_start);
191 cmd.adr = val;
192
193 }else if(validPeq){ // <cmd>=<int>
194 int64_t val;
195 if(word[peq+1] == 'x'){
196 val = (int64_t)std::strtoll(&word[peq+2],0,16);
197 }else{
198 val = (int64_t)std::strtoll(&word[peq+1],0,10);
199 }
200
201 cmd.val = val;
202 cmd.type = CMDtype::set;
203 cmdstring = word.substr(cmd_start, peq-cmd_start);
204
205 }else{
206 continue;
207 }
208 }
209 }
210
211
212 if(clsname.empty()){
213 clsname = "sys"; // No name passed. fallback to system commands
214 }
215
216 if(cmd.instance != 0xFF){
217 cmd.target = (CommandHandler::getHandlerFromClassName(clsname.c_str(),cmd.instance));
218 if(cmd.target != nullptr){
219 CmdHandlerCommanddef* cmdDef = cmd.target->getCommandFromName(cmdstring,CMDFLAG_HID_ONLY);
220
221 if(cmdDef){
222 cmd.cmdId = cmdDef->cmdId;
223 commands.push_back(cmd);
224 found = true;
225 }
226 }
227
228
229 }else{
230 // Targeting all classes with this name. Need to get the command id from all of them
231 std::vector<CommandHandler*> targets = CommandHandler::getHandlersFromClassName(clsname.c_str());
232
233 for(CommandHandler* target : targets){
234 CmdHandlerInfo* cmdhandlerinfo = target->getCommandHandlerInfo();
235 ParsedCommand newCmd = cmd;
236 newCmd.target = target;
237
238 if(targets.size() > 1) // Get unique instance id if multiple results
239 newCmd.instance = cmdhandlerinfo->instance;
240
241 CmdHandlerCommanddef* cmdDef = newCmd.target->getCommandFromName(cmdstring,CMDFLAG_HID_ONLY);
242 if(cmdDef){
243 newCmd.cmdId = cmdDef->cmdId;
244 commands.push_back(newCmd);
245 found = true;
246 }
247 }
248 }
249 if(!found){
251 error.info += ":"+clsname+"."+cmdstring;
253 }
254 }
255 // End parsing
256 lpos = pos+1; // Advance to last valid position after the ;
257 }
258 pos++;
259 }
260
261
262 // discard used chars from ringbuffer until the last found ;
264
265 return found;
266}
uint16_t val2
const uint32_t cmdId
RingBufferWrapper ringbuffer
Definition: CmdParser.h:45
int32_t bufferCapacity()
Definition: CmdParser.cpp:75
bool parse(std::vector< ParsedCommand > &commands)
Definition: CmdParser.cpp:85
virtual ~CmdParser()
Definition: CmdParser.cpp:25
CmdParser(const uint32_t bufferMaxCapacity=512)
Definition: CmdParser.cpp:18
void setClearBufferTimeout(uint32_t timeout)
Definition: CmdParser.cpp:71
uint32_t lastAddTime
Definition: CmdParser.h:43
void clear()
Definition: CmdParser.cpp:28
bool add(char *Buf, uint32_t *Len)
Definition: CmdParser.cpp:37
uint32_t clearBufferTimeout
Definition: CmdParser.h:42
const uint32_t bufferMaxCapacity
Definition: CmdParser.h:40
static std::vector< CommandHandler * > getHandlersFromClassName(const char *name)
static CommandHandler * getHandlerFromClassName(const char *name, const uint8_t instance=0xFF)
virtual CmdHandlerCommanddef * getCommandFromName(const std::string &cmd, uint32_t ignoredFlags=0)
static void addError(const Error &error)
std::string info
Definition: ErrorHandler.h:67
size_t length() noexcept
size_t freeSpace() noexcept
void clean() noexcept
size_t discardMultiple(size_t len) noexcept
size_t peekMultiple(uint8_t *dst, size_t len) noexcept
size_t appendMultiple(const uint8_t *data, size_t len) noexcept
uint8_t instance
CommandHandler * target
uint32_t cmdId