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