Files
espurna/code/espurna/ws_utils.h
2025-04-03 14:51:53 +03:00

274 lines
6.3 KiB
C++

/*
Part of the WEBSOCKET MODULE
Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
Copyright (C) 2019-2021 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
*/
#pragma once
// Generic payload for indexed aka enumerable entries
// For the specific root container, add 'name'ed key and the function that will generate entry from index / id
// Each key is appended to the 'schema' list that will be used by the webui to know the actual settings key
//
// For example
// ```
// {
// name:
// {
// schema: ["one", "two", "three", "four"],
// values: [
// [1,2,3,4],
// [5,6,7,8],
// [9,0,1,2],
// [3,4,5,6]
// ]
// }
// }
// ```
//
// On the webui side, these become
// ```
// one0 => 1, two0 => 2, three0 => 3, four0 => 4
// one1 => 5, two1 => 6, three1 => 7, four1 => 8
// ...etc...
// ```
// Where each row in values is the specific index, and the key string is taken from the schema list
// Obviously, number of elements is always expected to match
#include <ArduinoJson.h>
#include "system_time.h"
#include "settings.h"
namespace espurna {
namespace web {
namespace ws {
// generic way to set up iterable payload with a schema
struct EnumerablePayload {
using Check = bool(*)(size_t);
using Generator = void(*)(JsonArray&, size_t);
struct Pair {
StringView name;
Generator generate;
};
using Pairs = std::initializer_list<Pair>;
EnumerablePayload(JsonObject& root, StringView name);
void operator()(StringView name, settings::Iota iota, Check, Pairs&&);
void operator()(StringView name, size_t iota_end, Pairs&& pairs) {
(*this)(name, settings::Iota { iota_end }, nullptr, std::move(pairs));
}
JsonObject& root() {
return _root;
}
private:
JsonObject& _root;
};
// payload generator for IndexedSettings
struct EnumerableConfig {
using Check = bool(*)(size_t);
using Setting = const settings::query::IndexedSetting;
using SourceFunc = Setting::ValueFunc;
using TargetFunc = void (*)(JsonArray&, size_t);
EnumerableConfig(JsonObject& root, StringView name);
void operator()(StringView name, settings::Iota iota, Check check, Setting* begin, Setting* end);
void operator()(StringView name, settings::Iota iota, Setting* begin, Setting* end) {
(*this)(name, iota, nullptr, begin, end);
}
template <typename T>
void operator()(StringView name, settings::Iota iota, T&& settings) {
(*this)(name, iota, std::begin(settings), std::end(settings));
}
template <typename T>
void operator()(StringView name, size_t iota_end, T&& settings) {
(*this)(name, settings::Iota{iota_end}, std::forward<T>(settings));
}
template <typename T>
void operator()(StringView name, size_t iota_end, Check check, T&& settings) {
(*this)(name, settings::Iota{iota_end}, check, std::begin(settings), std::end(settings));
}
JsonObject& root() {
return _root;
}
void replacement(SourceFunc, TargetFunc);
private:
struct Replacement {
SourceFunc source;
TargetFunc target;
};
std::vector<Replacement> _replacements;
JsonObject& _root;
};
struct EnumerableTypes {
template <typename T>
using Enumeration = settings::options::Enumeration<T>;
EnumerableTypes(JsonObject& root, StringView name);
template <typename T>
void operator()(const Enumeration<T>* begin, const Enumeration<T>* end) {
for (auto it = begin; it != end; ++it) {
(*this)((*it).numeric(), (*it).string());
}
}
template <typename T>
void operator()(const T& other) {
(*this)(std::begin(other), std::end(other));
}
void operator()(int, StringView);
private:
JsonArray& _root;
};
struct PostponedDebug;
struct PostponedPayload {
static constexpr size_t BufferHint = size_t{ JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(1) };
static constexpr size_t CountMax { 8 };
struct Flag {
explicit Flag(PostponedPayload&);
~Flag();
Flag(const Flag&) = delete;
Flag& operator=(const Flag&) = delete;
Flag(Flag&&) = delete;
Flag& operator=(Flag&&) = delete;
const char* data() const {
return _ref._data.c_str();
}
bool pending() const {
return _ref.pending();
}
private:
PostponedPayload& _ref;
};
PostponedPayload();
explicit PostponedPayload(uint32_t id) :
_id(id)
{}
bool pending() const {
return _pending;
}
bool post(bool connected);
bool post();
void buffer(const char*, size_t);
bool connected() const;
std::shared_ptr<Flag> make_flag();
private:
friend Flag;
friend PostponedDebug;
void buffer_impl(StringView);
void buffer_impl(const char*, size_t);
size_t _count{};
String _data;
bool _pending { false };
uint32_t _id{};
};
struct PostponedDebug : public PostponedPayload {
void buffer(const DebugPrefix&, const char*, size_t);
};
struct InplaceLog;
struct InplacePayload {
static constexpr size_t BufferHint = size_t{ JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(1) };
using Clock = espurna::time::CoreClock;
using Send = std::function<void(JsonObject&, String&)>;
static constexpr size_t CountMax { 8 };
static constexpr auto DefaultTimeout = duration::Seconds{ 2 };
static constexpr auto DefaultWait = duration::Milliseconds{ 100 };
InplacePayload() = delete;
InplacePayload(JsonObject& root, uint32_t id);
void reset();
bool connected() const;
bool can_send() const;
void write(const char*, size_t);
bool poll_send();
bool send();
void timeout(Clock::duration duration) {
_timeout = duration;
}
void wait_time(Clock::duration duration) {
_wait = duration;
}
private:
void write_impl(const char*, size_t);
void send_impl();
friend InplaceLog;
Clock::duration _timeout { DefaultTimeout };
Clock::duration _wait { DefaultWait };
String _data;
size_t _count{};
JsonObject& _root;
uint32_t _id;
};
struct InplaceLog : public InplacePayload {
InplaceLog(JsonObject&, uint32_t);
void write(const char* , size_t);
bool send();
private:
JsonArray& _log;
};
} // namespace ws
} // namespace web
} // namespace espurna