Newer
Older
#include "cli.h"
void Cli::resetNextWord ()
{
_currentWordIndex = -1;
}
const char* Cli::findNextWord ()
{
if (_currentWordIndex >= 0)
// skip current word
while (_currentWordIndex < (int)_currentInput.length())
{
if (_currentInput[_currentWordIndex] == 32)
break;
_currentWordIndex++;
}
else
_currentWordIndex = 0;
// skip spaces
while (_currentWordIndex < (int)_currentInput.length())
{
if (_currentInput[_currentWordIndex] != 32)
break;
_currentWordIndex++;
}
const char* ret = _currentWordIndex < (int)_currentInput.length()? _currentInput.c_str() + _currentWordIndex: nullptr;
for (_currentWordEndIndex = _currentWordIndex; _currentInput.c_str()[_currentWordEndIndex] > 32; _currentWordEndIndex++);
#if 0
if (ret)
Serial.printf("# found new start of word '%s'\n", ret);
else
Serial.printf("# no more word?\n");
#endif
return ret;
}
void Cli::copyNextToTemp ()
{
if (!findNextWord())
{
_temp = emptyString;
return;
}
int start = _currentWordIndex;
int end = _currentWordEndIndex;
if (start < end)
_temp = _currentInput.substring(start, end);
else
_temp.clear();
}
bool Cli::kw (const __FlashStringHelper* keyWord, const char* input)
{
// return true if input matches keyWord
auto len = strlen_P((PGM_P)keyWord);
return len && strncasecmp_P((PGM_P)keyWord, input, len) == 0;
}
bool Cli::kw (const __FlashStringHelper* keyWord)
{
return kw(keyWord, _currentInput.c_str() + _currentWordIndex);
}
void Cli::syntax (const __FlashStringHelper* cmd)
{
syntax((PGM_P)cmd);
}
void Cli::syntax (const char* cmd)
{
if (!cmd || kw(F("AT"), cmd)) Serial.printf("%sAT -> OK\n", _msgHeader.c_str());
if (!cmd || kw(F("HELP"), cmd)) Serial.printf("%sHELP [CMD]\n", _msgHeader.c_str());
if (!cmd || kw(F("RAT"), cmd)) Serial.printf("%sRAT [ C <rate> <unit> ] - set or show rate (UM/MM/UH/MH)\n", _msgHeader.c_str());
if (!cmd || kw(F("VOL"), cmd)) Serial.printf("%sVOL [ <vol> | <unit> ] - set volume or unit (UL / ML)\n", _msgHeader.c_str());
if (!cmd || kw(F("MM"), cmd)) Serial.printf("%sMM [ <len> ] - set distance (mm)\n", _msgHeader.c_str());
if (!cmd || kw(F("DIA"), cmd)) Serial.printf("%sDIA [ <dia> ] - set or show inside syringe diameter (in mm)\n", _msgHeader.c_str());
if (!cmd || kw(F("DIR"), cmd)) Serial.printf("%sDIR [ INF | WDR ] - set or show direction (INFuse / WithDRaw)\n", _msgHeader.c_str());
if (!cmd || kw(F("FIL"), cmd)) Serial.printf("%sFIL - start filling using direction at rate for volume\n", _msgHeader.c_str());
if (!cmd || kw(F("STP"), cmd)) Serial.printf("%sSTP - stop\n", _msgHeader.c_str());
if (!cmd || kw(F("DIS"), cmd)) Serial.printf("%sDIS - show volume dispensed\n", _msgHeader.c_str());
if (!cmd || kw(F("ZERO"), cmd)) Serial.printf("%sZERO - go to stopper\n", _msgHeader.c_str());
if (!cmd || kw(F("SET0"), cmd)) Serial.printf("%sSET0 - change ZERO to current position\n", _msgHeader.c_str());
if (!cmd || kw(F("NLCK"), cmd)) Serial.printf("%sNLCK - try to get out from stopper\n", _msgHeader.c_str());
if (!cmd || kw(F("CONF"), cmd)) Serial.printf("%sCONF - show configuration\n", _msgHeader.c_str());
}
void Cli::answer (bool ok, const String& error_message) const
{
Serial.printf("%s%s", _msgHeader.c_str(), ok? "OK": "ERROR");
if (!ok && error_message.length())
Serial.printf(" (%s)", error_message.c_str());
Serial.printf("\n");
void Cli::checkEmergency ()
{
if (syringe.emergency())
Serial.printf("%s: in EMERGENCY state, try NLCK command\n", _msgHeader.c_str());
}
void Cli::loop (Stream& input)
{
while (true)
{
if (!input.available())
return;
int c = input.read();
if (c == 13)
break;
if (c >= 32 && c <= 127)
_currentInput += (char)c;
resetNextWord();
Syringe::Syringe_configuration_t conf = syringe.configuration();
if (findNextWord())
{
if (kw(F("AT")))
{
}
else if (kw(F("HELP")))
{
copyNextToTemp();
if (_temp.length())
syntax(_temp.c_str());
else
}
else if (kw(F("RAT")))
{
copyNextToTemp();
if (_temp.equalsIgnoreCase(F("C")))
{
copyNextToTemp();
conf.rate_ul_per_s = atof(_temp.c_str());
copyNextToTemp();
if (_temp.length() == 0)
conf.rate_ul_per_s *= 60; // default uL/mn
else if (_temp.equalsIgnoreCase(F("us")))
conf.rate_ul_per_s *= 1;
else if (_temp.equalsIgnoreCase(F("ms")))
conf.rate_ul_per_s *= 1000;
else if (_temp.equalsIgnoreCase(F("um")))
conf.rate_ul_per_s *= 60;
else if (_temp.equalsIgnoreCase(F("mm")))
else if (_temp.equalsIgnoreCase(F("uh")))
conf.rate_ul_per_s *= 3600;
else if (_temp.equalsIgnoreCase(F("mh")))
answer(syringe.configureSyringe(conf), F("invalid 'RAT C rate [unit]'"));
answer(false, F("RAT must be followed by C"));
Serial.printf("%sRAT: %g ul/mn\n", _msgHeader.c_str(),
syringe.configuration().rate_ul_per_s / 60);
}
else if (kw(F("VOL")))
{
{
if (isdigit(_temp[0]))
conf.volume_value = atof(_temp.c_str());
else if (_temp.equalsIgnoreCase(F("ul")))
conf.volume_unit_ul = 1;
else if (_temp.equalsIgnoreCase(F("ml")))
conf.volume_unit_ul = 1000;
else if (_temp.equalsIgnoreCase(F("cl")))
conf.volume_unit_ul = 10000;
else if (_temp.equalsIgnoreCase(F("dl")))
conf.volume_unit_ul = 100000;
else if (_temp.equalsIgnoreCase(F("l")))
conf.volume_unit_ul = 1000000;
answer(syringe.configureSyringe(conf), F("invalid volume or volume unit"));
Serial.printf("%sVOL: %g ul (%g mm) (target)\n", _msgHeader.c_str(), syringe.confToMm3(), syringe.confToMm());
}
else if (kw(F("MM")))
{
copyNextToTemp();
if (_temp.length())
{
if (isdigit(_temp[0]))
conf.volume_value = syringe.mmToMm3(atof(_temp.c_str()));
answer(syringe.configureSyringe(conf), F("invalid length"));
copyNextToTemp();
}
Serial.printf("%sVOL: %g ul (%g mm) (target)\n", _msgHeader.c_str(), syringe.confToMm3(), syringe.confToMm());
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
}
else if (kw(F("DIA")))
{
copyNextToTemp();
if (_temp.length())
{
conf.diameter_mm = atof(_temp.c_str());
answer(syringe.configureSyringe(conf), F("invalid diameter"));
}
Serial.printf("%sDIA: %g mm\n", _msgHeader.c_str(), syringe.configuration().diameter_mm);
}
else if (kw(F("DIR")))
{
copyNextToTemp();
if (_temp.length())
{
if (_temp.equalsIgnoreCase(F("inf")))
conf.direction = 1;
else if (_temp.equalsIgnoreCase(F("wdr")))
conf.direction = -1;
else
conf.direction = 0;
answer(syringe.configureSyringe(conf), F("invalid direction -> INF | WDR"));
}
Serial.printf("%sDIR: %s\n", _msgHeader.c_str(),
syringe.configuration().direction > 0? "INFuse": "WithDRaw");
}
else if (kw(F("FIL")))
{
syringe.fill();
answer(true);
}
else if (kw(F("STP")))
{
syringe.stayHere();
answer(true);
else if (kw(F("SET0")))
{
syringe.resetPosition();
answer(true);
}
else if (kw(F("DIS")))
{
Serial.printf("%sDIS: I %g W %g UL\n", _msgHeader.c_str(),
conf.direction > 0? syringe.mmToMm3(syringe.stepToMm(syringe.whereStep())): 0,
conf.direction > 0? 0 : syringe.mmToMm3(syringe.stepToMm(syringe.whereStep())));
}
else if (kw(F("ZERO")))
{
checkEmergency();
answer(true);
}
else if (kw(F("NLCK")))
{
answer(true);
}
else if (kw(F("CONF")))
{
syringe.showConfiguration(syringe.configuration());
answer(true);
}
Serial.printf("%sinvalid command '%s'\n", _msgHeader.c_str(), _currentInput.c_str() + _currentWordIndex);
if (findNextWord())
Serial.printf("%sgarbage at end of line: '%s'\n", _msgHeader.c_str(), _currentInput.c_str() + _currentWordIndex);
resetNextWord();
_currentInput.clear();
}