Moved TRMNL stuff to its own file
This commit is contained in:
1
Makefile
1
Makefile
@@ -1,5 +1,6 @@
|
||||
srcs += main.cpp
|
||||
srcs += helpers.cpp
|
||||
srcs += TRMNL.cpp
|
||||
|
||||
all:
|
||||
g++ ${srcs} -o server
|
||||
|
||||
232
TRMNL.cpp
Normal file
232
TRMNL.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
#include "TRMNL.h"
|
||||
|
||||
using std::list;
|
||||
using std::map;
|
||||
using std::optional;
|
||||
using std::string;
|
||||
|
||||
using nlohmann::json;
|
||||
|
||||
TRMNL::TRMNL(const string& id,
|
||||
const string& api_key,
|
||||
const string& friendly_id,
|
||||
int refresh_rate)
|
||||
: m_id(id),
|
||||
m_api_key(api_key),
|
||||
m_friendly_id(friendly_id),
|
||||
m_refresh_rate(refresh_rate),
|
||||
m_update_handler()
|
||||
{}
|
||||
|
||||
const string& TRMNL::id() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
const string& TRMNL::api_key() const
|
||||
{
|
||||
return m_api_key;
|
||||
}
|
||||
|
||||
const string& TRMNL::friendly_id() const
|
||||
{
|
||||
return m_friendly_id;
|
||||
}
|
||||
|
||||
int TRMNL::refresh_rate() const
|
||||
{
|
||||
return m_refresh_rate;
|
||||
}
|
||||
|
||||
void TRMNL::id(const std::string& id)
|
||||
{
|
||||
m_id = id;
|
||||
if (m_update_handler)
|
||||
{
|
||||
m_update_handler(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void TRMNL::api_key(const std::string& api_key)
|
||||
{
|
||||
m_api_key = api_key;
|
||||
if (m_update_handler)
|
||||
{
|
||||
m_update_handler(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void TRMNL::friendly_id(const std::string& friendly_id)
|
||||
{
|
||||
m_friendly_id = friendly_id;
|
||||
if (m_update_handler)
|
||||
{
|
||||
m_update_handler(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void TRMNL::refresh_rate(int refresh_rate)
|
||||
{
|
||||
m_refresh_rate = refresh_rate;
|
||||
if (m_update_handler)
|
||||
{
|
||||
m_update_handler(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void TRMNL::set_update_handler(std::function<void (const TRMNL& trmnl)> handler)
|
||||
{
|
||||
m_update_handler = handler;
|
||||
}
|
||||
|
||||
string TRMNL::friendly_from_id(string id)
|
||||
{
|
||||
size_t pos = id.find(':');
|
||||
while (pos != string::npos)
|
||||
{
|
||||
id.erase(pos, 1);
|
||||
pos = id.find(':');
|
||||
}
|
||||
|
||||
if (id.size() <= 6)
|
||||
{
|
||||
return id;
|
||||
}
|
||||
else
|
||||
{
|
||||
return id.substr(id.size() - 6);
|
||||
}
|
||||
}
|
||||
|
||||
void to_json(json& j, const TRMNL& trmnl)
|
||||
{
|
||||
j = json{
|
||||
{"ID", trmnl.m_id},
|
||||
{"api_key", trmnl.m_api_key},
|
||||
{"friendly_id", trmnl.m_friendly_id},
|
||||
{"refresh_rate", trmnl.m_refresh_rate},
|
||||
};
|
||||
}
|
||||
|
||||
void from_json(const json& j, TRMNL& trmnl)
|
||||
{
|
||||
bool updated = false;
|
||||
try
|
||||
{
|
||||
j.at("ID").get_to(trmnl.m_id);
|
||||
updated = true;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{}
|
||||
|
||||
try
|
||||
{
|
||||
j.at("api_key").get_to(trmnl.m_api_key);
|
||||
updated = true;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{}
|
||||
|
||||
try
|
||||
{
|
||||
j.at("friendly_id").get_to(trmnl.m_friendly_id);
|
||||
updated = true;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{}
|
||||
|
||||
try
|
||||
{
|
||||
j.at("refresh_rate").get_to(trmnl.m_refresh_rate);
|
||||
updated = true;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{}
|
||||
|
||||
if (updated && trmnl.m_update_handler)
|
||||
{
|
||||
trmnl.m_update_handler(trmnl);
|
||||
}
|
||||
}
|
||||
|
||||
void TRMNLContainer::TRMNL_update_handler(const TRMNL& trmnl)
|
||||
{
|
||||
auto it = m_by_id.find(trmnl.m_id);
|
||||
if (it == m_by_id.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// An update is needed only if friendly ID changes
|
||||
if (0 == m_by_friendly.count(trmnl.m_friendly_id))
|
||||
{
|
||||
m_by_friendly[trmnl.m_friendly_id] = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
void TRMNLContainer::add_device(TRMNL trmnl)
|
||||
{
|
||||
if (trmnl.m_id.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
trmnl.set_update_handler([this](const TRMNL& trmnl){ TRMNL_update_handler(trmnl); });
|
||||
auto it = m_devices.insert(m_devices.end(), trmnl);
|
||||
|
||||
m_by_id[trmnl.m_id] = it;
|
||||
|
||||
if (!trmnl.m_friendly_id.empty())
|
||||
{
|
||||
m_by_friendly[trmnl.m_friendly_id] = it;
|
||||
}
|
||||
}
|
||||
|
||||
TRMNL* TRMNLContainer::get_device_by_id(const string& id)
|
||||
{
|
||||
TRMNL* result = nullptr;
|
||||
|
||||
if (0 != m_by_id.count(id))
|
||||
{
|
||||
result = &(*m_by_id[id]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TRMNL* TRMNLContainer::get_device_by_friendly(const string& friendly)
|
||||
{
|
||||
TRMNL* result = nullptr;
|
||||
|
||||
if (0 != m_by_friendly.count(friendly))
|
||||
{
|
||||
result = &(*m_by_friendly[friendly]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void TRMNLContainer::clear()
|
||||
{
|
||||
m_by_friendly.clear();
|
||||
m_by_id.clear();
|
||||
m_devices.clear();
|
||||
}
|
||||
|
||||
void to_json(json& j, const TRMNLContainer& cont)
|
||||
{
|
||||
j = cont.m_devices;
|
||||
}
|
||||
|
||||
void from_json(const json& j, TRMNLContainer& cont)
|
||||
{
|
||||
if (!j.is_array())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < j.size(); ++i)
|
||||
{
|
||||
cont.add_device(j[i]);
|
||||
}
|
||||
}
|
||||
73
TRMNL.h
Normal file
73
TRMNL.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#ifndef TRMNL_H_
|
||||
#define TRMNL_H_
|
||||
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "json.hpp"
|
||||
|
||||
class TRMNL
|
||||
{
|
||||
private:
|
||||
std::string m_id;
|
||||
std::string m_api_key;
|
||||
std::string m_friendly_id;
|
||||
int m_refresh_rate;
|
||||
|
||||
std::function<void (const TRMNL& trmnl)> m_update_handler;
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
TRMNL(const std::string& id = "",
|
||||
const std::string& api_key = "",
|
||||
const std::string& friendly_id = "",
|
||||
int refresh_rate = 600);
|
||||
|
||||
// Getters
|
||||
const std::string& id() const;
|
||||
const std::string& api_key() const;
|
||||
const std::string& friendly_id() const;
|
||||
int refresh_rate() const;
|
||||
|
||||
// Setters
|
||||
void id(const std::string& id);
|
||||
void api_key(const std::string& api_key);
|
||||
void friendly_id(const std::string& friendly_id);
|
||||
void refresh_rate(int refresh_rate);
|
||||
|
||||
void set_update_handler(std::function<void (const TRMNL& trmnl)> handler);
|
||||
|
||||
static std::string friendly_from_id(std::string id);
|
||||
|
||||
friend void to_json(nlohmann::json& j, const TRMNL& trmnl);
|
||||
friend void from_json(const nlohmann::json& j, TRMNL& trmnl);
|
||||
friend class TRMNLContainer;
|
||||
};
|
||||
|
||||
class TRMNLContainer
|
||||
{
|
||||
private:
|
||||
std::list<TRMNL> m_devices;
|
||||
std::map<std::string, std::list<TRMNL>::iterator> m_by_id;
|
||||
std::map<std::string, std::list<TRMNL>::iterator> m_by_friendly;
|
||||
|
||||
void TRMNL_update_handler(const TRMNL& trmnl);
|
||||
|
||||
public:
|
||||
TRMNLContainer() = default;
|
||||
|
||||
void add_device(TRMNL trmnl);
|
||||
|
||||
TRMNL* get_device_by_id(const std::string& id);
|
||||
TRMNL* get_device_by_friendly(const std::string& friendly);
|
||||
|
||||
void clear();
|
||||
|
||||
friend void to_json(nlohmann::json& j, const TRMNLContainer& cont);
|
||||
friend void from_json(const nlohmann::json& j, TRMNLContainer& cont);
|
||||
};
|
||||
|
||||
#endif // TRMNL_H_
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
// Optional
|
||||
"devices_filename": "devices.json",
|
||||
"folder_images": "images",
|
||||
"cert_file": "path/to/file",
|
||||
"key_file": "path/to/file"
|
||||
}
|
||||
|
||||
21
helpers.cpp
21
helpers.cpp
@@ -6,6 +6,7 @@ using nlohmann::json;
|
||||
|
||||
using std::endl;
|
||||
using std::ifstream;
|
||||
using std::ofstream;
|
||||
using std::istream;
|
||||
using std::ostream;
|
||||
using std::string;
|
||||
@@ -18,7 +19,7 @@ bool read_file_json(json& j, const string& filename, ostream* log)
|
||||
{
|
||||
if (nullptr != log)
|
||||
{
|
||||
*log << "Could not open file" << endl;
|
||||
*log << "Could not open file to read - " << filename << endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -45,6 +46,24 @@ bool read_stream_json(json& j, istream& in, ostream* log)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool write_file_json(const json& j, const string& filename, ostream* log)
|
||||
{
|
||||
ofstream file(filename);
|
||||
|
||||
if (!file.is_open())
|
||||
{
|
||||
if (nullptr != log)
|
||||
{
|
||||
*log << "Could not open file to write - " << filename << endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
file << j.dump(4) << endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool json_extract(const json& j, const string& key, string& out)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
@@ -23,6 +23,13 @@ bool read_file_json(nlohmann::json& j, const std::string& filename, std::ostream
|
||||
// Returns - true if read and parse is successful
|
||||
bool read_stream_json(nlohmann::json& j, std::istream& in, std::ostream* log);
|
||||
|
||||
// Writes json to supplied filename
|
||||
// j - input json
|
||||
// filename - filepath to which to write json
|
||||
// log - ostream to log human readable errors to - can be nullptr
|
||||
// Returns - true if write is successful
|
||||
bool write_file_json(const nlohmann::json& j, const std::string& filename, std::ostream* log);
|
||||
|
||||
// JSON Extraction Helpers
|
||||
// out value is modified only if an extraction happened
|
||||
// Returns - whether an extraction happened
|
||||
|
||||
200
main.cpp
200
main.cpp
@@ -12,149 +12,18 @@
|
||||
#include "json.hpp"
|
||||
|
||||
#include "helpers.h"
|
||||
#include "TRMNL.h"
|
||||
|
||||
using nlohmann::json;
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct device
|
||||
{
|
||||
string id;
|
||||
string api_key;
|
||||
string friendly_id;
|
||||
int refresh_rate;
|
||||
|
||||
// Default constructor
|
||||
device()
|
||||
: id(""),
|
||||
api_key(""),
|
||||
friendly_id(""),
|
||||
refresh_rate(600)
|
||||
{}
|
||||
};
|
||||
|
||||
void to_json(json& j, const device& d)
|
||||
{
|
||||
j = json{
|
||||
{"ID", d.id},
|
||||
{"api_key", d.api_key},
|
||||
{"friendly_id", d.friendly_id},
|
||||
{"refresh_rate", d.refresh_rate}
|
||||
};
|
||||
}
|
||||
|
||||
bool json_extract(const json& j, const string& key, device& out)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
if (!key.empty())
|
||||
{
|
||||
if (j.contains(key) && j[key].is_object())
|
||||
{
|
||||
result |= json_extract(j[key], "ID", out.id);
|
||||
result |= json_extract(j[key], "api_key", out.api_key);
|
||||
result |= json_extract(j[key], "friendly_id", out.friendly_id);
|
||||
result |= json_extract(j[key], "refresh_rate", out.refresh_rate);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result |= json_extract(j, "ID", out.id);
|
||||
result |= json_extract(j, "api_key", out.api_key);
|
||||
result |= json_extract(j, "friendly_id", out.friendly_id);
|
||||
result |= json_extract(j, "refresh_rate", out.refresh_rate);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
string device_id_to_friendly(string id)
|
||||
{
|
||||
size_t pos = id.find(':');
|
||||
while (pos != string::npos)
|
||||
{
|
||||
id.erase(pos, 1);
|
||||
pos = id.find(':');
|
||||
}
|
||||
|
||||
if (id.size() <= 6)
|
||||
{
|
||||
return id;
|
||||
}
|
||||
else
|
||||
{
|
||||
return id.substr(id.size() - 6);
|
||||
}
|
||||
}
|
||||
|
||||
class DeviceContainer
|
||||
{
|
||||
private:
|
||||
list<device> m_devices;
|
||||
map<string, list<device>::iterator> m_by_id;
|
||||
map<string, list<device>::iterator> m_by_friendly;
|
||||
|
||||
public:
|
||||
DeviceContainer() = default;
|
||||
|
||||
void add_device(const device& d)
|
||||
{
|
||||
if (d.id.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = m_devices.insert(m_devices.end(), d);
|
||||
m_by_id.insert(make_pair(it->id, it));
|
||||
|
||||
if (!it->friendly_id.empty())
|
||||
{
|
||||
m_by_friendly.insert(make_pair(it->friendly_id, it));
|
||||
}
|
||||
}
|
||||
|
||||
const device* get_device_by_id(const string& id)
|
||||
{
|
||||
if (0 == m_by_id.count(id))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return &(*(*(m_by_id.find(id))).second);
|
||||
}
|
||||
}
|
||||
|
||||
const device* get_device_by_friendly(const string& friendly)
|
||||
{
|
||||
if (0 == m_by_friendly.count(friendly))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return &(*(*(m_by_friendly.find(friendly))).second);
|
||||
}
|
||||
}
|
||||
|
||||
void set_device_friendly(const string& id, const string& friendly)
|
||||
{
|
||||
if (0 == m_by_id.count(id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = m_by_id[id];
|
||||
it->friendly_id = friendly;
|
||||
m_by_friendly.insert(make_pair(it->friendly_id, it));
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
string config_filename = "config.json";
|
||||
string devices_filename = "devices.json";
|
||||
string host = "";
|
||||
string folder_images = "images";
|
||||
string cert_file = "";
|
||||
string key_file = "";
|
||||
uint16_t port = 0;
|
||||
@@ -163,8 +32,7 @@ int main(int argc, char **argv)
|
||||
bool ok;
|
||||
json cfg;
|
||||
json devs;
|
||||
vector<device> devices;
|
||||
DeviceContainer container;
|
||||
TRMNLContainer container;
|
||||
|
||||
if (argc > 2)
|
||||
{
|
||||
@@ -210,30 +78,29 @@ int main(int argc, char **argv)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (devs.is_array())
|
||||
{
|
||||
for (int i = 0; i < devs.size(); ++i)
|
||||
{
|
||||
device d;
|
||||
json_extract(devs[i], "", d);
|
||||
devices.push_back(d);
|
||||
}
|
||||
}
|
||||
container.clear();
|
||||
container = devs;
|
||||
|
||||
for (auto device : devices)
|
||||
{
|
||||
container.add_device(device);
|
||||
}
|
||||
|
||||
|
||||
auto setup_handler = [&container](const httplib::Request& req, httplib::Response& res)
|
||||
auto setup_handler = [&container, &devices_filename](const httplib::Request& req, httplib::Response& res)
|
||||
{
|
||||
if (req.has_header("ID"))
|
||||
{
|
||||
json response;
|
||||
string id = req.get_header_value("ID");
|
||||
const device* dev = container.get_device_by_id(id);
|
||||
if (nullptr == dev)
|
||||
TRMNL* trmnl = container.get_device_by_id(id);
|
||||
|
||||
// Refresh date from file
|
||||
// Someone might have put the new device in
|
||||
if (nullptr == trmnl)
|
||||
{
|
||||
json j;
|
||||
read_file_json(j, devices_filename, &cout);
|
||||
container.clear();
|
||||
container = j;
|
||||
trmnl = container.get_device_by_id(id);
|
||||
}
|
||||
|
||||
if (nullptr == trmnl)
|
||||
{
|
||||
res.status = 404;
|
||||
|
||||
@@ -247,29 +114,40 @@ int main(int argc, char **argv)
|
||||
}
|
||||
else
|
||||
{
|
||||
bool should_dump = false;
|
||||
|
||||
res.status = 200;
|
||||
if (dev->friendly_id.empty())
|
||||
if (trmnl->friendly_id().empty())
|
||||
{
|
||||
container.set_device_friendly(id, device_id_to_friendly(id));
|
||||
should_dump = true;
|
||||
trmnl->friendly_id(TRMNL::friendly_from_id(id));
|
||||
}
|
||||
|
||||
device* dev_mut = const_cast<device*>(dev);
|
||||
if (trmnl->api_key().empty())
|
||||
{
|
||||
should_dump = true;
|
||||
trmnl->api_key("nullptr");
|
||||
}
|
||||
|
||||
response["status"] = 200;
|
||||
response["api_key"] = "nullptr";
|
||||
response["friendly_id"] = dev->friendly_id;
|
||||
response["api_key"] = trmnl->api_key();
|
||||
response["friendly_id"] = trmnl->friendly_id();
|
||||
// TODO: Check for image in folder
|
||||
response["image_url"] = "https://trmnl.com/images/setup/setup-logo.bmp";
|
||||
response["filename"] = "welcome";
|
||||
|
||||
res.body = response.dump();
|
||||
|
||||
json test = *dev_mut;
|
||||
cout << test.dump(4) << endl;
|
||||
if (should_dump)
|
||||
{
|
||||
write_file_json(container, devices_filename, &cout);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
res.status = 500;
|
||||
// Bad Request - No ID header
|
||||
res.status = 400;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user