Skip to content

Commit

Permalink
WIP backend requests
Browse files Browse the repository at this point in the history
  • Loading branch information
PBrunot committed Aug 12, 2024
1 parent 2991884 commit 2d7e119
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 17 deletions.
3 changes: 3 additions & 0 deletions conf/conf.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ namespace fabomatic
/// @brief Backend reply (sub-topic of the full machine topic)
static constexpr std::string_view response_topic{"/reply"};

/// @brief Backend requests (sub-topic of the full machine topic)
static constexpr std::string_view request_topic{"/request"};

/// @brief Number of tries to get a reply from the backend
static constexpr auto MAX_TRIES{2};

Expand Down
1 change: 1 addition & 0 deletions include/BoardLogic.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ namespace fabomatic
[[nodiscard]] auto getMachine() const -> const Machine &;
[[nodiscard]] auto authorize(const card::uid_t uid) -> bool;
[[nodiscard]] auto getHostname() const -> const std::string;
auto processBackendRequests() -> void;

// copy reference
BoardLogic &operator=(const BoardLogic &board) = delete;
Expand Down
8 changes: 7 additions & 1 deletion include/FabBackend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,16 @@ namespace fabomatic
std::string mqtt_password{""};
std::string mqtt_client_name{""};

MQTTClientCallbackSimpleFunction callback;
MQTTClientCallbackSimpleFunction callback_resp;
MQTTClientCallbackSimpleFunction callback_req;
WiFiClient wifi_client;

std::string topic{""};
std::string response_topic{""};
std::string request_topic{""};
std::string last_query{""};
std::string last_reply{""};
std::string last_request{""};

bool online{false};
bool answer_pending{false};
Expand All @@ -55,6 +58,7 @@ namespace fabomatic
Buffer buffer;

auto messageReceived(String &topic, String &payload) -> void;
auto requestReceived(String &topic, String &payload) -> void;

template <typename QueryT>
[[nodiscard]] auto publish(const QueryT &payload) -> PublishResult;
Expand Down Expand Up @@ -86,6 +90,8 @@ namespace fabomatic
[[nodiscard]] auto transmitBuffer() -> bool;
[[nodiscard]] auto saveBuffer() -> bool;

[[nodiscard]] auto checkBackendRequest() -> std::optional<std::unique_ptr<MQTTInterface::BackendRequest>>;

auto connect() -> bool;
auto connectWiFi() -> bool;
auto loop() -> bool;
Expand Down
29 changes: 19 additions & 10 deletions include/MQTTtypes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace fabomatic::MQTTInterface
const card::uid_t uid;

UserQuery() = delete;
constexpr UserQuery(card::uid_t card_uid) : uid(card_uid){};
constexpr UserQuery(card::uid_t card_uid) : uid(card_uid) {};

[[nodiscard]] auto waitForReply() const -> bool override { return true; };
[[nodiscard]] auto payload() const -> const std::string override;
Expand Down Expand Up @@ -63,7 +63,7 @@ namespace fabomatic::MQTTInterface
const card::uid_t uid;

StartUseQuery() = delete;
constexpr StartUseQuery(card::uid_t card_uid) : uid(card_uid){};
constexpr StartUseQuery(card::uid_t card_uid) : uid(card_uid) {};

[[nodiscard]] auto payload() const -> const std::string override;
[[nodiscard]] auto waitForReply() const -> bool override { return true; };
Expand All @@ -83,7 +83,7 @@ namespace fabomatic::MQTTInterface
/// @param card_uid machine user card id
/// @param mid machine id
/// @param duration duration of usage, in seconds
constexpr StopUseQuery(card::uid_t card_uid, std::chrono::seconds duration) : uid(card_uid), duration_s(duration){};
constexpr StopUseQuery(card::uid_t card_uid, std::chrono::seconds duration) : uid(card_uid), duration_s(duration) {};
[[nodiscard]] auto payload() const -> const std::string override;
[[nodiscard]] auto waitForReply() const -> bool override { return true; };
[[nodiscard]] auto buffered() const -> bool override { return true; };
Expand All @@ -102,7 +102,7 @@ namespace fabomatic::MQTTInterface
/// @param card_uid machine user card id
/// @param mid machine id
/// @param duration duration of usage, in seconds
constexpr InUseQuery(card::uid_t card_uid, std::chrono::seconds duration) : uid(card_uid), duration_s(duration){};
constexpr InUseQuery(card::uid_t card_uid, std::chrono::seconds duration) : uid(card_uid), duration_s(duration) {};
[[nodiscard]] auto payload() const -> const std::string override;
[[nodiscard]] auto waitForReply() const -> bool override { return true; };
[[nodiscard]] auto buffered() const -> bool override { return false; };
Expand All @@ -115,7 +115,7 @@ namespace fabomatic::MQTTInterface
const card::uid_t uid;

RegisterMaintenanceQuery() = delete;
constexpr RegisterMaintenanceQuery(card::uid_t card_uid) : uid(card_uid){};
constexpr RegisterMaintenanceQuery(card::uid_t card_uid) : uid(card_uid) {};

[[nodiscard]] auto payload() const -> const std::string override;
[[nodiscard]] auto waitForReply() const -> bool override { return true; };
Expand All @@ -129,7 +129,7 @@ namespace fabomatic::MQTTInterface
const bool request_ok{false}; /* True if the request was processed by the server */

Response() = delete;
constexpr Response(bool result) : request_ok(result){};
constexpr Response(bool result) : request_ok(result) {};
};

/// @brief Result code for user authentication result
Expand All @@ -150,10 +150,10 @@ namespace fabomatic::MQTTInterface
FabUser::UserLevel user_level{FabUser::UserLevel::Unknown}; /* User priviledges */

UserResponse() = delete;
UserResponse(bool rok) : Response(rok){};
UserResponse(bool rok) : Response(rok) {};

UserResponse(bool rok, UserResult res) : Response(rok),
result(static_cast<uint8_t>(res)){};
result(static_cast<uint8_t>(res)) {};

[[nodiscard]] static auto fromJson(JsonDocument &doc) -> std::unique_ptr<UserResponse>;

Expand All @@ -174,7 +174,7 @@ namespace fabomatic::MQTTInterface
uint16_t grace{0}; /* Grace period in minutes */
std::string description{""}; /* Description of the expired maintenance */
MachineResponse() = delete;
MachineResponse(bool rok) : Response(rok){};
MachineResponse(bool rok) : Response(rok) {};

[[nodiscard]] static auto fromJson(JsonDocument &doc) -> std::unique_ptr<MachineResponse>;
};
Expand All @@ -184,10 +184,19 @@ namespace fabomatic::MQTTInterface
{
public:
SimpleResponse() = delete;
constexpr SimpleResponse(bool rok) : Response(rok){};
constexpr SimpleResponse(bool rok) : Response(rok) {};

[[nodiscard]] static auto fromJson(JsonDocument &doc) -> std::unique_ptr<SimpleResponse>;
};

/// @brief Class for server request
class BackendRequest
{
public:
card::uid_t requester;
std::string request_type;
BackendRequest(const card::uid_t &uid, const std::string &request) : requester{uid}, request_type(request) {};
[[nodiscard]] static auto fromJson(const JsonDocument &doc) -> std::optional<std::unique_ptr<BackendRequest>>;
};
} // namespace fabomatic::MQTTInterface
#endif // MQTTTYPES_HPP_
16 changes: 16 additions & 0 deletions include/card.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,22 @@ namespace fabomatic::card
return ss.str();
}

/**
* @brief Returns an UID from its string representation
* @param str hex string to convert
* @return uid value
*/
[[nodiscard]] inline auto str_uid(const std::string &str) -> card::uid_t
{
uint64_t ll_value;

std::stringstream ss{};
ss << std::hex << str;
ss >> ll_value;

return ll_value;
}

/**
* @brief Converts a UID from an array of bytes to a number
* @param uid array of bytes
Expand Down
4 changes: 3 additions & 1 deletion platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ name = fab-o-matic

[env]
platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.08.10/platform-espressif32.zip
#platform =https://github.com/pioarduino/platform-espressif32/releases/download/51.03.04/platform-espressif32.zip
framework = arduino
test_framework = unity
check_tool = clangtidy
Expand All @@ -31,8 +32,9 @@ lib_deps = https://github.com/PBrunot/LiquidCrystal.git#use_const
adafruit/Adafruit NeoPixel@^1.12.3
https://github.com/tzapu/[email protected]
ArduinoOTA
build_unflags = -std=gnu++11 -fexceptions
build_unflags = -std=gnu++11 -fexceptions -fno-lto
build_flags = -std=gnu++2b
-flto=auto
-g3
-Os
-I conf
Expand Down
22 changes: 22 additions & 0 deletions src/BoardLogic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -652,4 +652,26 @@ namespace fabomatic
std::to_string(conf::default_config::machine_id.id);
}

auto BoardLogic::processBackendRequests() -> void
{
auto &backend = getServer();
if (auto result = backend.checkBackendRequest(); result.has_value())
{
auto req = result->get();
FabUser fu{req->requester, "BACKEND", true, FabUser::UserLevel::FabAdmin};

if (req->request_type == "start")
{
logout();
if (!authorize(fu.card_uid))
{
ESP_LOGE(TAG, "Failure to execute start request from backend");
}
}
if (req->request_type == "stop")
{
logout();
}
}
}
} // namespace fabomatic
61 changes: 56 additions & 5 deletions src/FabBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,12 +245,24 @@ namespace fabomatic
*/
void FabBackend::messageReceived(String &s_topic, String &s_payload)
{
ESP_LOGI(TAG, "MQTT Client: Received on %s -> %s", s_topic.c_str(), s_payload.c_str());
ESP_LOGI(TAG, "MQTT Client: Reply received on %s -> %s", s_topic.c_str(), s_payload.c_str());

last_reply.assign(s_payload.c_str());
answer_pending = false;
}

/**
* @brief Callback function for received MQTT request.
*
* @param s_topic The topic the message was received on.
* @param s_payload The payload of the message.
*/
void FabBackend::requestReceived(String &s_topic, String &s_payload)
{
ESP_LOGI(TAG, "MQTT Client: Request received on %s -> %s", s_topic.c_str(), s_payload.c_str());
last_request.assign(s_payload.c_str());
}

/**
* @brief Connects to the WiFi network.
*
Expand Down Expand Up @@ -327,10 +339,13 @@ namespace fabomatic

client.begin(ip, conf::mqtt::PORT_NUMBER, wifi_client);

callback = [&](String &a, String &b)
callback_resp = [&](String &a, String &b)
{ return messageReceived(a, b); };

client.onMessage(callback);
callback_req = [&](String &a, String &b)
{ return requestReceived(a, b); };

client.onMessage(callback_resp);

if (!client.connect(mqtt_client_name.c_str(),
mqtt_user.c_str(),
Expand All @@ -343,7 +358,7 @@ namespace fabomatic
// Setup subscriptions
if (client.connected())
{
std::stringstream tmp_topic;
std::stringstream tmp_topic{};
tmp_topic << topic << conf::mqtt::response_topic;
response_topic.assign(tmp_topic.str());

Expand All @@ -356,6 +371,21 @@ namespace fabomatic
ESP_LOGD(TAG, "MQTT Client: subscribed to reply topic %s", response_topic.c_str());
online = true;
}

tmp_topic.clear();
tmp_topic << topic << conf::mqtt::request_topic;
request_topic.assign(tmp_topic.str());

if (!client.subscribe(request_topic.c_str()))
{
ESP_LOGE(TAG, "MQTT Client: failure to subscribe to requests topic %s", request_topic.c_str());
}
else
{
ESP_LOGD(TAG, "MQTT Client: subscribed to requests topic %s", request_topic.c_str());
online = true;
}

// Announce the board to the server
if (auto query = MQTTInterface::AliveQuery{}; publish(query) == PublishResult::PublishedWithoutAnswer)
{
Expand Down Expand Up @@ -622,7 +652,7 @@ namespace fabomatic
}
}
}
last_reply = "";
last_reply.clear();

ESP_LOGW(TAG, "Retransmittion completed, remaining messages=%d", buffer.count());
return !hasBufferedMsg();
Expand Down Expand Up @@ -655,4 +685,25 @@ namespace fabomatic
buffer.setChanged(false);
ESP_LOGI(TAG, "Loaded buffer with %d messages", buffer.count());
}

auto FabBackend::checkBackendRequest() -> std::optional<std::unique_ptr<MQTTInterface::BackendRequest>>
{
if (!this->last_request.empty())
{
const auto payload = last_request.c_str();
if (DeserializationError error = deserializeJson(doc, payload))
{
ESP_LOGE(TAG, "Failed to parse json: %s (%s)", payload, error.c_str());
last_request.clear();
return std::nullopt;
}

last_request.clear();

auto resp = MQTTInterface::BackendRequest::fromJson(doc);
return resp;
}
return std::nullopt;
}

} // namespace fabomatic
11 changes: 11 additions & 0 deletions src/MQTTtypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,15 @@ namespace fabomatic::MQTTInterface
auto response = std::make_unique<SimpleResponse>(doc["request_ok"].as<bool>());
return response;
}

auto BackendRequest::fromJson(const JsonDocument &doc) -> std::optional<std::unique_ptr<BackendRequest>>
{
if (doc.containsKey("request") && doc.containsKey("uid"))
{
const auto request = doc["request"].as<std::string>();
const auto struid = doc["uid"].as<std::string>();
return std::make_unique<BackendRequest>(card::str_uid(struid), request);
}
return std::nullopt;
}
} // namespace fabomatic::MQTTInterface
17 changes: 17 additions & 0 deletions test/test_chrono/test_chrono.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include "Tasks.hpp"
#include "Logging.hpp"
#include "Espressif.hpp"
#include "card.hpp"
#include "secrets.hpp"

[[maybe_unused]] static const char *TAG4 = "test_chrono";

Expand All @@ -27,6 +29,20 @@ namespace fabomatic::tests
// set stuff up here
}

void test_card(void)
{
for (const auto &[uid, level, name] : secrets::cards::whitelist)
{
auto str_val = card::uid_str(uid);
auto uid2 = card::str_uid(str_val);
auto arr1 = card::to_array(uid);
auto uid3 = card::from_array(arr1);

TEST_ASSERT_TRUE_MESSAGE(uid2 == uid, "Card UID: string conversion data loss");
TEST_ASSERT_TRUE_MESSAGE(uid3 == uid, "Card UID: array conversion data loss");
}
}

void test_steady_clock(void)
{
static constexpr auto nb_tests = 100;
Expand Down Expand Up @@ -58,6 +74,7 @@ void setup()
esp_log_level_set(TAG4, LOG_LOCAL_LEVEL);
UNITY_BEGIN();
RUN_TEST(fabomatic::tests::test_steady_clock);
RUN_TEST(fabomatic::tests::test_card);
UNITY_END(); // stop unit testing
}

Expand Down

0 comments on commit 2d7e119

Please sign in to comment.