Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

90 lines
2.9 KiB

Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
  1. /*
  2. Part of the TERMINAL MODULE
  3. Copyright (C) 2020 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
  4. */
  5. #pragma once
  6. #include <Arduino.h>
  7. #include "terminal_parsing.h"
  8. #include <unordered_map>
  9. #include <functional>
  10. #include <vector>
  11. namespace terminal {
  12. struct Terminal;
  13. // We need to be able to pass arbitrary Args structure into the command function
  14. // Like Embedis implementation, we only pass things that we actually use instead of complete obj instance
  15. struct CommandContext {
  16. std::vector<String> argv;
  17. size_t argc;
  18. Print& output;
  19. };
  20. struct Terminal {
  21. enum class Result {
  22. Error, // Genric error condition
  23. Command, // We successfully parsed the line and executed the callback specified via addCommand
  24. CommandNotFound, // ... similar to the above, but command was never added via addCommand
  25. BufferOverflow, // Command line processing failed, no \r\n / \n before buffer was filled
  26. Pending, // We got something in the buffer, but can't yet do anything with it
  27. NoInput // We got nothing in the buffer and stream read() returns -1
  28. };
  29. using CommandFunc = void(*)(const CommandContext&);
  30. using ProcessFunc = bool(*)(Result);
  31. // stream - see `stream` description below
  32. // buffer_size - set internal limit for the total command line length
  33. Terminal(Stream& stream, size_t buffer_size = 128) :
  34. stream(stream),
  35. buffer_size(buffer_size)
  36. {
  37. buffer.reserve(buffer_size);
  38. }
  39. static void addCommand(const String& name, CommandFunc func);
  40. static size_t commandsSize();
  41. static std::vector<String> commandNames();
  42. // Try to process a single line (until either `\r\n` or just `\n`)
  43. Result processLine();
  44. // Calls processLine() repeatedly.
  45. // Blocks until the stream no longer has any data available.
  46. // `process_f` will return each individual processLine() Result,
  47. // and we can either stop (false) or continue (true) the function.
  48. void process(ProcessFunc = defaultProcessFunc);
  49. private:
  50. static bool defaultProcessFunc(Result);
  51. // general input / output stream:
  52. // - stream.read() should return user iput
  53. // - stream.write() can be called from the command callback
  54. // - stream.write() can be called by us to show error messages
  55. Stream& stream;
  56. // buffer for the input stream, fixed in size
  57. std::vector<char> buffer;
  58. const size_t buffer_size;
  59. // TODO: every command is shared, instance should probably also have an
  60. // option to add 'private' commands list?
  61. // Note: we can save ~2.5KB by using std::vector<std::pair<String, CommandFunc>>
  62. // https://github.com/xoseperez/espurna/pull/2247#issuecomment-633689741
  63. static std::unordered_map<String, CommandFunc,
  64. parsing::LowercaseFnv1Hash<String>,
  65. parsing::LowercaseEquals<String>> commands;
  66. };
  67. }