Implement functional skeleton of message protocol handling
This commit is contained in:
parent
7ff332ce1a
commit
af9f122a98
5 changed files with 408 additions and 0 deletions
15
modules/control/Firmware/src/Messages/Composer.cpp
Normal file
15
modules/control/Firmware/src/Messages/Composer.cpp
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#include "Composer.h"
|
||||||
|
|
||||||
|
bool message::composer::sendMessage(protocol::Message type,
|
||||||
|
etl::string_view content) {
|
||||||
|
if (content.length() > protocol::MAX_PAYLOAD_LEN) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint8_t length =
|
||||||
|
protocol::HEADER_LEN + static_cast<uint8_t>(content.length());
|
||||||
|
// Serial.write("HX");
|
||||||
|
// Serial.write(length);
|
||||||
|
// Serial.write(protocol::to_underlying(type));
|
||||||
|
// Serial.write(content.data());
|
||||||
|
return true;
|
||||||
|
};
|
9
modules/control/Firmware/src/Messages/Composer.h
Normal file
9
modules/control/Firmware/src/Messages/Composer.h
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#pragma once
|
||||||
|
#include "Protocol.h"
|
||||||
|
#include <etl/string.h>
|
||||||
|
|
||||||
|
namespace message {
|
||||||
|
namespace composer {
|
||||||
|
bool sendMessage(protocol::Message type, etl::string_view content);
|
||||||
|
} // namespace composer
|
||||||
|
} // namespace message
|
242
modules/control/Firmware/src/Messages/Parser.cpp
Normal file
242
modules/control/Firmware/src/Messages/Parser.cpp
Normal file
|
@ -0,0 +1,242 @@
|
||||||
|
#include "Parser.h"
|
||||||
|
|
||||||
|
message::parser::StateVisitor::StateVisitor(etl::byte_stream_reader &sreader)
|
||||||
|
: stream{sreader} {}
|
||||||
|
|
||||||
|
auto message::parser::StateVisitor::operator()(state::ModeSelection) -> State {
|
||||||
|
if (!stream.available<uint8_t>()) {
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mode = stream.read<uint8_t>();
|
||||||
|
if (!mode.has_value()) {
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mode.value()) {
|
||||||
|
case static_cast<uint8_t>(protocol::Mode::Message):
|
||||||
|
return state::Message{};
|
||||||
|
case static_cast<uint8_t>(protocol::Mode::Settings):
|
||||||
|
return state::Settings{};
|
||||||
|
case static_cast<uint8_t>(protocol::Mode::LightControl):
|
||||||
|
return state::LightControl{};
|
||||||
|
case static_cast<uint8_t>(protocol::Mode::Command):
|
||||||
|
return state::Command{};
|
||||||
|
default:
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto message::parser::StateVisitor::operator()(state::Message) -> State {
|
||||||
|
if (!stream.available<uint8_t>()) {
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mode = stream.read<uint8_t>();
|
||||||
|
if (!mode.has_value()) {
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mode.value()) {
|
||||||
|
case static_cast<uint8_t>(protocol::Message::LightData):
|
||||||
|
return state::MessageLightData{};
|
||||||
|
case static_cast<uint8_t>(protocol::Message::Info):
|
||||||
|
return state::MessageInfo{};
|
||||||
|
case static_cast<uint8_t>(protocol::Message::Warning):
|
||||||
|
return state::MessageWarning{};
|
||||||
|
case static_cast<uint8_t>(protocol::Message::Error):
|
||||||
|
return state::MessageError{};
|
||||||
|
case static_cast<uint8_t>(protocol::Message::Success):
|
||||||
|
return state::MessageSuccess{};
|
||||||
|
default:
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto message::parser::StateVisitor::operator()(state::MessageLightData)
|
||||||
|
-> State {
|
||||||
|
return state::Complete{};
|
||||||
|
}
|
||||||
|
auto message::parser::StateVisitor::operator()(state::MessageInfo) -> State {
|
||||||
|
return state::Complete{};
|
||||||
|
}
|
||||||
|
auto message::parser::StateVisitor::operator()(state::MessageWarning) -> State {
|
||||||
|
return state::Complete{};
|
||||||
|
}
|
||||||
|
auto message::parser::StateVisitor::operator()(state::MessageError) -> State {
|
||||||
|
return state::Complete{};
|
||||||
|
}
|
||||||
|
auto message::parser::StateVisitor::operator()(state::MessageSuccess) -> State {
|
||||||
|
return state::Complete{};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto message::parser::StateVisitor::operator()(state::Settings) -> State {
|
||||||
|
if (!stream.available<uint8_t>()) {
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mode = stream.read<uint8_t>();
|
||||||
|
if (!mode.has_value()) {
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mode.value()) {
|
||||||
|
case static_cast<uint8_t>(protocol::Settings::SetBaud):
|
||||||
|
return state::SettingsSetBaud{};
|
||||||
|
case static_cast<uint8_t>(protocol::Settings::SetWifiPassword):
|
||||||
|
return state::SettingsWifiPassword{};
|
||||||
|
case static_cast<uint8_t>(protocol::Settings::SetWifiSSID):
|
||||||
|
return state::SettingsWifiSSID{};
|
||||||
|
default:
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto message::parser::StateVisitor::operator()(state::SettingsSetBaud)
|
||||||
|
-> State {
|
||||||
|
return state::Complete{};
|
||||||
|
}
|
||||||
|
auto message::parser::StateVisitor::operator()(state::SettingsWifiPassword)
|
||||||
|
-> State {
|
||||||
|
return state::Complete{};
|
||||||
|
}
|
||||||
|
auto message::parser::StateVisitor::operator()(state::SettingsWifiSSID)
|
||||||
|
-> State {
|
||||||
|
return state::Complete{};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto message::parser::StateVisitor::operator()(state::LightControl) -> State {
|
||||||
|
if (!stream.available<uint8_t>()) {
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mode = stream.read<uint8_t>();
|
||||||
|
if (!mode.has_value()) {
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mode.value()) {
|
||||||
|
case static_cast<uint8_t>(protocol::LightControl::On):
|
||||||
|
return state::LightControlOn{};
|
||||||
|
case static_cast<uint8_t>(protocol::LightControl::Off):
|
||||||
|
return state::LightControlOff{};
|
||||||
|
case static_cast<uint8_t>(protocol::LightControl::Toggle):
|
||||||
|
return state::LightControlToggle{};
|
||||||
|
case static_cast<uint8_t>(protocol::LightControl::Increase):
|
||||||
|
return state::LightControlIncrease{};
|
||||||
|
case static_cast<uint8_t>(protocol::LightControl::Decrease):
|
||||||
|
return state::LightControlDecrease{};
|
||||||
|
default:
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto message::parser::StateVisitor::operator()(state::LightControlOn) -> State {
|
||||||
|
if (stream.available_bytes() > 0) {
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
return state::Complete{};
|
||||||
|
}
|
||||||
|
auto message::parser::StateVisitor::operator()(state::LightControlOff)
|
||||||
|
-> State {
|
||||||
|
if (stream.available_bytes() > 0) {
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
return state::Complete{};
|
||||||
|
}
|
||||||
|
auto message::parser::StateVisitor::operator()(state::LightControlToggle)
|
||||||
|
-> State {
|
||||||
|
if (stream.available_bytes() > 1) {
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
return state::Complete{};
|
||||||
|
}
|
||||||
|
auto message::parser::StateVisitor::operator()(state::LightControlIncrease)
|
||||||
|
-> State {
|
||||||
|
if (stream.available_bytes() > 1) {
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
return state::Complete{};
|
||||||
|
}
|
||||||
|
auto message::parser::StateVisitor::operator()(state::LightControlDecrease)
|
||||||
|
-> State {
|
||||||
|
if (stream.available_bytes() > 1) {
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
return state::Complete{};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto message::parser::StateVisitor::operator()(state::Command) -> State {
|
||||||
|
if (!stream.available<uint8_t>()) {
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mode = stream.read<uint8_t>();
|
||||||
|
if (!mode.has_value()) {
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mode.value()) {
|
||||||
|
case static_cast<uint8_t>(protocol::Command::RequestLightData):
|
||||||
|
return state::CommandRequestLightData{};
|
||||||
|
case static_cast<uint8_t>(protocol::Command::EnterConsoleFlashing):
|
||||||
|
return state::CommandEnterConsoleFlashing{};
|
||||||
|
case static_cast<uint8_t>(protocol::Command::ExitConsoleFlashing):
|
||||||
|
return state::CommandExitConsoleFlashing{};
|
||||||
|
case static_cast<uint8_t>(protocol::Command::PairBluetooth):
|
||||||
|
return state::CommandPairBluetooth{};
|
||||||
|
case static_cast<uint8_t>(protocol::Command::Help):
|
||||||
|
return state::CommandHelp{};
|
||||||
|
case static_cast<uint8_t>(protocol::Command::Version):
|
||||||
|
return state::CommandVersion{};
|
||||||
|
default:
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto message::parser::StateVisitor::operator()(state::CommandRequestLightData)
|
||||||
|
-> State {
|
||||||
|
return state::Complete{};
|
||||||
|
}
|
||||||
|
auto message::parser::StateVisitor::operator()(
|
||||||
|
state::CommandEnterConsoleFlashing) -> State {
|
||||||
|
if (stream.available_bytes() > 0) {
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
return state::Complete{};
|
||||||
|
}
|
||||||
|
auto message::parser::StateVisitor::operator()(
|
||||||
|
state::CommandExitConsoleFlashing) -> State {
|
||||||
|
if (stream.available_bytes() > 0) {
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
return state::Complete{};
|
||||||
|
}
|
||||||
|
auto message::parser::StateVisitor::operator()(state::CommandPairBluetooth)
|
||||||
|
-> State {
|
||||||
|
if (stream.available_bytes() > 0) {
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
return state::Complete{};
|
||||||
|
}
|
||||||
|
auto message::parser::StateVisitor::operator()(state::CommandHelp) -> State {
|
||||||
|
return state::Complete{};
|
||||||
|
}
|
||||||
|
auto message::parser::StateVisitor::operator()(state::CommandVersion) -> State {
|
||||||
|
return state::Complete{};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto message::parser::StateVisitor::operator()(state::Complete) -> State {
|
||||||
|
return state::Complete{};
|
||||||
|
}
|
||||||
|
auto message::parser::StateVisitor::operator()(state::Invalid) -> State {
|
||||||
|
return state::Invalid{};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto message::parser::parse(etl::byte_stream_reader &reader) -> void {
|
||||||
|
StateVisitor visitor{reader};
|
||||||
|
State state{state::ModeSelection{}};
|
||||||
|
while (!etl::holds_alternative<state::Invalid>(state) &&
|
||||||
|
!etl::holds_alternative<state::Complete>(state)) {
|
||||||
|
state = etl::visit(visitor, state);
|
||||||
|
}
|
||||||
|
if (etl::holds_alternative<state::Invalid>(state)) {
|
||||||
|
// TODO: error
|
||||||
|
}
|
||||||
|
}
|
88
modules/control/Firmware/src/Messages/Parser.h
Normal file
88
modules/control/Firmware/src/Messages/Parser.h
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
#pragma once
|
||||||
|
#include "Protocol.h"
|
||||||
|
#include <etl/byte_stream.h>
|
||||||
|
#include <etl/variant.h>
|
||||||
|
|
||||||
|
namespace message {
|
||||||
|
namespace parser {
|
||||||
|
namespace state {
|
||||||
|
struct Invalid {};
|
||||||
|
struct ModeSelection {};
|
||||||
|
struct Message {};
|
||||||
|
struct MessageLightData {};
|
||||||
|
struct MessageInfo {};
|
||||||
|
struct MessageWarning {};
|
||||||
|
struct MessageError {};
|
||||||
|
struct MessageSuccess {};
|
||||||
|
struct Settings {};
|
||||||
|
struct SettingsSetBaud {};
|
||||||
|
struct SettingsWifiPassword {};
|
||||||
|
struct SettingsWifiSSID {};
|
||||||
|
struct LightControl {};
|
||||||
|
struct LightControlOn {};
|
||||||
|
struct LightControlOff {};
|
||||||
|
struct LightControlToggle {};
|
||||||
|
struct LightControlIncrease {};
|
||||||
|
struct LightControlDecrease {};
|
||||||
|
struct Command {};
|
||||||
|
struct CommandRequestLightData {};
|
||||||
|
struct CommandEnterConsoleFlashing {};
|
||||||
|
struct CommandExitConsoleFlashing {};
|
||||||
|
struct CommandPairBluetooth {};
|
||||||
|
struct CommandHelp {};
|
||||||
|
struct CommandVersion {};
|
||||||
|
struct Complete {};
|
||||||
|
} // namespace state
|
||||||
|
|
||||||
|
using State = etl::variant<
|
||||||
|
state::Invalid, state::ModeSelection, state::Message,
|
||||||
|
state::MessageLightData, state::MessageInfo, state::MessageWarning,
|
||||||
|
state::MessageError, state::MessageSuccess, state::Settings,
|
||||||
|
state::SettingsSetBaud, state::SettingsWifiPassword,
|
||||||
|
state::SettingsWifiSSID, state::LightControl, state::LightControlOn,
|
||||||
|
state::LightControlOff, state::LightControlToggle,
|
||||||
|
state::LightControlIncrease, state::LightControlDecrease, state::Command,
|
||||||
|
state::CommandRequestLightData, state::CommandEnterConsoleFlashing,
|
||||||
|
state::CommandExitConsoleFlashing, state::CommandPairBluetooth,
|
||||||
|
state::CommandHelp, state::CommandVersion, state::Complete>;
|
||||||
|
|
||||||
|
void parse(etl::byte_stream_reader &reader);
|
||||||
|
|
||||||
|
class StateVisitor {
|
||||||
|
private:
|
||||||
|
etl::byte_stream_reader stream;
|
||||||
|
|
||||||
|
public:
|
||||||
|
StateVisitor(etl::byte_stream_reader &);
|
||||||
|
State operator()(state::ModeSelection);
|
||||||
|
State operator()(state::Message);
|
||||||
|
State operator()(state::MessageLightData);
|
||||||
|
State operator()(state::MessageInfo);
|
||||||
|
State operator()(state::MessageWarning);
|
||||||
|
State operator()(state::MessageError);
|
||||||
|
State operator()(state::MessageSuccess);
|
||||||
|
|
||||||
|
State operator()(state::Settings);
|
||||||
|
State operator()(state::SettingsSetBaud);
|
||||||
|
State operator()(state::SettingsWifiPassword);
|
||||||
|
State operator()(state::SettingsWifiSSID);
|
||||||
|
|
||||||
|
State operator()(state::LightControl);
|
||||||
|
State operator()(state::LightControlOn);
|
||||||
|
State operator()(state::LightControlOff);
|
||||||
|
State operator()(state::LightControlToggle);
|
||||||
|
State operator()(state::LightControlIncrease);
|
||||||
|
State operator()(state::LightControlDecrease);
|
||||||
|
State operator()(state::Command);
|
||||||
|
State operator()(state::CommandRequestLightData);
|
||||||
|
State operator()(state::CommandEnterConsoleFlashing);
|
||||||
|
State operator()(state::CommandExitConsoleFlashing);
|
||||||
|
State operator()(state::CommandPairBluetooth);
|
||||||
|
State operator()(state::CommandHelp);
|
||||||
|
State operator()(state::CommandVersion);
|
||||||
|
|
||||||
|
State operator()(state::Complete);
|
||||||
|
State operator()(state::Invalid);
|
||||||
|
};
|
||||||
|
} // namespace parser
|
||||||
|
} // namespace message
|
54
modules/control/Firmware/src/Messages/Protocol.h
Normal file
54
modules/control/Firmware/src/Messages/Protocol.h
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#pragma once
|
||||||
|
#include <etl/type_def.h>
|
||||||
|
#include <etl/type_traits.h>
|
||||||
|
#include <etl/variant.h>
|
||||||
|
#include <etl/visitor.h>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace message {
|
||||||
|
namespace protocol {
|
||||||
|
static const uint8_t HEADER_LEN = 5;
|
||||||
|
static const uint8_t MAX_TOTAL_LEN = 255;
|
||||||
|
static const uint8_t MAX_PAYLOAD_LEN = MAX_TOTAL_LEN - HEADER_LEN;
|
||||||
|
|
||||||
|
enum class Mode : uint8_t {
|
||||||
|
Message = 0x0,
|
||||||
|
Settings = 0x1,
|
||||||
|
LightControl = 0x2,
|
||||||
|
Command = 0x3
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Message : uint8_t {
|
||||||
|
LightData = 0x0,
|
||||||
|
Info = 0x1,
|
||||||
|
Warning = 0x2,
|
||||||
|
Error = 0x3,
|
||||||
|
Success = 0x5,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Settings : uint8_t {
|
||||||
|
SetBaud = 0x0,
|
||||||
|
SetWifiPassword = 0x1,
|
||||||
|
SetWifiSSID = 0x2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class LightControl : uint8_t {
|
||||||
|
On = 0x0,
|
||||||
|
Off = 0x1,
|
||||||
|
Toggle = 0x2,
|
||||||
|
Increase = 0x3,
|
||||||
|
Decrease = 0x4,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Command : uint8_t {
|
||||||
|
RequestLightData = 0x0,
|
||||||
|
EnterConsoleFlashing = 0x1,
|
||||||
|
ExitConsoleFlashing = 0x2,
|
||||||
|
PairBluetooth = 0x3,
|
||||||
|
Help = 0x4,
|
||||||
|
Version = 0x5,
|
||||||
|
};
|
||||||
|
|
||||||
|
using Submode = etl::variant<Mode, Message, Settings, LightControl, Command>;
|
||||||
|
} // namespace protocol
|
||||||
|
} // namespace message
|
Reference in a new issue