547 lines
21 KiB
C++
547 lines
21 KiB
C++
#include "server/api_server.hpp"
|
|
|
|
#include "domain/layout_profile.hpp"
|
|
#include "domain/layout_schema.hpp"
|
|
#include "mission/mission_enqueue.hpp"
|
|
#include "util/http_util.hpp"
|
|
#include "util/id_util.hpp"
|
|
#include "util/string_util.hpp"
|
|
#include "validation/sensor_validator.hpp"
|
|
|
|
namespace lm {
|
|
|
|
ApiServer::ApiServer(StateRepository& repo,
|
|
MissionQueue& mission_queue,
|
|
MissionStore& mission_store,
|
|
ModbusTriggerService& modbus,
|
|
MissionScheduler& scheduler,
|
|
RobotRuntime& robot_runtime)
|
|
: repo_(repo),
|
|
mission_queue_(mission_queue),
|
|
mission_store_(mission_store),
|
|
modbus_(modbus),
|
|
scheduler_(scheduler),
|
|
robot_runtime_(robot_runtime)
|
|
{
|
|
}
|
|
|
|
void ApiServer::registerRoutes(httplib::Server& svr)
|
|
{
|
|
svr.Options(R"(/api/(.*))", [](const httplib::Request&, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
res.status = 204;
|
|
});
|
|
|
|
svr.Get("/api/health", [](const httplib::Request&, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
res.set_header("Content-Type", "application/json; charset=utf-8");
|
|
res.body = nlohmann::json({{"ok", true}}).dump();
|
|
});
|
|
|
|
svr.Get("/api/state", [this](const httplib::Request&, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
repo_.ensureSchema();
|
|
std::string active_name;
|
|
const auto idx = LayoutProfile::findActiveIndex(repo_.app().state);
|
|
if (idx)
|
|
active_name = repo_.app().state["layouts"][*idx]["name"].get<std::string>();
|
|
const nlohmann::json response = {{"version", repo_.app().state.value("version", 3)},
|
|
{"active_layout_id", repo_.app().state["active_layout_id"]},
|
|
{"active_layout_name", active_name},
|
|
{"layouts", LayoutProfile::buildCatalog(repo_.app().state)},
|
|
{"layout", repo_.app().state["layout"]},
|
|
{"lidars", repo_.app().state["lidars"]},
|
|
{"imus", repo_.app().state.contains("imus") ? repo_.app().state["imus"] : nlohmann::json::array()}};
|
|
res.set_header("Content-Type", "application/json; charset=utf-8");
|
|
res.body = response.dump();
|
|
});
|
|
|
|
svr.Get("/api/layouts", [this](const httplib::Request&, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
repo_.ensureSchema();
|
|
const nlohmann::json response = {{"active_layout_id", repo_.app().state["active_layout_id"]},
|
|
{"layouts", LayoutProfile::buildCatalog(repo_.app().state)}};
|
|
res.set_header("Content-Type", "application/json; charset=utf-8");
|
|
res.body = response.dump();
|
|
});
|
|
|
|
svr.Post("/api/layouts", [this](const httplib::Request& req, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
repo_.ensureSchema();
|
|
nlohmann::json payload;
|
|
try
|
|
{
|
|
payload = nlohmann::json::parse(req.body);
|
|
}
|
|
catch (...)
|
|
{
|
|
return HttpUtil::jsonError(res, 400, "invalid JSON");
|
|
}
|
|
if (!payload.is_object() || !payload.contains("name") || !payload["name"].is_string())
|
|
return HttpUtil::jsonError(res, 400, "name is required");
|
|
const std::string name = StringUtil::trimCopy(payload["name"].get<std::string>());
|
|
if (name.empty())
|
|
return HttpUtil::jsonError(res, 400, "name is required");
|
|
if (LayoutProfile::nameExists(repo_.app().state, name))
|
|
return HttpUtil::jsonError(res, 409, "layout name already exists");
|
|
|
|
const bool clone = payload.contains("clone") && payload["clone"].is_boolean() && payload["clone"].get<bool>();
|
|
nlohmann::json layout = LayoutSchema::defaultLayoutObject();
|
|
nlohmann::json lidars = nlohmann::json::array();
|
|
nlohmann::json imus = nlohmann::json::array();
|
|
if (clone)
|
|
{
|
|
layout = repo_.app().state["layout"];
|
|
lidars = repo_.app().state["lidars"];
|
|
imus = repo_.app().state.contains("imus") && repo_.app().state["imus"].is_array() ? repo_.app().state["imus"] : nlohmann::json::array();
|
|
}
|
|
nlohmann::json profile = LayoutProfile::make(name, layout, lidars, imus);
|
|
LayoutSchema::ensure(profile["layout"]);
|
|
if (!repo_.saveProfile(profile))
|
|
return HttpUtil::jsonError(res, 500, "failed to save layout file");
|
|
repo_.app().state["layouts"].push_back(LayoutProfile::catalogEntryFromProfile(profile));
|
|
repo_.app().state["active_layout_id"] = profile["id"].get<std::string>();
|
|
repo_.reloadActiveCache();
|
|
repo_.save();
|
|
|
|
res.status = 201;
|
|
res.set_header("Content-Type", "application/json; charset=utf-8");
|
|
res.body = profile.dump();
|
|
});
|
|
|
|
svr.Post(R"(/api/layouts/([0-9a-fA-F]+)/activate)", [this](const httplib::Request& req, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
const std::string id = req.matches[1].str();
|
|
repo_.ensureSchema();
|
|
if (!LayoutProfile::findIndex(repo_.app().state, id))
|
|
return HttpUtil::jsonError(res, 404, "layout not found");
|
|
repo_.app().state["active_layout_id"] = id;
|
|
repo_.reloadActiveCache();
|
|
repo_.save();
|
|
res.set_header("Content-Type", "application/json; charset=utf-8");
|
|
res.body = nlohmann::json({{"ok", true}, {"active_layout_id", id}}).dump();
|
|
});
|
|
|
|
svr.Delete(R"(/api/layouts/([0-9a-fA-F]+))", [this](const httplib::Request& req, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
const std::string id = req.matches[1].str();
|
|
repo_.ensureSchema();
|
|
if (!repo_.app().state.contains("layouts") || !repo_.app().state["layouts"].is_array())
|
|
return HttpUtil::jsonError(res, 404, "layout not found");
|
|
if (repo_.app().state["layouts"].size() <= 1)
|
|
return HttpUtil::jsonError(res, 400, "cannot delete the last layout");
|
|
const auto idx = LayoutProfile::findIndex(repo_.app().state, id);
|
|
if (!idx)
|
|
return HttpUtil::jsonError(res, 404, "layout not found");
|
|
|
|
const bool was_active =
|
|
repo_.app().state.contains("active_layout_id") && repo_.app().state["active_layout_id"].get<std::string>() == id;
|
|
repo_.deleteProfile(id);
|
|
repo_.app().state["layouts"].erase(repo_.app().state["layouts"].begin() + static_cast<nlohmann::json::difference_type>(*idx));
|
|
if (was_active)
|
|
repo_.app().state["active_layout_id"] = repo_.app().state["layouts"][0]["id"].get<std::string>();
|
|
repo_.reloadActiveCache();
|
|
repo_.save();
|
|
res.status = 204;
|
|
});
|
|
|
|
svr.Get("/api/lidars", [this](const httplib::Request&, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
res.set_header("Content-Type", "application/json; charset=utf-8");
|
|
res.body = repo_.app().state["lidars"].dump();
|
|
});
|
|
|
|
svr.Post("/api/lidars", [this](const httplib::Request& req, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
nlohmann::json payload;
|
|
try
|
|
{
|
|
payload = nlohmann::json::parse(req.body);
|
|
}
|
|
catch (...)
|
|
{
|
|
return HttpUtil::jsonError(res, 400, "invalid JSON");
|
|
}
|
|
std::string err;
|
|
if (!SensorValidator::validateLidarPayload(payload, err))
|
|
return HttpUtil::jsonError(res, 400, err);
|
|
|
|
const std::string name = StringUtil::trimCopy(payload["name"].get<std::string>());
|
|
const std::string ip = StringUtil::trimCopy(payload["ip"].get<std::string>());
|
|
const int port = payload["port"].get<int>();
|
|
if (SensorValidator::lidarTripletExists(repo_.app().state, name, ip, port))
|
|
return HttpUtil::jsonError(res, 409, "lidar with same name, ip and port already exists");
|
|
|
|
nlohmann::json lidar = {
|
|
{"id", IdUtil::newId()},
|
|
{"name", name},
|
|
{"ip", ip},
|
|
{"port", port},
|
|
};
|
|
repo_.app().state["lidars"].push_back(lidar);
|
|
if (!repo_.saveAppState())
|
|
return HttpUtil::jsonError(res, 500, "failed to save layout");
|
|
|
|
res.status = 201;
|
|
res.set_header("Content-Type", "application/json; charset=utf-8");
|
|
res.body = lidar.dump();
|
|
});
|
|
|
|
svr.Put(R"(/api/lidars/([0-9a-fA-F]+))", [this](const httplib::Request& req, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
const std::string id = req.matches[1].str();
|
|
|
|
nlohmann::json payload;
|
|
try
|
|
{
|
|
payload = nlohmann::json::parse(req.body);
|
|
}
|
|
catch (...)
|
|
{
|
|
return HttpUtil::jsonError(res, 400, "invalid JSON");
|
|
}
|
|
std::string err;
|
|
if (!SensorValidator::validateLidarPayload(payload, err))
|
|
return HttpUtil::jsonError(res, 400, err);
|
|
|
|
auto idx = SensorValidator::findLidarIndex(repo_.app().state, id);
|
|
if (!idx)
|
|
return HttpUtil::jsonError(res, 404, "lidar not found");
|
|
|
|
const std::string name = StringUtil::trimCopy(payload["name"].get<std::string>());
|
|
const std::string ip = StringUtil::trimCopy(payload["ip"].get<std::string>());
|
|
const int port = payload["port"].get<int>();
|
|
if (SensorValidator::lidarTripletExists(repo_.app().state, name, ip, port, &id))
|
|
return HttpUtil::jsonError(res, 409, "lidar with same name, ip and port already exists");
|
|
|
|
auto& lidar = repo_.app().state["lidars"][*idx];
|
|
lidar["name"] = name;
|
|
lidar["ip"] = ip;
|
|
lidar["port"] = port;
|
|
if (!repo_.saveAppState())
|
|
return HttpUtil::jsonError(res, 500, "failed to save layout");
|
|
|
|
res.set_header("Content-Type", "application/json; charset=utf-8");
|
|
res.body = lidar.dump();
|
|
});
|
|
|
|
svr.Delete(R"(/api/lidars/([0-9a-fA-F]+))", [this](const httplib::Request& req, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
const std::string id = req.matches[1].str();
|
|
auto idx = SensorValidator::findLidarIndex(repo_.app().state, id);
|
|
if (!idx)
|
|
return HttpUtil::jsonError(res, 404, "lidar not found");
|
|
|
|
repo_.app().state["lidars"].erase(repo_.app().state["lidars"].begin() + static_cast<nlohmann::json::difference_type>(*idx));
|
|
// Also remove pose entry if present
|
|
if (repo_.app().state.contains("layout") && repo_.app().state["layout"].is_object())
|
|
{
|
|
if (repo_.app().state["layout"].contains("lidarPoses") && repo_.app().state["layout"]["lidarPoses"].is_object())
|
|
repo_.app().state["layout"]["lidarPoses"].erase(id);
|
|
if (repo_.app().state["layout"].contains("lidarPositions") && repo_.app().state["layout"]["lidarPositions"].is_object())
|
|
repo_.app().state["layout"]["lidarPositions"].erase(id);
|
|
}
|
|
if (!repo_.saveAppState())
|
|
return HttpUtil::jsonError(res, 500, "failed to save layout");
|
|
res.status = 204;
|
|
});
|
|
|
|
svr.Get("/api/layout", [this](const httplib::Request&, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
res.set_header("Content-Type", "application/json; charset=utf-8");
|
|
res.body = repo_.app().state["layout"].dump();
|
|
});
|
|
|
|
svr.Put("/api/layout", [this](const httplib::Request& req, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
nlohmann::json payload;
|
|
try
|
|
{
|
|
payload = nlohmann::json::parse(req.body);
|
|
}
|
|
catch (...)
|
|
{
|
|
return HttpUtil::jsonError(res, 400, "invalid JSON");
|
|
}
|
|
if (!payload.is_object())
|
|
return HttpUtil::jsonError(res, 400, "layout must be an object");
|
|
|
|
repo_.app().state["layout"] = payload;
|
|
if (!repo_.saveAppState())
|
|
return HttpUtil::jsonError(res, 500, "failed to save layout");
|
|
res.set_header("Content-Type", "application/json; charset=utf-8");
|
|
res.body = repo_.app().state["layout"].dump();
|
|
});
|
|
|
|
svr.Put(R"(/api/layouts/([0-9a-fA-F]+))", [this](const httplib::Request& req, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
const std::string id = req.matches[1].str();
|
|
nlohmann::json payload;
|
|
try
|
|
{
|
|
payload = nlohmann::json::parse(req.body);
|
|
}
|
|
catch (...)
|
|
{
|
|
return HttpUtil::jsonError(res, 400, "invalid JSON");
|
|
}
|
|
if (!payload.is_object())
|
|
return HttpUtil::jsonError(res, 400, "payload must be an object");
|
|
|
|
repo_.ensureSchema();
|
|
const auto idx = LayoutProfile::findIndex(repo_.app().state, id);
|
|
if (!idx)
|
|
return HttpUtil::jsonError(res, 404, "layout not found");
|
|
|
|
auto loaded = repo_.loadProfileById(id);
|
|
if (!loaded)
|
|
return HttpUtil::jsonError(res, 404, "layout file not found");
|
|
nlohmann::json profile = *loaded;
|
|
|
|
if (payload.contains("name") && payload["name"].is_string())
|
|
{
|
|
const std::string name = StringUtil::trimCopy(payload["name"].get<std::string>());
|
|
if (name.empty())
|
|
return HttpUtil::jsonError(res, 400, "name is required");
|
|
if (LayoutProfile::nameExists(repo_.app().state, name, &id))
|
|
return HttpUtil::jsonError(res, 409, "layout name already exists");
|
|
profile["name"] = name;
|
|
}
|
|
if (payload.contains("layout") && payload["layout"].is_object())
|
|
profile["layout"] = payload["layout"];
|
|
if (payload.contains("lidars") && payload["lidars"].is_array())
|
|
profile["lidars"] = payload["lidars"];
|
|
if (payload.contains("imus") && payload["imus"].is_array())
|
|
profile["imus"] = payload["imus"];
|
|
if (!profile.contains("imus") || !profile["imus"].is_array())
|
|
profile["imus"] = nlohmann::json::array();
|
|
LayoutSchema::ensure(profile["layout"]);
|
|
LayoutProfile::touch(profile);
|
|
if (!repo_.saveProfile(profile))
|
|
return HttpUtil::jsonError(res, 500, "failed to save layout file");
|
|
|
|
repo_.app().state["layouts"][*idx] = LayoutProfile::catalogEntryFromProfile(profile);
|
|
const bool is_active =
|
|
repo_.app().state.contains("active_layout_id") && repo_.app().state["active_layout_id"].get<std::string>() == id;
|
|
if (is_active)
|
|
{
|
|
repo_.app().state["layout"] = profile["layout"];
|
|
repo_.app().state["lidars"] = profile["lidars"];
|
|
repo_.app().state["imus"] = profile["imus"];
|
|
}
|
|
repo_.save();
|
|
|
|
res.set_header("Content-Type", "application/json; charset=utf-8");
|
|
res.body = profile.dump();
|
|
});
|
|
|
|
svr.Get("/api/imus", [this](const httplib::Request&, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
repo_.ensureSchema();
|
|
res.set_header("Content-Type", "application/json; charset=utf-8");
|
|
res.body = (repo_.app().state.contains("imus") ? repo_.app().state["imus"] : nlohmann::json::array()).dump();
|
|
});
|
|
|
|
svr.Post("/api/imus", [this](const httplib::Request& req, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
nlohmann::json payload;
|
|
try
|
|
{
|
|
payload = nlohmann::json::parse(req.body);
|
|
}
|
|
catch (...)
|
|
{
|
|
return HttpUtil::jsonError(res, 400, "invalid JSON");
|
|
}
|
|
std::string err;
|
|
if (!SensorValidator::validateImuPayload(payload, err))
|
|
return HttpUtil::jsonError(res, 400, err);
|
|
|
|
const std::string name = StringUtil::trimCopy(payload["name"].get<std::string>());
|
|
const std::string frame_id = StringUtil::trimCopy(payload["frame_id"].get<std::string>());
|
|
const std::string topic = StringUtil::trimCopy(payload["topic"].get<std::string>());
|
|
if (SensorValidator::imuFrameExists(repo_.app().state, frame_id))
|
|
return HttpUtil::jsonError(res, 409, "imu with same frame_id already exists");
|
|
|
|
if (!repo_.app().state.contains("imus") || !repo_.app().state["imus"].is_array())
|
|
repo_.app().state["imus"] = nlohmann::json::array();
|
|
|
|
const std::string source =
|
|
payload.contains("source") && payload["source"].is_string() ? payload["source"].get<std::string>()
|
|
: "external";
|
|
const bool enabled = !payload.contains("enabled") || payload["enabled"].get<bool>();
|
|
const double rate_hz =
|
|
payload.contains("rate_hz") && payload["rate_hz"].is_number() ? payload["rate_hz"].get<double>() : 100.0;
|
|
|
|
nlohmann::json imu = {{"id", IdUtil::newId()},
|
|
{"name", name},
|
|
{"frame_id", frame_id},
|
|
{"topic", topic},
|
|
{"source", source},
|
|
{"enabled", enabled},
|
|
{"rate_hz", rate_hz}};
|
|
repo_.app().state["imus"].push_back(imu);
|
|
if (!repo_.saveAppState())
|
|
return HttpUtil::jsonError(res, 500, "failed to save layout");
|
|
|
|
res.status = 201;
|
|
res.set_header("Content-Type", "application/json; charset=utf-8");
|
|
res.body = imu.dump();
|
|
});
|
|
|
|
svr.Put(R"(/api/imus/([0-9a-fA-F]+))", [this](const httplib::Request& req, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
const std::string id = req.matches[1].str();
|
|
|
|
nlohmann::json payload;
|
|
try
|
|
{
|
|
payload = nlohmann::json::parse(req.body);
|
|
}
|
|
catch (...)
|
|
{
|
|
return HttpUtil::jsonError(res, 400, "invalid JSON");
|
|
}
|
|
std::string err;
|
|
if (!SensorValidator::validateImuPayload(payload, err))
|
|
return HttpUtil::jsonError(res, 400, err);
|
|
|
|
auto idx = SensorValidator::findImuIndex(repo_.app().state, id);
|
|
if (!idx)
|
|
return HttpUtil::jsonError(res, 404, "imu not found");
|
|
|
|
const std::string name = StringUtil::trimCopy(payload["name"].get<std::string>());
|
|
const std::string frame_id = StringUtil::trimCopy(payload["frame_id"].get<std::string>());
|
|
const std::string topic = StringUtil::trimCopy(payload["topic"].get<std::string>());
|
|
if (SensorValidator::imuFrameExists(repo_.app().state, frame_id, &id))
|
|
return HttpUtil::jsonError(res, 409, "imu with same frame_id already exists");
|
|
|
|
auto& imu = repo_.app().state["imus"][*idx];
|
|
imu["name"] = name;
|
|
imu["frame_id"] = frame_id;
|
|
imu["topic"] = topic;
|
|
if (payload.contains("source") && payload["source"].is_string())
|
|
imu["source"] = payload["source"];
|
|
if (payload.contains("enabled") && payload["enabled"].is_boolean())
|
|
imu["enabled"] = payload["enabled"];
|
|
if (payload.contains("rate_hz") && payload["rate_hz"].is_number())
|
|
imu["rate_hz"] = payload["rate_hz"];
|
|
if (!repo_.saveAppState())
|
|
return HttpUtil::jsonError(res, 500, "failed to save layout");
|
|
|
|
res.set_header("Content-Type", "application/json; charset=utf-8");
|
|
res.body = imu.dump();
|
|
});
|
|
|
|
svr.Delete(R"(/api/imus/([0-9a-fA-F]+))", [this](const httplib::Request& req, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
const std::string id = req.matches[1].str();
|
|
auto idx = SensorValidator::findImuIndex(repo_.app().state, id);
|
|
if (!idx)
|
|
return HttpUtil::jsonError(res, 404, "imu not found");
|
|
|
|
repo_.app().state["imus"].erase(repo_.app().state["imus"].begin() + static_cast<nlohmann::json::difference_type>(*idx));
|
|
if (repo_.app().state.contains("layout") && repo_.app().state["layout"].is_object())
|
|
{
|
|
if (repo_.app().state["layout"].contains("imuPoses") && repo_.app().state["layout"]["imuPoses"].is_object())
|
|
repo_.app().state["layout"]["imuPoses"].erase(id);
|
|
}
|
|
if (!repo_.saveAppState())
|
|
return HttpUtil::jsonError(res, 500, "failed to save layout");
|
|
res.status = 204;
|
|
});
|
|
|
|
svr.Get("/api/mission_queue", [this](const httplib::Request&, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
res.set_header("Content-Type", "application/json; charset=utf-8");
|
|
res.body = nlohmann::json({{"queue", mission_queue_.list()}, {"runner", mission_queue_.runnerStatus()}}).dump();
|
|
});
|
|
|
|
svr.Post("/api/mission_queue", [this](const httplib::Request& req, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
nlohmann::json payload;
|
|
try
|
|
{
|
|
payload = nlohmann::json::parse(req.body);
|
|
}
|
|
catch (...)
|
|
{
|
|
return HttpUtil::jsonError(res, 400, "invalid JSON");
|
|
}
|
|
if (!payload.contains("source"))
|
|
payload["source"] = "ui";
|
|
enqueueRequest(payload, res, 201);
|
|
});
|
|
|
|
svr.Delete("/api/mission_queue", [this](const httplib::Request&, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
std::string err;
|
|
if (!mission_queue_.clearAll(err))
|
|
return HttpUtil::jsonError(res, 400, err);
|
|
res.status = 204;
|
|
});
|
|
|
|
svr.Put("/api/mission_queue/reorder", [this](const httplib::Request& req, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
nlohmann::json payload;
|
|
try
|
|
{
|
|
payload = nlohmann::json::parse(req.body);
|
|
}
|
|
catch (...)
|
|
{
|
|
return HttpUtil::jsonError(res, 400, "invalid JSON");
|
|
}
|
|
if (!payload.contains("ordered_ids") || !payload["ordered_ids"].is_array())
|
|
return HttpUtil::jsonError(res, 400, "ordered_ids is required");
|
|
std::string err;
|
|
if (!mission_queue_.reorder(payload["ordered_ids"], err))
|
|
return HttpUtil::jsonError(res, 400, err);
|
|
res.set_header("Content-Type", "application/json; charset=utf-8");
|
|
res.body = nlohmann::json({{"ok", true}}).dump();
|
|
});
|
|
|
|
svr.Delete(R"(/api/mission_queue/([0-9a-fA-F]+))", [this](const httplib::Request& req, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
const std::string id = req.matches[1].str();
|
|
std::string err;
|
|
if (!mission_queue_.removeById(id, err))
|
|
return HttpUtil::jsonError(res, 400, err);
|
|
res.status = 204;
|
|
});
|
|
|
|
svr.Post("/api/mission_queue/pause", [this](const httplib::Request&, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
std::string err;
|
|
if (!mission_queue_.pause(err))
|
|
return HttpUtil::jsonError(res, 400, err);
|
|
res.set_header("Content-Type", "application/json; charset=utf-8");
|
|
res.body = mission_queue_.runnerStatus().dump();
|
|
});
|
|
|
|
svr.Post("/api/mission_queue/continue", [this](const httplib::Request&, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
std::string err;
|
|
if (!mission_queue_.resume(err))
|
|
return HttpUtil::jsonError(res, 400, err);
|
|
res.set_header("Content-Type", "application/json; charset=utf-8");
|
|
res.body = mission_queue_.runnerStatus().dump();
|
|
});
|
|
|
|
svr.Post("/api/mission_queue/cancel", [this](const httplib::Request&, httplib::Response& res) {
|
|
HttpUtil::addCors(res);
|
|
std::string err;
|
|
if (!mission_queue_.cancel(err))
|
|
return HttpUtil::jsonError(res, 400, err);
|
|
res.set_header("Content-Type", "application/json; charset=utf-8");
|
|
res.body = mission_queue_.runnerStatus().dump();
|
|
});
|
|
|
|
registerMissionRoutes(svr);
|
|
registerIntegrationRoutes(svr);
|
|
registerMirV2Routes(svr);
|
|
registerRobotRoutes(svr);
|
|
}
|
|
|
|
} // namespace lm
|