File controller.h
File List > demo-projets > jaculus > main > util > controller.h
Go to the documentation of this file.
#pragma once
#include <jac/link/mux.h>
#include <jac/link/router.h>
#include "uploader.h"
#include "logger.h"
#include <jac/link/routerCommunicator.h>
#include <sstream>
#include <filesystem>
#include <optional>
#include <thread>
#include "esp_pthread.h"
template<class Machine>
class Controller {
Router _router;
std::optional<Uploader> _uploader;
std::unique_ptr<BufferedInputPacketCommunicator> _input;
std::unique_ptr<OutputPacketCommunicator> _output;
std::unique_ptr<Machine> _machine;
bool _running = false;
std::thread _thread;
std::function<std::string()> _getMemoryStats;
std::function<std::string()> _getStorageStats;
std::vector<std::function<void(Machine&)>> _onConfigureMachine;
std::thread _controllerThread;
void configureMachine() {
_machine = std::make_unique<Machine>();
for (auto& f : _onConfigureMachine) {
f(*_machine);
}
}
public:
void processStart(int sender, std::span<const uint8_t> data);
void processStop(int sender);
void processStatus(int sender);
enum class Command : uint8_t {
START = 0x01,
STOP = 0x02,
STATUS = 0x03,
OK = 0x20,
ERROR = 0x21,
};
Controller(std::function<std::string()> getMemoryStats, std::function<std::string()> getStorageStats):
_getMemoryStats(getMemoryStats),
_getStorageStats(getStorageStats)
{
Logger::_logStream = std::make_unique<TransparentOutputStreamCommunicator>(_router, 255, std::vector<int>{});
Logger::_debugStream = std::make_unique<TransparentOutputStreamCommunicator>(_router, 254, std::vector<int>{});
auto uploaderInput = std::make_unique<AsyncBufferedInputPacketCommunicator>();
auto uploaderOutput = std::make_unique<TransparentOutputPacketCommunicator>(_router, 1);
_router.subscribeChannel(1, *uploaderInput);
_uploader.emplace(std::move(uploaderInput), std::move(uploaderOutput));
auto controllerInput = std::make_unique<AsyncBufferedInputPacketCommunicator>();
_router.subscribeChannel(0, *controllerInput);
_input = std::move(controllerInput);
_output = std::make_unique<TransparentOutputPacketCommunicator>(_router, 0);
_controllerThread = std::thread([this]() {
while (true) {
auto [sender, data] = _input->get();
if (data.size() == 0) {
continue;
}
auto begin = data.begin();
Command cmd = static_cast<Command>(data[0]);
begin++;
Logger::debug(std::string("Controller: ") + std::to_string(static_cast<int>(cmd)));
switch (cmd) {
case Command::START: {
processStart(sender, std::span<const uint8_t>(begin, data.end()));
break;
}
case Command::STOP: {
processStop(sender);
break;
}
case Command::STATUS: {
processStatus(sender);
break;
}
default: {
break;
}
}
}
});
}
Router& router() {
return _router;
}
Uploader& uploader() {
return *_uploader;
}
bool startMachine(std::string path);
bool stopMachine();
void onConfigureMachine(std::function<void(Machine&)> f) {
_onConfigureMachine.push_back(f);
}
};
template<class Machine>
void Controller<Machine>::processStart(int sender, std::span<const uint8_t> data) {
std::string filename(data.begin(), data.end());
auto result = Command::OK;
if (!startMachine(filename)) {
result = Command::ERROR;
}
auto response = this->_output->buildPacket({sender});
response->put(static_cast<uint8_t>(result));
response->send();
}
template<class Machine>
void Controller<Machine>::processStop(int sender) {
auto result = Command::OK;
if (!stopMachine()) {
result = Command::ERROR;
}
auto response = this->_output->buildPacket({sender});
response->put(static_cast<uint8_t>(result));
response->send();
}
template<class Machine>
void Controller<Machine>::processStatus(int sender) {
auto response = this->_output->buildPacket({sender});
response->put(static_cast<uint8_t>(Command::STATUS));
response->put(static_cast<uint8_t>(_running));
response->put(_machine->eventLoop_getExitCode());
std::stringstream oss;
oss << "Memory usage: " << _getMemoryStats() << std::endl;
oss << "Storage usage: " << _getStorageStats() << std::endl;
std::string data = oss.str();
response->put(std::span<const uint8_t>(reinterpret_cast<const uint8_t*>(data.data()), data.size()));
response->send();
}
template<class Machine>
bool Controller<Machine>::startMachine(std::string path) {
if (_running) {
return false;
}
if (!std::filesystem::exists(path)) {
Logger::log("File not found: " + path);
return false;
}
esp_pthread_cfg_t cfg = esp_pthread_get_default_config();
cfg.stack_size = 8 * 1024;
esp_pthread_set_cfg(&cfg);
_thread = std::thread([this, path]() {
Controller<Machine>& self = *this;
self._running = true;
Logger::log("Starting machine");
self.configureMachine();
self._machine->initialize();
try {
self._machine->evalFile(path);
self._machine->eventLoop_run();
}
catch (const std::runtime_error& e) {
Logger::log("Runtime error - " + std::string(e.what()));
}
catch (const std::exception& e) {
Logger::log("Exception - " + std::string(e.what()));
}
catch (...) {
Logger::log("Unknown exception");
}
self._running = false;
});
return true;
}
template<class Machine>
bool Controller<Machine>::stopMachine() {
if (!_running) {
return false;
}
_machine->eventLoop_exit();
if (_thread.joinable()) {
_thread.join();
}
_thread = std::thread();
return true;
}