|
|
@@ -0,0 +1,182 @@
|
|
|
+#include "versatile_saver.h"
|
|
|
+#include "core/imgui_utility.hpp"
|
|
|
+#include "image_process_v5/sp_image.h"
|
|
|
+#include "image_process_v5/image_process.h"
|
|
|
+
|
|
|
+#include <ImGuiFileDialog.h>
|
|
|
+
|
|
|
+#include <boost/asio/post.hpp>
|
|
|
+
|
|
|
+using obj_type = object_manager_v2::obj_query_config;
|
|
|
+using boost::asio::post;
|
|
|
+
|
|
|
+extern boost::asio::io_context *main_ctx;
|
|
|
+
|
|
|
+namespace {
|
|
|
+ bool can_save_jpg(const obj_type &obj) {
|
|
|
+ return true; // TODO
|
|
|
+ }
|
|
|
+
|
|
|
+ bool can_save_png(const obj_type &obj) {
|
|
|
+ return true; // TODO
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+struct versatile_saver::impl {
|
|
|
+ enum save_type_enum: int {
|
|
|
+ JPG, PNG, // for sp_image
|
|
|
+ UNKNOWN
|
|
|
+ };
|
|
|
+
|
|
|
+ create_config conf;
|
|
|
+
|
|
|
+ struct item_info_type : create_config::item_info {
|
|
|
+ save_type_enum save_type = UNKNOWN;
|
|
|
+ obj_conn_type conn;
|
|
|
+ timestamp_type last_update_ts = 0;
|
|
|
+ bool is_selected = true;
|
|
|
+ };
|
|
|
+
|
|
|
+ using item_list_type = std::vector<item_info_type>;
|
|
|
+ item_list_type items;
|
|
|
+
|
|
|
+ size_t save_cnt = 0;
|
|
|
+ bool enable_autosave = false;
|
|
|
+ timestamp_type last_save_ts = 0;
|
|
|
+
|
|
|
+ constexpr static auto filepath_max_length = 256;
|
|
|
+ char filepath_buf[filepath_max_length] = {};
|
|
|
+ int min_interval_ms = 0;
|
|
|
+
|
|
|
+ void save_jpg(item_info_type &item) {
|
|
|
+ image_save_jpg(OBJ_QUERY(sp_image, item.name), get_save_filename(item));
|
|
|
+ }
|
|
|
+
|
|
|
+ void save_png(item_info_type &item) {
|
|
|
+ image_save_png(OBJ_QUERY(sp_image, item.name), get_save_filename(item));
|
|
|
+ }
|
|
|
+
|
|
|
+ void save_item(item_info_type &item) {
|
|
|
+ switch (item.save_type) {
|
|
|
+ //@formatter:off
|
|
|
+ case JPG: { save_jpg(item); break; }
|
|
|
+ case PNG: { save_png(item); break; }
|
|
|
+ default: { assert(false); }
|
|
|
+ //@formatter:on
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void save_all() {
|
|
|
+ for (auto &item: items) {
|
|
|
+ if (!item.is_selected) continue;
|
|
|
+ TP_DETACH([&] { save_item(item); });
|
|
|
+ }
|
|
|
+ last_save_ts = current_timestamp();
|
|
|
+ ++save_cnt;
|
|
|
+ }
|
|
|
+
|
|
|
+ item_info_type &query_item(obj_name_type name) {
|
|
|
+ const auto iter = std::ranges::find_if(
|
|
|
+ items, [=](const auto &item) { return item.name == name; });
|
|
|
+ assert(iter != items.end());
|
|
|
+ return *iter;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool should_auto_save() {
|
|
|
+ assert(enable_autosave);
|
|
|
+ return std::ranges::all_of(items, [this](const auto &item) {
|
|
|
+ if (!item.is_selected) return true;
|
|
|
+ return item.last_update_ts - last_save_ts > min_interval_ms * 1e3;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ void object_callback(const obj_name_type name) {
|
|
|
+ auto &item = query_item(name);
|
|
|
+ item.last_update_ts = OBJ_TS(name);
|
|
|
+ if (enable_autosave && should_auto_save()) {
|
|
|
+ save_all();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ std::string get_save_filename(item_info_type &item) {
|
|
|
+ const auto filename = fmt::format("{}_{}", item.save_name, save_cnt);
|
|
|
+ const auto save_folder = std::filesystem::path(filepath_buf);
|
|
|
+ if (!exists(save_folder)) {
|
|
|
+ create_directories(save_folder);
|
|
|
+ }
|
|
|
+ return save_folder / filename;
|
|
|
+ }
|
|
|
+
|
|
|
+ void show_ui() {
|
|
|
+ ImGui::Checkbox("Autosave", &enable_autosave);
|
|
|
+ ImGui::SameLine();
|
|
|
+ if (!enable_autosave) {
|
|
|
+ if (ImGui::Button("Save")) {
|
|
|
+ post(*main_ctx, [this] { save_all(); });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (true) {
|
|
|
+ auto guard = imgui_disable_guard(enable_autosave);
|
|
|
+ ImGui::InputText("Save Folder", filepath_buf, filepath_max_length);
|
|
|
+ ImGui::InputInt("Autosave Interval (ms)", &min_interval_ms);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (auto &item: items) {
|
|
|
+ if (ImGui::TreeNode(item.save_name.c_str())) {
|
|
|
+ ImGui::Checkbox("##select", &item.is_selected);
|
|
|
+
|
|
|
+ // query object info
|
|
|
+ auto item_info = obj_type();
|
|
|
+ item_info.name = item.name;
|
|
|
+ main_ob->query_all(item_info);
|
|
|
+
|
|
|
+ // save type
|
|
|
+ ImGui::SameLine();
|
|
|
+ if (can_save_jpg(item_info)) {
|
|
|
+ ImGui::SameLine();
|
|
|
+ ImGui::RadioButton("JPG", (int *) &item.save_type, JPG);
|
|
|
+ }
|
|
|
+ if (can_save_png(item_info)) {
|
|
|
+ ImGui::SameLine();
|
|
|
+ ImGui::RadioButton("PNG", (int *) &item.save_type, PNG);
|
|
|
+ }
|
|
|
+ ImGui::TreePop();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ explicit impl(create_config _conf)
|
|
|
+ : conf(std::move(_conf)) {
|
|
|
+ std::ranges::transform(
|
|
|
+ conf.items, std::back_inserter(items),
|
|
|
+ [this](const create_config::item_info &item) {
|
|
|
+ auto ret = item_info_type();
|
|
|
+ *(create_config::item_info *) &ret = item;
|
|
|
+ ret.conn = OBJ_SIG(ret.name)->connect(
|
|
|
+ [this](auto name) { object_callback(name); });
|
|
|
+ ret.save_type = JPG; // TODO: check for item type
|
|
|
+ return ret;
|
|
|
+ });
|
|
|
+
|
|
|
+ strcpy(filepath_buf, "./capture");
|
|
|
+ min_interval_ms = conf.min_interval_ms;
|
|
|
+ }
|
|
|
+
|
|
|
+ ~impl() {
|
|
|
+ for (auto &item: items) {
|
|
|
+ item.conn.disconnect();
|
|
|
+ }
|
|
|
+ TP_SYNC;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+versatile_saver::versatile_saver(const create_config &conf)
|
|
|
+ : pimpl(std::make_unique<impl>(conf)) {
|
|
|
+}
|
|
|
+
|
|
|
+versatile_saver::~versatile_saver() = default;
|
|
|
+
|
|
|
+void versatile_saver::show_ui() const {
|
|
|
+ pimpl->show_ui();
|
|
|
+}
|