Implement functional skeleton of message protocol handling

This commit is contained in:
GHOSCHT 2023-03-11 16:52:25 +01:00
parent 93ff6c8f2e
commit e8ec695273
5 changed files with 408 additions and 0 deletions

View 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;
};

View 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

View 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
}
}

View 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

View 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