#include "cli.h" #include "fs.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); if (!cmd || kw(F("HELP"), cmd)) Serial.printf("%sHELP [CMD]\n", _msgHeader); if (!cmd || kw(F("RAT"), cmd)) Serial.printf("%sRAT [ C <rate> [<unit>] ] - set or show rate ([UM]/MM/UH/MH)\n", _msgHeader); if (!cmd || kw(F("RVOL"), cmd)) Serial.printf("%sVOL [ <vol> | <unit> ] - set remaining volume or unit (UL / ML)\n", _msgHeader); if (!cmd || kw(F("EVOL"), cmd)) Serial.printf("%sRVOL <vol> - set exchange volume\n", _msgHeader); if (!cmd || kw(F("FMM"), cmd)) Serial.printf("%sMM [ <len> ] - set distance from the front (mm)\n", _msgHeader); if (!cmd || kw(F("EMM"), cmd)) Serial.printf("%sRMM <len> - set exchange distance (mm)\n", _msgHeader); if (!cmd || kw(F("DIA"), cmd)) Serial.printf("%sDIA [ <dia> ] - set or show inside syringe diameter (in mm)\n", _msgHeader); if (!cmd || kw(F("ACC"), cmd)) Serial.printf("%sACC [ <mm/s/s> ] - set or show acceletaration (mm/s/s) (0<=>oo)\n", _msgHeader); if (!cmd || kw(F("RVM"), cmd)) Serial.printf("%sRVM [ <M4|M5> ] - set or show auger size\n", _msgHeader); if (!cmd || kw(F("DIR"), cmd)) Serial.printf("%sDIR [ INF | WDR ] - set or show direction (INFuse / WithDRaw)\n", _msgHeader); if (!cmd || kw(F("MOV"), cmd)) Serial.printf("%sFIL - start mmoving using direction at rate for volume\n", _msgHeader); if (!cmd || kw(F("STP"), cmd)) Serial.printf("%sSTP - stop\n", _msgHeader); if (!cmd || kw(F("DIS"), cmd)) Serial.printf("%sDIS - show volume dispensed\n", _msgHeader); if (!cmd || kw(F("ZERO"), cmd)) Serial.printf("%sZERO - go to stopper\n", _msgHeader); if (!cmd || kw(F("SET0"), cmd)) Serial.printf("%sSET0 - change ZERO to current position\n", _msgHeader); if (!cmd || kw(F("NLCK"), cmd)) Serial.printf("%sNLCK - try to get out from stopper\n", _msgHeader); if (!cmd || kw(F("CLKW"), cmd)) Serial.printf("%sCLKW [0 | 1] - 1: push = rotate clockwise\n", _msgHeader); if (!cmd || kw(F("CONF"), cmd)) Serial.printf("%sCONF - show configuration\n", _msgHeader); if (!cmd || kw(F("FS-LS"), cmd)) Serial.printf("%sFS-LS - show all the files\n", _msgHeader); if (!cmd || kw(F("FS-CAT"), cmd)) Serial.printf("%sFS-CAT - print a file\n", _msgHeader); if (!cmd || kw(F("FS-SAVE"), cmd)) Serial.printf("%sFS-SAVE - save the file syringe_filled_json\n", _msgHeader); if (!cmd || kw(F("FS-LOAD"), cmd)) Serial.printf("%sFS-LOAD - load the file syringe_filled_json\n", _msgHeader); } void Cli::answer (bool ok, const String& error_message) const { Serial.printf("%s%s", _msgHeader, ok? "OK": "ERROR"); if (!ok && error_message.length()) Serial.printf(" (%s)", error_message.c_str()); Serial.printf("\n"); } void Cli::check_emergency () { if (syringe_filled.get_emergency()) Serial.printf("%sin EMERGENCY state, try NLCK command\n", _msgHeader); } 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; // ignore other chars } resetNextWord(); if (findNextWord()) { if (kw(F("AT"))) { answer(true); } else if (kw(F("HELP"))) { copyNextToTemp(); if (_temp.length()) syntax(_temp.c_str()); else syntax(); } else if (kw(F("RAT"))) //*******************RATE*******************// { copyNextToTemp(); if (_temp.equalsIgnoreCase(F("C"))) { copyNextToTemp(); syringe_filled.set_exchange_throughput_uL_per_sec(atof(_temp.c_str())); copyNextToTemp(); float exchange_throughput_uL_per_sec = syringe_filled.get_exchange_throughput_uL_per_sec(); if (_temp.length() == 0) exchange_throughput_uL_per_sec /= 60; // default uL/mn else if (_temp.equalsIgnoreCase(F("us"))) exchange_throughput_uL_per_sec /= 1; else if (_temp.equalsIgnoreCase(F("ms"))) exchange_throughput_uL_per_sec *= 1000; else if (_temp.equalsIgnoreCase(F("um"))) exchange_throughput_uL_per_sec /= 60; else if (_temp.equalsIgnoreCase(F("mm"))) exchange_throughput_uL_per_sec *= 1000 / 60.0; else if (_temp.equalsIgnoreCase(F("uh"))) exchange_throughput_uL_per_sec /= 3600; else if (_temp.equalsIgnoreCase(F("mh"))) exchange_throughput_uL_per_sec *= 1000 / 3600.0; else exchange_throughput_uL_per_sec = -1; answer(syringe_filled.check_configuration(), F("invalid 'RAT C rate [unit]'")); } else if (_temp.length() > 0) answer(false, F("RAT must be followed by C")); Serial.printf("%sRAT: %g ul/mn\n", _msgHeader, syringe_filled.get_exchange_throughput_uL_per_sec() * 60); } else if (kw(F("RVOL"))) //*******************REMAINING_VOLUME*******************// { copyNextToTemp(); while (_temp.length()) { if (isdigit(_temp[0])) syringe_filled.set_remaining_volume_mL(atof(_temp.c_str())); else if (_temp.equalsIgnoreCase(F("ul"))) syringe_filled.set_remaining_volume_mL(atof(_temp.c_str())); else if (_temp.equalsIgnoreCase(F("ml"))) syringe_filled.set_remaining_volume_mL(atof(_temp.c_str())*1000); else if (_temp.equalsIgnoreCase(F("cl"))) syringe_filled.set_remaining_volume_mL(atof(_temp.c_str())*10000); else if (_temp.equalsIgnoreCase(F("dl"))) syringe_filled.set_remaining_volume_mL(atof(_temp.c_str())*100000); else if (_temp.equalsIgnoreCase(F("l"))) syringe_filled.set_remaining_volume_mL(atof(_temp.c_str())*1000000); answer(syringe_filled.check_configuration(), F("invalid remaining volume")); copyNextToTemp(); } Serial.printf("%sRVOL: %g ul (target)\n", _msgHeader, syringe_filled.get_remaining_volume_mL()); } else if (kw(F("EVOL"))) //*******************EXCHANGE_VOLUME*******************// { copyNextToTemp(); while (_temp.length()) { if (isdigit(_temp[0]) || _temp[0] == '-') syringe_filled.set_exchange_volume_mL(atof(_temp.c_str())); answer(syringe_filled.check_configuration(), F("invalid exchange volume")); copyNextToTemp(); } Serial.printf("%sEVOL: %g ul (target)\n", _msgHeader, syringe_filled.get_exchange_volume_mL()); } else if (kw(F("FMM"))) //*******************FRONT_DISTANCE*******************// { copyNextToTemp(); String name_syringe = syringe_filled.get_syringe_filled_data()["name_syringe"]; if (_temp.length()) { if (isdigit(_temp[0])) syringe_filled.set_remaining_volume_mL(syringe_filled.distance_mm_to_volume_mL(atof(_temp.c_str()))); answer(syringe_filled.check_configuration(), F("invalid length to the front")); copyNextToTemp(); } Serial.printf("%sFMM: %g ul (%g mm) (target)\n", _msgHeader, syringe_filled.get_remaining_volume_mL(), syringe_filled.volume_mL_to_distance_mm(syringe_filled.get_remaining_volume_mL())); } else if (kw(F("EMM"))) //*******************EXCHANGE_DISTANCE*******************// { copyNextToTemp(); String name_syringe = syringe_filled.get_syringe_filled_data()["name_syringe"]; if (_temp.length()) { if (isdigit(_temp[0]) || _temp[0] == '-') syringe_filled.set_exchange_volume_mL(syringe_filled.distance_mm_to_volume_mL(atof(_temp.c_str()))); answer(syringe_filled.check_configuration(), F("invalid exchange length")); copyNextToTemp(); } Serial.printf("%sRMM: %g ul (%g mm) (target)\n", _msgHeader, syringe_filled.get_exchange_volume_mL(), syringe_filled.volume_mL_to_distance_mm(syringe_filled.get_exchange_volume_mL())); } #if 0 else if (kw(F("DIA"))) //*******************INTERNAL_DIAMETER*******************//* { copyNextToTemp(); String name_syringe = syringe_filled.get_syringe_filled_data()["name_syringe"]; if (_temp.length()) { syringe.get_syringe_database()[name_syringe].set_internal_diameter_mm(name_syringe,atof(_temp.c_str()))); answer( syringe.check_configuration(name_syringe), F("invalid diameter")); } Serial.printf("%sDIA: %g mm\n", _msgHeader, syringe.get_syringe_database()[name_syringe]["internal_diameter_mm"]); } #endif else if (kw(F("RVM"))) { copyNextToTemp(); if (_temp == F("M4")) syringe_filled.set_mm_per_revolution(M4_MMREV); else if (_temp == F("M5")) syringe_filled.set_mm_per_revolution(M5_MMREV); else if (_temp.length() > 0) answer(false, F("invalid auger size: M4 or M5 are accepted")); Serial.printf("%sRVM: %g mm per revolution\n", _msgHeader, syringe_filled.get_mm_per_revolution()); } else if (kw(F("ACC"))) //*******************ACCELERATION*******************// { copyNextToTemp(); if (_temp.length()) syringe_filled.set_retain_acceleration_mm_per_sec_per_sec(atof(_temp.c_str())); Serial.printf("%sACC: %g mm per second per second\n", _msgHeader, syringe_filled.get_retain_acceleration_mm_per_sec_per_sec()); } else if (kw(F("DIR"))) //*******************DIRECTION*******************// { copyNextToTemp(); if (_temp.length()) { if (_temp.equalsIgnoreCase(F("inf"))) syringe_filled.set_push(true); else if (_temp.equalsIgnoreCase(F("wdr"))) syringe_filled.set_push(false); answer(syringe_filled.check_configuration(), F("invalid direction -> INF | WDR")); } Serial.printf("%sDIR: %s\n", _msgHeader, syringe_filled.get_push()? "INFuse": "WithDRaw"); } else if (kw(F("MOV"))) //*******************MOUVEMENT*******************// { check_emergency(); syringe_filled.start_exchange(); answer(true); } else if (kw(F("STP"))) { syringe_filled.stay_here(); answer(true); } else if (kw(F("SET0"))) { syringe_filled.reset_position(); answer(true); } else if (kw(F("DIS"))) //*******************DISTANCE_PARCOURU*******************// { Serial.printf("%sDIS: I %g W %g UL\n", _msgHeader, syringe_filled.get_push()? syringe_filled.distance_mm_to_volume_mL(syringe_filled.step_to_mm(syringe_filled.where_step())): 0, syringe_filled.get_push()? 0 : syringe_filled.distance_mm_to_volume_mL(syringe_filled.step_to_mm(syringe_filled.where_step()))); } else if (kw(F("ZERO"))) //*******************ZERO*******************// { check_emergency(); syringe_filled.go_to_zero(); answer(true); } else if (kw(F("NLCK"))) //*******************RUN_FROM_EMERGENCY*******************// { syringe_filled.run_from_emergency(); answer(true); } else if (kw(F("CLKW"))) //*******************CLOCKWISE_EQUALS_PUSH*******************// { copyNextToTemp(); if (_temp.length() == 1) { if (_temp[0] == '0' || _temp[0] == '1') { syringe_filled.set_clockwise_equals_push(_temp[0] == '1'); answer(true); } else answer(false, F("CLKW: invalid argument")); } Serial.printf("%sCLKW: push %sclockwise\n", _msgHeader, syringe_filled.get_clockwise_equals_push()? "": "anti-"); } else if (kw(F("CONF"))) //*******************SHOW_CONFIGURATION*******************// { syringe_filled.show_configuration(); answer(true); } else if (kw(F("FS-LS"))) //*******************SHOW_ALL_THE_FILES*******************// { ls(&Serial); answer(true); } else if (kw(F("FS-CAT"))) //*******************PRINT_A_FILE*******************// { copyNextToTemp(); if (_temp.length() > 0) { cat(_temp.c_str(), &Serial); answer(true); } else answer(false); } else if (kw(F("FS-SAVE"))) //*******************SAVE_THE_FILE_SYRINGEFILLEDJSON*******************// { syringe_filled.write_Json(); answer(true); } else if (kw(F("FS-LOAD"))) //*******************LOAD_THE_FILE_SYRINGEFILLEDJSON*******************// { syringe_filled.read_Json(); answer(true); } else { Serial.printf("%sinvalid command '%s'\n", _msgHeader, _currentInput.c_str() + _currentWordIndex); answer(false); } } if (findNextWord()) Serial.printf("%sgarbage at end of line: '%s'\n", _msgHeader, _currentInput.c_str() + _currentWordIndex); resetNextWord(); _currentInput.clear(); }