|
|
@@ -0,0 +1,544 @@
|
|
|
+#include "sophiar_connect.h"
|
|
|
+
|
|
|
+#include <boost/asio/buffer.hpp>
|
|
|
+#include <boost/asio/io_context.hpp>
|
|
|
+#include <boost/asio/ip/tcp.hpp>
|
|
|
+#include <boost/asio/read.hpp>
|
|
|
+#include <boost/asio/write.hpp>
|
|
|
+#include <boost/endian.hpp>
|
|
|
+#include <boost/smart_ptr.hpp>
|
|
|
+
|
|
|
+#include <spdlog/spdlog.h>
|
|
|
+
|
|
|
+#include <charconv>
|
|
|
+#include <bit>
|
|
|
+#include <variant>
|
|
|
+#include <vector>
|
|
|
+#include <unordered_map>
|
|
|
+
|
|
|
+#ifdef HAVE_IMGUI
|
|
|
+
|
|
|
+#include <imgui.h>
|
|
|
+
|
|
|
+#endif
|
|
|
+
|
|
|
+using namespace boost::asio::ip;
|
|
|
+using namespace std::string_view_literals;
|
|
|
+using boost::asio::buffer;
|
|
|
+using boost::asio::io_context;
|
|
|
+using boost::asio::read;
|
|
|
+using boost::asio::write;
|
|
|
+
|
|
|
+namespace sophiar {
|
|
|
+
|
|
|
+ struct variable_io::impl {
|
|
|
+
|
|
|
+ using var_store_type = std::variant<std::monostate,
|
|
|
+ bool_type, integer_type, float_type,
|
|
|
+ scalar_xyz_type, transform_type, array6_type>;
|
|
|
+ using var_index_type = uint16_t;
|
|
|
+ using var_len_type = uint8_t;
|
|
|
+
|
|
|
+ static constexpr var_len_type var_bin_len[] = {0, 1, 8, 8, 24, 56, 48};
|
|
|
+
|
|
|
+ struct var_info {
|
|
|
+ variable_type type;
|
|
|
+ var_index_type index;
|
|
|
+ std::string_view name;
|
|
|
+ var_store_type value;
|
|
|
+ };
|
|
|
+
|
|
|
+ using var_pool_type = std::vector<var_info>;
|
|
|
+ var_pool_type var_in_pool, var_out_pool;
|
|
|
+
|
|
|
+ using var_index_id_map_type = std::unordered_map<var_index_type, int>;
|
|
|
+ var_index_id_map_type var_index_id_map; // for VD_Input
|
|
|
+
|
|
|
+ tcp::endpoint server_ep;
|
|
|
+
|
|
|
+ boost::scoped_ptr<io_context> context;
|
|
|
+ boost::scoped_ptr<tcp::socket> socket_in, socket_out;
|
|
|
+
|
|
|
+ char *in_data = nullptr, *out_data = nullptr;
|
|
|
+ size_t in_data_len = 0, out_data_len = 0;
|
|
|
+
|
|
|
+ using msg_len_type = uint16_t;
|
|
|
+ msg_len_type next_msg_len = 0;
|
|
|
+ msg_len_type cur_out_len = sizeof(msg_len_type); // used for variable output
|
|
|
+
|
|
|
+ template<typename T>
|
|
|
+ static char *write_binary_number(char *ptr, T val) {
|
|
|
+ static constexpr auto need_swap =
|
|
|
+ (boost::endian::order::native != boost::endian::order::big);
|
|
|
+ if constexpr (need_swap && sizeof(T) > 1) {
|
|
|
+ boost::endian::endian_reverse_inplace(val);
|
|
|
+ }
|
|
|
+ auto real_ptr = (T *) ptr;
|
|
|
+ *real_ptr = val;
|
|
|
+ return ptr + sizeof(T);
|
|
|
+ }
|
|
|
+
|
|
|
+ template<typename T>
|
|
|
+ static char *read_binary_number(char *ptr, T *val) {
|
|
|
+ static constexpr auto need_swap =
|
|
|
+ (boost::endian::order::native != boost::endian::order::big);
|
|
|
+ *val = *(T *) ptr;
|
|
|
+ if constexpr (need_swap && sizeof(T) > 1) {
|
|
|
+ boost::endian::endian_reverse_inplace(*val);
|
|
|
+ }
|
|
|
+ return ptr + sizeof(T);
|
|
|
+ }
|
|
|
+
|
|
|
+ template<typename ...Args>
|
|
|
+ static char *write_binary_numbers(char *ptr, Args... args) {
|
|
|
+ return (..., (ptr = write_binary_number(ptr, args)));
|
|
|
+ }
|
|
|
+
|
|
|
+ template<typename ...Args>
|
|
|
+ static char *read_binary_numbers(char *ptr, Args... args) {
|
|
|
+ static_assert((... && std::is_pointer_v<Args>));
|
|
|
+ return (..., (ptr = read_binary_number(ptr, args)));
|
|
|
+ }
|
|
|
+
|
|
|
+ static char *write_variable(char *ptr, const var_store_type &val) {
|
|
|
+ switch (val.index()) {
|
|
|
+
|
|
|
+#define SIMPLE_CASE(TYPE) \
|
|
|
+ case VT_##TYPE: { \
|
|
|
+ auto real_val = std::get<TYPE##_type>(val); \
|
|
|
+ return write_binary_number(ptr, real_val); \
|
|
|
+ }
|
|
|
+
|
|
|
+ SIMPLE_CASE(bool)
|
|
|
+ SIMPLE_CASE(integer)
|
|
|
+ SIMPLE_CASE(float)
|
|
|
+
|
|
|
+#undef SIMPLE_CASE
|
|
|
+
|
|
|
+ case VT_scalar_xyz: {
|
|
|
+ auto &real_val = std::get<scalar_xyz_type>(val);
|
|
|
+ return write_binary_numbers(ptr, real_val.x(), real_val.y(), real_val.z());
|
|
|
+ }
|
|
|
+ case VT_transform: {
|
|
|
+ auto &real_val = std::get<transform_type>(val);
|
|
|
+ auto trans_part = real_val.translation();
|
|
|
+ auto quat_part = Eigen::Quaterniond{real_val.rotation()};
|
|
|
+ ptr = write_binary_numbers(ptr, trans_part.x(), trans_part.y(), trans_part.z());
|
|
|
+ ptr = write_binary_numbers(ptr, quat_part.w(), quat_part.x(), quat_part.y(), quat_part.z());
|
|
|
+ return ptr;
|
|
|
+ }
|
|
|
+ case VT_array6: {
|
|
|
+ auto &real_val = std::get<array6_type>(val);
|
|
|
+ for (auto &elem: real_val) {
|
|
|
+ ptr = write_binary_number(ptr, elem);
|
|
|
+ }
|
|
|
+ return ptr;
|
|
|
+ }
|
|
|
+ default: {
|
|
|
+ assert(val.index() == 0); // empty variable
|
|
|
+ return ptr;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ assert(false);
|
|
|
+ return ptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ static char *read_variable(char *ptr, var_store_type *val, variable_type type) {
|
|
|
+ switch (type) {
|
|
|
+
|
|
|
+#define SIMPLE_CASE(TYPE) \
|
|
|
+ case VT_##TYPE: { \
|
|
|
+ auto &real_val = std::get<TYPE##_type>(*val); \
|
|
|
+ return read_binary_number(ptr, &real_val); \
|
|
|
+ }
|
|
|
+
|
|
|
+ SIMPLE_CASE(bool)
|
|
|
+ SIMPLE_CASE(integer)
|
|
|
+ SIMPLE_CASE(float)
|
|
|
+
|
|
|
+#undef SIMPLE_CASE
|
|
|
+
|
|
|
+ case VT_scalar_xyz: {
|
|
|
+ auto &real_val = std::get<scalar_xyz_type>(*val);
|
|
|
+ return read_binary_numbers(ptr, &real_val.x(), &real_val.y(), &real_val.z());
|
|
|
+ }
|
|
|
+ case VT_transform: {
|
|
|
+ double tx, ty, tz, qw, qx, qy, qz;
|
|
|
+ ptr = read_binary_numbers(ptr, &tx, &ty, &tz, &qw, &qx, &qy, &qz);
|
|
|
+ std::get<transform_type>(*val) =
|
|
|
+ Eigen::Translation3d(tx, ty, tz) * Eigen::Quaterniond(qw, qx, qy, qz);
|
|
|
+ return ptr;
|
|
|
+ }
|
|
|
+ case VT_array6: {
|
|
|
+ auto &real_val = std::get<array6_type>(*val);
|
|
|
+ for (auto &elem: real_val) {
|
|
|
+ ptr = read_binary_number(ptr, &elem);
|
|
|
+ }
|
|
|
+ return ptr;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ assert(false);
|
|
|
+ return ptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ static void init_var_store(var_store_type *val, variable_type type) {
|
|
|
+ switch (type) {
|
|
|
+
|
|
|
+#define SIMPLE_CASE(TYPE) \
|
|
|
+ case VT_##TYPE : { \
|
|
|
+ val->emplace<VT_##TYPE>(); \
|
|
|
+ return; \
|
|
|
+ }
|
|
|
+
|
|
|
+ SIMPLE_CASE(bool)
|
|
|
+ SIMPLE_CASE(integer)
|
|
|
+ SIMPLE_CASE(float)
|
|
|
+ SIMPLE_CASE(scalar_xyz)
|
|
|
+ SIMPLE_CASE(transform)
|
|
|
+ SIMPLE_CASE(array6)
|
|
|
+
|
|
|
+#undef SIMPLE_CASE
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+#ifdef HAVE_IMGUI
|
|
|
+
|
|
|
+ static void show_variable(const var_store_type *val, variable_type type, std::string_view name) {
|
|
|
+ static auto empty_color = (ImVec4) ImColor::HSV(0, 1, 1);
|
|
|
+
|
|
|
+ switch (type) {
|
|
|
+ case VT_scalar_xyz: {
|
|
|
+ Eigen::Vector3f elems = Eigen::Vector3f::Zero();
|
|
|
+ if (val->index() == 0) {
|
|
|
+ ImGui::PushStyleColor(ImGuiCol_Text, empty_color);
|
|
|
+ } else {
|
|
|
+ auto &real_val = std::get<scalar_xyz_type>(*val);
|
|
|
+ elems = real_val.cast<float>();
|
|
|
+ }
|
|
|
+ ImGui::InputFloat3(name.data(), elems.data(), "%.1f");
|
|
|
+ if (val->index() == 0) {
|
|
|
+ ImGui::PopStyleColor();
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ case VT_transform: {
|
|
|
+ Eigen::Vector3f trans_part = Eigen::Vector3f::Zero();
|
|
|
+ Eigen::Quaternionf quat_part = Eigen::Quaternionf::Identity();
|
|
|
+ auto trans_name = fmt::format("{} (trans)", name);
|
|
|
+ auto quat_name = fmt::format("{} (quat)", name);
|
|
|
+ if (val->index() == 0) {
|
|
|
+ ImGui::PushStyleColor(ImGuiCol_Text, empty_color);
|
|
|
+ } else {
|
|
|
+ auto &real_val = std::get<transform_type>(*val);
|
|
|
+ trans_part = real_val.translation().cast<float>();
|
|
|
+ quat_part = real_val.rotation().cast<float>();
|
|
|
+ }
|
|
|
+ ImGui::InputFloat3(trans_name.c_str(), trans_part.data(), "%.1f");
|
|
|
+ ImGui::InputFloat4(quat_name.c_str(), quat_part.coeffs().data(), "%.3f");
|
|
|
+ if (val->index() == 0) {
|
|
|
+ ImGui::PopStyleColor();
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+#endif
|
|
|
+
|
|
|
+ static size_t least_power_of_2(size_t x) {
|
|
|
+ auto ret = std::bit_width(x);
|
|
|
+ if (std::has_single_bit(x)) --ret;
|
|
|
+ return 1ull << ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ void ensure_buffer(size_t in_len, size_t out_len, bool keep_out_data = false) {
|
|
|
+ if (in_len > in_data_len) [[unlikely]] {
|
|
|
+ auto next_len = least_power_of_2(in_len);
|
|
|
+ free(in_data);
|
|
|
+ in_data = (char *) malloc(next_len);
|
|
|
+ in_data_len = next_len;
|
|
|
+ }
|
|
|
+ if (out_len > out_data_len) [[unlikely]] {
|
|
|
+ auto next_len = least_power_of_2(out_len);
|
|
|
+ auto next_out_data = (char *) malloc(next_len);
|
|
|
+ if (keep_out_data) [[likely]] {
|
|
|
+ memcpy(next_out_data, out_data, out_data_len);
|
|
|
+ }
|
|
|
+ free(out_data);
|
|
|
+ out_data = next_out_data;
|
|
|
+ out_data_len = next_len;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t write_hello_msg(variable_direction dir) {
|
|
|
+ static constexpr auto var_in_cmd = "VAROUTB"sv;
|
|
|
+ static constexpr auto var_out_cmd = "VARINB"sv;
|
|
|
+
|
|
|
+ // calculate required length
|
|
|
+ const auto &pool = (dir == VD_Input ? var_in_pool : var_out_pool);
|
|
|
+ size_t var_cnt = pool.size();
|
|
|
+ if (var_cnt == 0) return 0;
|
|
|
+ size_t var_len = 0;
|
|
|
+ for (auto &var: pool) {
|
|
|
+ var_len += var.name.length();
|
|
|
+ }
|
|
|
+ auto cmd = (dir == VD_Input ? var_in_cmd : var_out_cmd);
|
|
|
+ msg_len_type msg_len = cmd.length() + 1 + (var_cnt - 1) + var_len;
|
|
|
+ auto out_len = sizeof(msg_len_type) + msg_len;
|
|
|
+ ensure_buffer(0, out_len);
|
|
|
+
|
|
|
+ // fill buffer
|
|
|
+ auto out_ptr = write_binary_number(out_data, msg_len);
|
|
|
+ memcpy(out_ptr, cmd.data(), cmd.length());
|
|
|
+ out_ptr += cmd.length();
|
|
|
+ *(out_ptr++) = ' ';
|
|
|
+ for (auto &var: pool) {
|
|
|
+ memcpy(out_ptr, var.name.data(), var.name.length());
|
|
|
+ out_ptr += var.name.length();
|
|
|
+ if (--var_cnt) {
|
|
|
+ *(out_ptr++) = ',';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ assert(out_ptr - out_data == out_len);
|
|
|
+ return out_len;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool try_receive_msg(tcp::socket *s, bool nonblock = true) {
|
|
|
+ if (next_msg_len == 0) {
|
|
|
+ if (nonblock) {
|
|
|
+ if (s->available() < sizeof(msg_len_type)) return false;
|
|
|
+ }
|
|
|
+ ensure_buffer(sizeof(msg_len_type), 0);
|
|
|
+ auto bytes_read = read(*s, buffer(in_data, sizeof(msg_len_type)));
|
|
|
+ assert(bytes_read == sizeof(msg_len_type));
|
|
|
+ read_binary_number(in_data, &next_msg_len);
|
|
|
+ }
|
|
|
+ assert(next_msg_len != 0);
|
|
|
+ if (nonblock) {
|
|
|
+ if (s->available() < next_msg_len) return false;
|
|
|
+ }
|
|
|
+ ensure_buffer(sizeof(msg_len_type) + next_msg_len, 0);
|
|
|
+ write_binary_number(in_data, next_msg_len); // in_data may be modified
|
|
|
+ auto bytes_read = read(*s, buffer(in_data + sizeof(msg_len_type), next_msg_len));
|
|
|
+ assert(bytes_read == next_msg_len);
|
|
|
+ next_msg_len = 0;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ void save_variable_index(variable_direction dir) {
|
|
|
+ msg_len_type msg_len;
|
|
|
+ auto in_ptr = read_binary_number(in_data, &msg_len);
|
|
|
+ auto end_ptr = in_ptr + msg_len;
|
|
|
+ auto &pool = (dir == VD_Input ? var_in_pool : var_out_pool);
|
|
|
+ for (auto &var: pool) {
|
|
|
+ auto ret = std::from_chars(in_ptr, end_ptr, var.index);
|
|
|
+ assert(ret.ec == std::errc{}); // no error
|
|
|
+ if (ret.ptr != end_ptr) {
|
|
|
+ assert(*ret.ptr == ',');
|
|
|
+ in_ptr = (char *) ret.ptr + 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // build index -> id map
|
|
|
+ if (dir == VD_Input) {
|
|
|
+ for (auto i = 0; i < var_in_pool.size(); ++i) {
|
|
|
+ auto var_index = var_in_pool[i].index;
|
|
|
+ var_index_id_map.insert({var_index, i});
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void setup_variable_io(tcp::socket *s, variable_direction dir) {
|
|
|
+ auto msg_len = write_hello_msg(dir);
|
|
|
+ if (msg_len == 0) return;
|
|
|
+ s->connect(server_ep);
|
|
|
+ assert(s->is_open());
|
|
|
+ write(*s, buffer(out_data, msg_len));
|
|
|
+ try_receive_msg(s, false);
|
|
|
+ save_variable_index(dir);
|
|
|
+ }
|
|
|
+
|
|
|
+ void initialize() {
|
|
|
+ next_msg_len = 0;
|
|
|
+ cur_out_len = sizeof(msg_len_type);
|
|
|
+
|
|
|
+ context.reset(new io_context{});
|
|
|
+ socket_in.reset(new tcp::socket{*context});
|
|
|
+ socket_out.reset(new tcp::socket{*context});
|
|
|
+ setup_variable_io(socket_in.get(), VD_Input);
|
|
|
+ setup_variable_io(socket_out.get(), VD_Output);
|
|
|
+ }
|
|
|
+
|
|
|
+ void handle_var_update_msg() {
|
|
|
+ msg_len_type msg_len;
|
|
|
+ auto in_ptr = read_binary_number(in_data, &msg_len);
|
|
|
+ auto end_ptr = in_ptr + msg_len;
|
|
|
+ while (in_ptr != end_ptr) {
|
|
|
+ var_index_type var_index;
|
|
|
+ var_len_type var_len;
|
|
|
+ in_ptr = read_binary_numbers(in_ptr, &var_index, &var_len);
|
|
|
+ auto var_id = var_index_id_map.at(var_index);
|
|
|
+ auto &var_info = var_in_pool[var_id];
|
|
|
+ auto &var_store = var_info.value;
|
|
|
+ auto var_type = var_info.type;
|
|
|
+ if (var_len == 0) { // empty variable
|
|
|
+ var_store.emplace<0>();
|
|
|
+ } else {
|
|
|
+ if (var_store.index() == 0) [[unlikely]] {
|
|
|
+ init_var_store(&var_store, var_type);
|
|
|
+ }
|
|
|
+ auto old_ptr = in_ptr;
|
|
|
+ in_ptr = read_variable(in_ptr, &var_store, var_type);
|
|
|
+ assert(in_ptr - old_ptr == var_len);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void update_in_var() {
|
|
|
+ assert(context != nullptr);
|
|
|
+ if (!socket_in->is_open()) return;
|
|
|
+ while (try_receive_msg(socket_in.get())) {
|
|
|
+ handle_var_update_msg();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void update_out_var(int var_id) {
|
|
|
+ const auto &var_info = var_out_pool.at(var_id);
|
|
|
+ auto &var_store = var_info.value;
|
|
|
+ auto var_len = var_bin_len[var_store.index()];
|
|
|
+ auto var_index = var_info.index;
|
|
|
+
|
|
|
+ auto var_frag_len = sizeof(var_index_type) + sizeof(var_len_type) + var_len;
|
|
|
+ auto next_out_len = cur_out_len + var_frag_len;
|
|
|
+ if (next_out_len > std::numeric_limits<msg_len_type>::max()) {
|
|
|
+ commit_out_var();
|
|
|
+ next_out_len = cur_out_len + var_frag_len;
|
|
|
+ }
|
|
|
+ ensure_buffer(0, next_out_len, true);
|
|
|
+ auto out_ptr = out_data + cur_out_len;
|
|
|
+ out_ptr = write_binary_numbers(out_ptr, var_index, var_len);
|
|
|
+ out_ptr = write_variable(out_ptr, var_store);
|
|
|
+ assert(out_ptr - out_data == next_out_len);
|
|
|
+ cur_out_len = next_out_len;
|
|
|
+ }
|
|
|
+
|
|
|
+ void commit_out_var() {
|
|
|
+ assert(cur_out_len >= sizeof(msg_len_type));
|
|
|
+ if (cur_out_len <= sizeof(msg_len_type)) return;
|
|
|
+ write_binary_number(out_data, cur_out_len);
|
|
|
+ write(*socket_out, buffer(out_data, cur_out_len));
|
|
|
+ cur_out_len = sizeof(msg_len_type);
|
|
|
+ }
|
|
|
+
|
|
|
+ ~impl() {
|
|
|
+ free(in_data);
|
|
|
+ free(out_data);
|
|
|
+ }
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ variable_io::variable_io()
|
|
|
+ : pimpl(std::make_unique<impl>()) {}
|
|
|
+
|
|
|
+ variable_io::~variable_io() = default;
|
|
|
+
|
|
|
+ int variable_io::add_variable(std::string_view name, variable_type type, variable_direction dir) {
|
|
|
+ auto &pool = (dir == VD_Input ? pimpl->var_in_pool : pimpl->var_out_pool);
|
|
|
+ auto var_id = (int) pool.size();
|
|
|
+ auto &var_info = pool.emplace_back();
|
|
|
+ var_info.type = type;
|
|
|
+ var_info.name = name;
|
|
|
+ return var_id;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool variable_io::connect(std::string_view server, uint16_t port) {
|
|
|
+ pimpl->server_ep = tcp::endpoint{address::from_string(server.data()), port};
|
|
|
+ try {
|
|
|
+ pimpl->initialize();
|
|
|
+ } catch (std::exception &e) {
|
|
|
+ SPDLOG_ERROR("Failed while establishing variable IO: {}", e.what());
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool variable_io::retrieve_update() {
|
|
|
+ try {
|
|
|
+ pimpl->update_in_var();
|
|
|
+ } catch (std::exception &e) {
|
|
|
+ SPDLOG_ERROR("Failed while retrieving variable update: {}", e.what());
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+#define VAL_RET_CASE_IMPL(TYPE) \
|
|
|
+ std::optional<TYPE##_type> variable_io::query_##TYPE##_variable(int variable_id) { \
|
|
|
+ const auto &var_store = pimpl->var_in_pool.at(variable_id).value; \
|
|
|
+ if (var_store.index() == 0) return {}; \
|
|
|
+ return std::get<TYPE##_type>(var_store); \
|
|
|
+ } \
|
|
|
+ void variable_io::update_##TYPE##_variable(int variable_id, std::optional<TYPE##_type> val) { \
|
|
|
+ assert(pimpl->var_out_pool[variable_id].type == VT_##TYPE); \
|
|
|
+ auto &var_store = pimpl->var_out_pool.at(variable_id).value; \
|
|
|
+ if (val.has_value()) { \
|
|
|
+ var_store = val.value(); \
|
|
|
+ } else { \
|
|
|
+ var_store.emplace<0>(); \
|
|
|
+ } \
|
|
|
+ pimpl->update_out_var(variable_id); \
|
|
|
+ }
|
|
|
+
|
|
|
+ VAL_RET_CASE_IMPL(bool)
|
|
|
+
|
|
|
+ VAL_RET_CASE_IMPL(integer)
|
|
|
+
|
|
|
+ VAL_RET_CASE_IMPL(float)
|
|
|
+
|
|
|
+#undef VAL_RET_CASE_IMPL
|
|
|
+
|
|
|
+#define REF_RET_CASE_IMPL(TYPE) \
|
|
|
+ std::optional<const TYPE##_type *> variable_io::query_##TYPE##_variable(int variable_id) { \
|
|
|
+ const auto &var_store = pimpl->var_in_pool.at(variable_id).value; \
|
|
|
+ if (var_store.index() == 0) return {}; \
|
|
|
+ return &std::get<TYPE##_type>(var_store); \
|
|
|
+ } \
|
|
|
+ void variable_io::update_##TYPE##_variable(int variable_id, std::optional<const TYPE##_type *> val) { \
|
|
|
+ assert(pimpl->var_out_pool[variable_id].type == VT_##TYPE); \
|
|
|
+ auto &var_store = pimpl->var_out_pool.at(variable_id).value; \
|
|
|
+ if (val.has_value()) { \
|
|
|
+ var_store = *val.value(); \
|
|
|
+ } else { \
|
|
|
+ var_store.emplace<0>(); \
|
|
|
+ } \
|
|
|
+ pimpl->update_out_var(variable_id); \
|
|
|
+ }
|
|
|
+
|
|
|
+ REF_RET_CASE_IMPL(scalar_xyz)
|
|
|
+
|
|
|
+ REF_RET_CASE_IMPL(transform)
|
|
|
+
|
|
|
+ REF_RET_CASE_IMPL(array6)
|
|
|
+
|
|
|
+#undef REF_RET_CASE_IMPL
|
|
|
+
|
|
|
+ bool variable_io::commit_update() {
|
|
|
+ try {
|
|
|
+ pimpl->commit_out_var();
|
|
|
+ } catch (std::exception &e) {
|
|
|
+ SPDLOG_ERROR("Failed while committing variable update: {}", e.what());
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+#ifdef HAVE_IMGUI
|
|
|
+
|
|
|
+ void variable_io::show_input_variable(int variable_id) {
|
|
|
+ const auto &var_info = pimpl->var_in_pool.at(variable_id);
|
|
|
+ impl::show_variable(&var_info.value, var_info.type, var_info.name);
|
|
|
+ }
|
|
|
+
|
|
|
+#endif
|
|
|
+
|
|
|
+};
|