From 0802f09370bdcf09af581690cd47de75fe98e21b Mon Sep 17 00:00:00 2001 From: Adrian Del Grosso <10929341+ad3154@users.noreply.github.com> Date: Thu, 17 Aug 2023 20:11:23 -0600 Subject: [PATCH] Added remaining render items, fix compiling on Ubuntu --- CMakeLists.txt | 3 +- EXAMPLE.iop | Bin 0 -> 2360 bytes README.md | 34 +- include/L2DFileDialog.hpp | 36 +- include/gui.hpp | 29 +- src/gui.cpp | 1151 ++++++++++++++++++++++++++++++------- submodules/agisostack | 2 +- 7 files changed, 1039 insertions(+), 216 deletions(-) create mode 100644 EXAMPLE.iop diff --git a/CMakeLists.txt b/CMakeLists.txt index cb750ce..94d4680 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ include(CMakePackageConfigHelpers) set(CPACK_DEBIAN_FILENAME DEB-DEFAULT) set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE) set(CPACK_PACKAGE_VENDOR "Open-Agriculture") -set(CPACK_PACKAGE_CONTACT ${contact}) +set(CPACK_PACKAGE_CONTACT "delgrossoengineering@protonmail.com") set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/Open-Agriculture") set(CPACK_PACKAGE_VERSION ${CMAKE_PROJECT_VERSION}) if(CPack_CMake_INCLUDED EQUAL 1) @@ -26,6 +26,7 @@ set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) find_package(OpenGL REQUIRED) +set(BUILD_TESTING OFF) add_subdirectory(submodules/agisostack) add_subdirectory(submodules/sdl) diff --git a/EXAMPLE.iop b/EXAMPLE.iop new file mode 100644 index 0000000000000000000000000000000000000000..6473a17374b9348cf1bffbbc40b03b57e693bc3c GIT binary patch literal 2360 zcmZwJy>Ht_6aesd$Fn6_{*XzL`j$?f0vJs!$=~@f6iGk>7>HoePLrVkq0o+COH^eF zskZRO;vfuci!4n+s$-KmxtlGMa^$KShqkJ71m(g4uw zXOAwOj%#(#*Vp;+py!(Qr8`%-y>WYk0z~1};#;Lst<~4pF~;pd8;*}B&6Y`kL^{Kf zb!CqLFNz-q&32Q*mq4Pc^{zX%F8C8?bk0$C?a2fO6;J>Y>XH4)7_;!EeQJ#_T;6WV zQ3fwTNi3L-i%*)pzI~TrqB)q4HJrh~?z+whvm!AM5nLI1^Y9w;#%II)qk9a)5qR@{ z_^Ak+QTQ5SxK?If4aIs|W(+8M=s10fYzkH&0jm&)Wl-4~Q$d3y=wRST8ZwZD9OR(@ zMJNG>GNw0IFuk~K7TLzOs+Hz4XjquQ?1_Mj6dl;cVFBW zJ6VDHdXot zB0tLs#b|ouo;U6m&9_ zP9pvL&we@?q;(Hni%43hI?)BKhtfLIzx?W_^&o9{=*_644QdcW&_*b2ApPTSe%c7q zDG$9BlXQxvNJ`MDP&$S5cYpZlRFF=4=5PZoiAy>| zGbAJEOemc}`iuYkbS6k=J@oFPq_Z?jvVzWr(pjWmvYVf#>3f|G(m4;kwEy6O{hPRg66F d%s9mRyR#W2((et%u6>EogV`*?4)46M-T}!iW2gWC literal 0 HcmV?d00001 diff --git a/README.md b/README.md index 5af4981..faed0f9 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,35 @@ # AgIsoStack DDOP Generator -## Wip +## Overview + +This program allows you to create, save, load, and edit ISO11783 device descriptor object pools (DDOP) for use with communicating with an ISOBUS task controller. + +It is written in C++ and is based on the DDOP objects from [AgIsoStack++](https://github.com/Open-Agriculture/AgIsoStack-plus-plus), with a simple GUI created with [Dear ImGui](https://github.com/ocornut/imgui) running on OpenGL3. + +When used in combination with AgIsoStack (or any other TC client), it provides an easy way to visualize your DDOP, build a binary version of it, and easily deserialize it back into C++ objects if you want to interact with a DDOP in your own application. + +![Main Screen Image](docs/images/example1.png) + +### Features + +* Supports dynamically editing any DDOP or creating one from scratch +* Compatible with both TC version 3 and 4 +* Basic object pool error checking to help you find errors before loading onto a TC +* Completely free and open source alternative to many paid products! + +### Compilation + +This project is built with CMake. + +Make sure you have all the dependencies installed. + +``` +sudo apt install cmake build-essential +``` + +Then compile with CMake: + +``` +cmake -S . -B build +cmake --build build +``` diff --git a/include/L2DFileDialog.hpp b/include/L2DFileDialog.hpp index a32e889..7ae6481 100644 --- a/include/L2DFileDialog.hpp +++ b/include/L2DFileDialog.hpp @@ -31,8 +31,9 @@ #include #include -#include +#include #include +#include #include #include #include @@ -287,7 +288,11 @@ namespace FileDialog std::time_t tt = std::chrono::system_clock::to_time_t(st); std::tm mt; +#ifndef _MSC_VER + localtime_r(&tt, &mt); +#else localtime_s(&mt, &tt); +#endif std::stringstream ss; ss << std::put_time(&mt, "%F %R"); @@ -296,6 +301,11 @@ namespace FileDialog } ImGui::EndChild(); + if (file_dialog_current_path.empty()) + { + file_dialog_current_path = std::filesystem::current_path().string(); + } + std::string selected_file_path = file_dialog_current_path + (file_dialog_current_path.back() == '\\' ? "" : "\\") + (file_dialog_current_folder.size() > 0 ? file_dialog_current_folder : file_dialog_current_file); char *buf = &selected_file_path[0]; ImGui::PushItemWidth(724); @@ -321,7 +331,7 @@ namespace FileDialog { if (strlen(new_folder_name) <= 0) { - strcpy_s(new_folder_error, "Folder name can't be empty"); + strncpy(new_folder_error, "Folder name can't be empty", sizeof(new_folder_error)); } else { @@ -333,11 +343,11 @@ namespace FileDialog ImGui::SameLine(); if (ImGui::Button("Cancel##1")) { - strcpy_s(new_folder_name, ""); - strcpy_s(new_folder_error, ""); + strncpy(new_folder_name, "", sizeof(new_folder_name)); + strncpy(new_folder_error, "", sizeof(new_folder_error)); ImGui::CloseCurrentPopup(); } - ImGui::TextColored(ImColor(1.0f, 0.0f, 0.2f, 1.0f), new_folder_error); + ImGui::TextColored(ImColor(1.0f, 0.0f, 0.2f, 1.0f), "%s", new_folder_error); ImGui::EndPopup(); } @@ -367,7 +377,7 @@ namespace FileDialog file_dialog_file_select_index = 0; file_dialog_folder_select_index = 0; file_dialog_current_file = ""; - strcpy_s(file_dialog_error, ""); + strncpy(file_dialog_error, "", sizeof(file_dialog_error)); initial_path_set = false; file_dialog_open = false; }; @@ -400,13 +410,13 @@ namespace FileDialog { if (file_dialog_current_folder == "") { - strcpy_s(file_dialog_error, "Error: You must select a folder!"); + strncpy(file_dialog_error, "Error: You must select a folder!", sizeof(file_dialog_error)); } else { auto path = file_dialog_current_path + (file_dialog_current_path.back() == '\\' ? "" : "\\") + file_dialog_current_file; - strcpy_s(buffer, path.length() + 1, path.c_str()); - strcpy_s(file_dialog_error, ""); + strncpy(buffer, path.c_str(), sizeof(buffer)); + strncpy(file_dialog_error, "", sizeof(file_dialog_error)); reset_everything(); } } @@ -414,13 +424,13 @@ namespace FileDialog { if (file_dialog_current_file == "") { - strcpy_s(file_dialog_error, "Error: You must select a file!"); + strncpy(file_dialog_error, "Error: You must select a file!", sizeof(file_dialog_error)); } else { auto path = file_dialog_current_path + (file_dialog_current_path.back() == '\\' ? "" : "\\") + file_dialog_current_file; - strcpy_s(buffer, path.length() + 1, path.c_str()); - strcpy_s(file_dialog_error, ""); + strncpy(buffer, path.c_str(), buffer_size); + strncpy(file_dialog_error, "", sizeof(file_dialog_error)); reset_everything(); } } @@ -428,7 +438,7 @@ namespace FileDialog if (strlen(file_dialog_error) > 0) { - ImGui::TextColored(ImColor(1.0f, 0.0f, 0.2f, 1.0f), file_dialog_error); + ImGui::TextColored(ImColor(1.0f, 0.0f, 0.2f, 1.0f), "%s", file_dialog_error); } ImGui::End(); diff --git a/include/gui.hpp b/include/gui.hpp index c990bbb..1f677ca 100644 --- a/include/gui.hpp +++ b/include/gui.hpp @@ -23,6 +23,9 @@ class DDOPGeneratorGUI void start(); +private: + static constexpr std::size_t FILE_PATH_BUFFER_MAX_LENGTH = 1024; + bool render_menu_bar(); void render_open_file_menu(); void parseElementChildrenOfElement(std::uint16_t objectID); @@ -33,18 +36,21 @@ class DDOPGeneratorGUI void render_device_process_data_settings(std::shared_ptr object); void render_device_property_settings(std::shared_ptr object); void render_device_presentation_settings(std::shared_ptr object); + void render_object_components(std::shared_ptr object); void render_current_selected_object_settings(std::shared_ptr object); + void render_device_element_components(std::shared_ptr object); + void render_device_process_data_components(std::shared_ptr object); + void render_device_property_components(std::shared_ptr object); + void render_device_presentation_components(std::shared_ptr object); void render_save(); + void render_all_objects(); void on_selected_object_changed(std::shared_ptr newObject); - -private: - static constexpr std::size_t FILE_PATH_BUFFER_MAX_LENGTH = 1024; - static std::string get_element_type_string(isobus::task_controller_object::DeviceElementObject::Type type); static std::string get_object_type_string(isobus::task_controller_object::ObjectTypes type); const std::array generate_localization_label(); + std::uint16_t get_first_unused_id() const; - std::string languageCode; ///< The last received language code, such as "en", "es", "de", etc. + std::string languageCode; isobus::LanguageCommandInterface::DecimalSymbols decimalSymbol = isobus::LanguageCommandInterface::DecimalSymbols::Point; isobus::LanguageCommandInterface::TimeFormats timeFormat = isobus::LanguageCommandInterface::TimeFormats::TwelveHourAmPm; isobus::LanguageCommandInterface::DateFormats dateFormat = isobus::LanguageCommandInterface::DateFormats::mmddyyyy; @@ -66,11 +72,24 @@ class DDOPGeneratorGUI char structureLabelBuffer[8] = { 0 }; char extendedStructureLabelBuffer[129] = { 0 }; char hexIsoNameBuffer[17] = { 0 }; + char languageCodeBuffer[3] = { 0 }; std::string lastFileName; int elementNumberBuffer = 0; int parentObjectBuffer = 0; + int ddiBuffer = 0; + int objectIDBuffer = 0; + int presentationObjectBuffer = 0; + int valueBuffer = 0; + int numberDecimalsBuffer = 0; + int offsetBuffer = 0; + int versionIndex = 0; + int addChildComboIndex = 0; + float scaleBuffer = 0.0f; std::uint16_t selectedObjectID = 0xFFFF; + std::array propertiesBitfieldBuffer = { false }; + std::array triggerBitfieldBuffer = { false }; bool openFileDialogue = false; + bool saveModal = false; bool saveAsModal = false; bool currentPoolValid = false; }; diff --git a/src/gui.cpp b/src/gui.cpp index f78b0e1..a60b8c8 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -133,6 +133,7 @@ void DDOPGeneratorGUI::start() // GUI Main Code: bool prevSaveAsModalState = saveAsModal; + bool prevSaveModalState = saveModal; shouldExit = render_menu_bar(); render_open_file_menu(); @@ -141,6 +142,11 @@ void DDOPGeneratorGUI::start() ImGui::OpenPopup("##Save As Modal"); } + if ((saveModal != prevSaveModalState)) + { + ImGui::OpenPopup("##Save Modal"); + } + render_save(); if ((nullptr != currentObjectPool) && currentPoolValid) @@ -155,6 +161,7 @@ void DDOPGeneratorGUI::start() ImGui::BeginChild("ChildL", ImVec2(ImGui::GetContentRegionAvail().x * 0.5f, ImGui::GetContentRegionAvail().y), false); ImGui::SeparatorText("Object Tree"); render_object_tree(); + render_all_objects(); ImGui::EndChild(); ImGui::SameLine(); @@ -168,8 +175,43 @@ void DDOPGeneratorGUI::start() { ImGui::Text("Object Type: "); ImGui::SameLine(); - ImGui::Text((get_object_type_string(selectedObject->get_object_type()) + " (" + selectedObject->get_table_id() + ") ").c_str()); + ImGui::Text("%s", (get_object_type_string(selectedObject->get_object_type()) + " (" + selectedObject->get_table_id() + ") ").c_str()); render_current_selected_object_settings(selectedObject); + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 0.0f, 0.0f, 0.8f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 0.0f, 0.0f, 0.7f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 0.0f, 0.0f, 0.6f)); + if ((selectedObject->get_object_type() != isobus::task_controller_object::ObjectTypes::Device) && + ImGui::Button("Delete Object")) + { + std::uint16_t idOfDeletedObject = selectedObject->get_object_id(); + currentObjectPool->remove_object_by_id(selectedObject->get_object_id()); + + // Prune all other references to this object + for (std::uint32_t i = 0; i < currentObjectPool->size(); i++) + { + auto objectToProcess = currentObjectPool->get_object_by_index(i); + + if ((nullptr != objectToProcess) && + (isobus::task_controller_object::ObjectTypes::DeviceElement == objectToProcess->get_object_type())) + { + auto element = std::dynamic_pointer_cast(objectToProcess); + + for (std::size_t j = 0; j < element->get_number_child_objects(); j++) + { + if (element->get_child_object_id(j) != idOfDeletedObject) + { + element->remove_reference_to_child_object(idOfDeletedObject); + } + if (element->get_parent_object() == idOfDeletedObject) + { + element->set_parent_object(0xFFFF); + } + } + } + } + } + ImGui::PopStyleColor(3); } } ImGui::EndChild(); @@ -178,8 +220,6 @@ void DDOPGeneratorGUI::start() ImGui::End(); } - ImGui::ShowDemoWindow(); // For testing - // Rendering ImGui::Render(); glViewport(0, 0, static_cast(lIO.DisplaySize.x), static_cast(lIO.DisplaySize.y)); @@ -203,16 +243,36 @@ void DDOPGeneratorGUI::start() bool DDOPGeneratorGUI::render_menu_bar() { bool retVal = false; + bool shouldShowErrors = false; + bool shouldShowNoErrors = false; + bool shouldShowNewDDOP = false; + bool shouldShowAbout = false; if (true == ImGui::BeginMainMenuBar()) { if (true == ImGui::BeginMenu("File")) { - if (true == ImGui::MenuItem("Quit", "Exit gracefully")) + if (ImGui::MenuItem("Quit", "Exit gracefully")) { retVal = true; } - if (true == ImGui::MenuItem("Open", "Load a DDOP from a file")) + if (ImGui::MenuItem("New", "Create a new DDOP")) + { + shouldShowNewDDOP = true; + + currentObjectPool.reset(); + currentPoolValid = false; + currentObjectPool = std::make_unique(); + + currentObjectPool->add_device("New Device", + "1.0.0", + "0", + "0", + std::array(), + std::vector(), + 0); + } + if (ImGui::MenuItem("Open", "Load a DDOP from a file")) { FileDialog::file_dialog_open = true; openFileDialogue = true; @@ -222,13 +282,18 @@ bool DDOPGeneratorGUI::render_menu_bar() { ImGui::BeginDisabled(); } - if (true == ImGui::MenuItem("Save as", "Save current DDOP to a file")) + if (ImGui::MenuItem("Save", "Overwrite current DDOP file")) + { + FileDialog::file_dialog_open = false; + saveModal = true; + } + if (ImGui::MenuItem("Save as", "Save current DDOP to a file")) { FileDialog::file_dialog_open = false; saveAsModal = true; memset(filePathBuffer, 0, IM_ARRAYSIZE(filePathBuffer)); } - if (true == ImGui::MenuItem("Close", "Closes the active file")) + if (ImGui::MenuItem("Close", "Closes the active file")) { currentObjectPool.reset(); currentPoolValid = false; @@ -239,8 +304,169 @@ bool DDOPGeneratorGUI::render_menu_bar() } ImGui::EndMenu(); } + + if (true == ImGui::BeginMenu("Edit")) + { + if (!currentPoolValid) + { + ImGui::BeginDisabled(); + } + + if (true == ImGui::MenuItem("Check for Errors", "Serialize the DDOP and display detected errors")) + { + if ((nullptr != currentObjectPool) && currentPoolValid) + { + std::vector binaryDDOP; + logger.logHistory.clear(); + auto serializationSuccess = currentObjectPool->generate_binary_object_pool(binaryDDOP); + + if (serializationSuccess) + { + shouldShowNoErrors = true; + } + else + { + shouldShowErrors = true; + } + } + } + else if (!currentPoolValid) + { + ImGui::EndDisabled(); + } + ImGui::EndMenu(); + } + + if ((nullptr == currentObjectPool) || (false == currentPoolValid)) + { + ImGui::BeginDisabled(); + } + + if (true == ImGui::BeginMenu("Create Object")) + { + if (true == ImGui::MenuItem("Device Element")) + { + currentObjectPool->add_device_element("Designator", 0, 0xFFFF, isobus::task_controller_object::DeviceElementObject::Type::Function, get_first_unused_id()); + auto newObject = currentObjectPool->get_object_by_index(currentObjectPool->size() - 1); + on_selected_object_changed(newObject); + selectedObjectID = newObject->get_object_id(); + } + if (true == ImGui::MenuItem("Device Process Data")) + { + currentObjectPool->add_device_process_data("Designator", 0, 0xFFFF, 0, 0, get_first_unused_id()); + auto newObject = currentObjectPool->get_object_by_index(currentObjectPool->size() - 1); + on_selected_object_changed(newObject); + selectedObjectID = newObject->get_object_id(); + } + if (true == ImGui::MenuItem("Device Property")) + { + currentObjectPool->add_device_property("Designator", 0, 0, 0xFFFF, get_first_unused_id()); + auto newObject = currentObjectPool->get_object_by_index(currentObjectPool->size() - 1); + on_selected_object_changed(newObject); + selectedObjectID = newObject->get_object_id(); + } + if (true == ImGui::MenuItem("Device Value Presentation")) + { + currentObjectPool->add_device_value_presentation("Designator", 0, 0.0f, 0, get_first_unused_id()); + auto newObject = currentObjectPool->get_object_by_index(currentObjectPool->size() - 1); + on_selected_object_changed(newObject); + selectedObjectID = newObject->get_object_id(); + } + ImGui::EndMenu(); + } + + if ((nullptr == currentObjectPool) || (false == currentPoolValid)) + { + ImGui::EndDisabled(); + } + + if (true == ImGui::BeginMenu("About")) + { + ImGui::EndMenu(); + shouldShowAbout = true; + } + ImGui::EndMainMenuBar(); } + + if (shouldShowNoErrors) + { + ImGui::OpenPopup("No Serialization Errors"); + } + else if (shouldShowErrors) + { + ImGui::OpenPopup("Serialization Errors"); + } + else if (shouldShowNewDDOP) + { + ImGui::OpenPopup("New DDOP"); + } + else if (shouldShowAbout) + { + ImGui::OpenPopup("About"); + } + + if (ImGui::BeginPopupModal("No Serialization Errors", NULL, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::Text("No serialization errors detected."); + ImGui::Text("This does not mean the DDOP will be accepted by a TC"); + ImGui::Text("it only confirms the structure of the DDOP is valid."); + + ImGui::SetItemDefaultFocus(); + if (ImGui::Button("OK", ImVec2(120, 0))) + { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + if (ImGui::BeginPopupModal("Serialization Errors", NULL, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::Text("Serialization errors detected."); + ImGui::Separator(); + + for (auto &logString : logger.logHistory) + { + ImGui::Text("%s", logString.logText.c_str()); + } + + ImGui::SetItemDefaultFocus(); + if (ImGui::Button("OK", ImVec2(120, 0))) + { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + if (ImGui::BeginPopupModal("New DDOP", NULL, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::Text("Enter your device information to create a new DDOP"); + ImGui::Separator(); + + selectedObjectID = 0; + on_selected_object_changed(currentObjectPool->get_object_by_index(0)); + render_device_settings(std::dynamic_pointer_cast(currentObjectPool->get_object_by_index(0))); + ImGui::Separator(); + + ImGui::SetItemDefaultFocus(); + if (ImGui::Button("OK", ImVec2(120, 0))) + { + currentPoolValid = true; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + if (ImGui::BeginPopupModal("About", NULL, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::Text("A free Open-Agriculture Project"); + ImGui::Text("MIT Licensed: by acquiring a copy of this software you agree to our license."); + ImGui::Separator(); + if (ImGui::Button("OK")) + { + shouldShowAbout = false; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + return retVal; } @@ -253,12 +479,11 @@ void DDOPGeneratorGUI::render_open_file_menu() else if (openFileDialogue) { std::string selectedFileToRead(filePathBuffer); + lastFileName = selectedFileToRead; memset(filePathBuffer, 0, FILE_PATH_BUFFER_MAX_LENGTH); openFileDialogue = false; - if (!selectedFileToRead.empty() && - (selectedFileToRead.substr(selectedFileToRead.length() - 4) == ".iop") || - (selectedFileToRead.substr(selectedFileToRead.length() - 4) == ".IOP")) + if (!selectedFileToRead.empty()) { loadedIopData = isobus::IOPFileInterface::read_iop_file(selectedFileToRead); @@ -304,7 +529,7 @@ void DDOPGeneratorGUI::render_open_file_menu() for (auto &logString : logger.logHistory) { - ImGui::Text(logString.logText.c_str()); + ImGui::Text("%s", logString.logText.c_str()); } ImGui::SetItemDefaultFocus(); @@ -348,8 +573,7 @@ void DDOPGeneratorGUI::parseElementChildrenOfElement(std::uint16_t aObjectID) if (isElementOpen) { - ImGui::Text(("Element Number: " + std::to_string(currentElement->get_element_number())).c_str()); - ImGui::Text(("Type: " + elementType).c_str()); + render_device_element_components(currentElement); parseChildren(currentElement); ImGui::TreePop(); @@ -369,181 +593,44 @@ void DDOPGeneratorGUI::parseChildren(std::shared_ptrget_object_by_id(element->get_child_object_id(c)); - if (selectedObjectID == currentChild->get_object_id()) - { - childFlags |= ImGuiTreeNodeFlags_Selected; - } - - bool isChildOpen = false; - - if (currentChild->get_object_type() != isobus::task_controller_object::ObjectTypes::DeviceElement) + if (nullptr != currentChild) { - ImGui::Indent(); - isChildOpen = ImGui::TreeNodeEx((currentChild->get_designator() + " (" + currentChild->get_table_id() + " " + std::to_string(currentChild->get_object_id()) + ")").c_str(), childFlags); - ImGui::Unindent(); - - if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) + if (selectedObjectID == currentChild->get_object_id()) { - selectedObjectID = currentChild->get_object_id(); - on_selected_object_changed(currentChild); + childFlags |= ImGuiTreeNodeFlags_Selected; } - } - - if (isChildOpen) - { - if (isobus::task_controller_object::ObjectTypes::DeviceProcessData == currentChild->get_object_type()) - { - auto currentDPD = std::dynamic_pointer_cast(currentChild); - ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "DDI: %u", currentDPD->get_ddi()); + bool isChildOpen = false; - bool areAnyTriggers = false; - ImGui::Text("Triggers:"); + if (currentChild->get_object_type() != isobus::task_controller_object::ObjectTypes::DeviceElement) + { ImGui::Indent(); - for (std::uint8_t t = 0; t < 5; t++) - { - if (0 != ((1 << t) & currentDPD->get_trigger_methods_bitfield())) - { - switch (static_cast(1 << t)) - { - case isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::DistanceInterval: - { - ImGui::BulletText("Distance Interval"); - areAnyTriggers = true; - } - break; - - case isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::OnChange: - { - ImGui::BulletText("On Change"); - areAnyTriggers = true; - } - break; - - case isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::ThresholdLimits: - { - ImGui::BulletText("Threshold Limits"); - areAnyTriggers = true; - } - break; - - case isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::TimeInterval: - { - ImGui::BulletText("Time Interval"); - areAnyTriggers = true; - } - break; - - case isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::Total: - { - ImGui::BulletText("Total"); - areAnyTriggers = true; - } - break; - - default: - { - ImGui::BulletText("Proprietary or Reserved"); - areAnyTriggers = true; - } - break; - } - - if (!areAnyTriggers) - { - ImGui::BulletText("None"); - } - } - } + isChildOpen = ImGui::TreeNodeEx((currentChild->get_designator() + " (" + currentChild->get_table_id() + " " + std::to_string(currentChild->get_object_id()) + ")").c_str(), childFlags); ImGui::Unindent(); - bool areAnyProperties = false; - ImGui::Text("Properties:"); - ImGui::Indent(); - for (std::uint8_t t = 0; t < 3; t++) + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) { - if (0 != ((1 << t) & currentDPD->get_properties_bitfield())) - { - switch (static_cast(1 << t)) - { - case isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::Settable: - { - ImGui::BulletText("Settable"); - areAnyProperties = true; - } - break; - - case isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::MemberOfDefaultSet: - { - ImGui::BulletText("Member of Default Set"); - areAnyProperties = true; - } - break; - - case isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::ControlSource: - { - ImGui::BulletText("Control Source"); - areAnyProperties = true; - } - break; - - default: - { - ImGui::BulletText("Proprietary or Reserved"); - areAnyProperties = true; - } - break; - } - - if (!areAnyProperties) - { - ImGui::BulletText("None"); - } - } + selectedObjectID = currentChild->get_object_id(); + on_selected_object_changed(currentChild); } - ImGui::Unindent(); + } - // Try and get the presentation - if (0xFFFF != currentDPD->get_device_value_presentation_object_id()) + if (isChildOpen) + { + if (isobus::task_controller_object::ObjectTypes::DeviceProcessData == currentChild->get_object_type()) { - auto lDVP = std::dynamic_pointer_cast(currentObjectPool->get_object_by_id(currentDPD->get_device_value_presentation_object_id())); + auto currentDPD = std::dynamic_pointer_cast(currentChild); - if ((nullptr != lDVP) && - (ImGui::TreeNode(("Presentation: " + lDVP->get_designator() + " (" + lDVP->get_table_id() + " " + std::to_string(lDVP->get_object_id()) + ")").c_str()))) - - { - ImGui::Text("Number of Decimals: %u", lDVP->get_number_of_decimals()); - ImGui::Text("Offset: %d", lDVP->get_offset()); - ImGui::Text("Scale: %f", lDVP->get_scale()); - ImGui::TreePop(); - } + render_device_process_data_components(currentDPD); } - } - else if (isobus::task_controller_object::ObjectTypes::DeviceProperty == currentChild->get_object_type()) - { - auto lpDPT = std::dynamic_pointer_cast(currentChild); - - ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "DDI: %u", lpDPT->get_ddi()); - - ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Value: %d", lpDPT->get_value()); - - // Try and get the presentation - if (0xFFFF != lpDPT->get_device_value_presentation_object_id()) + else if (isobus::task_controller_object::ObjectTypes::DeviceProperty == currentChild->get_object_type()) { - auto lDVP = std::dynamic_pointer_cast(currentObjectPool->get_object_by_id(lpDPT->get_device_value_presentation_object_id())); + auto lpDPT = std::dynamic_pointer_cast(currentChild); - if ((nullptr != lDVP) && - (ImGui::TreeNode(("Presentation: " + lDVP->get_designator() + " (" + lDVP->get_table_id() + " " + std::to_string(lDVP->get_object_id()) + ")").c_str()))) - - { - ImGui::Text("Number of Decimals: %u", lDVP->get_number_of_decimals()); - ImGui::Text("Offset: %d", lDVP->get_offset()); - ImGui::Text("Scale: %f", lDVP->get_scale()); - ImGui::TreePop(); - } + render_device_property_components(lpDPT); } + ImGui::TreePop(); } - ImGui::TreePop(); } } } @@ -575,7 +662,7 @@ void DDOPGeneratorGUI::render_object_tree() if (isOpen) { - ImGui::Text(("Serial Number: " + std::dynamic_pointer_cast(lpObject)->get_serial_number()).c_str()); + ImGui::Text("%s", ("Serial Number: " + std::dynamic_pointer_cast(lpObject)->get_serial_number()).c_str()); // Search for all elements with the device object as their parent and render recursively // Search device elements for all elements that refer to aElementNumbers their parent @@ -657,6 +744,9 @@ void DDOPGeneratorGUI::render_device_settings(std::shared_ptrget_element_number() != elementNumberBuffer) { object->set_element_number(elementNumberBuffer); } + ImGui::BeginDisabled(); + ImGui::InputInt("Object ID", &objectIDBuffer); + if (objectIDBuffer < 0) + { + objectIDBuffer = 0; + } + else if (objectIDBuffer > 0xFFFF) + { + objectIDBuffer = 0xFFFF; + } + + if ((objectIDBuffer != object->get_object_id()) && + (!currentObjectPool->get_object_by_id(objectIDBuffer))) + { + object->set_object_id(objectIDBuffer); + } + else + { + objectIDBuffer = object->get_object_id(); + } + ImGui::EndDisabled(); + ImGui::InputInt("Parent Object ID", &parentObjectBuffer); + if (parentObjectBuffer < 0) + { + parentObjectBuffer = 0xFFFF; + } + if (object->get_parent_object() > 0xFFFF) { parentObjectBuffer = 0xFFFF; @@ -921,22 +1042,53 @@ void DDOPGeneratorGUI::render_device_element_settings(std::shared_ptrget_designator() + "\""; - ImGui::Text(designator.c_str()); + ImGui::Text("%s", designator.c_str()); } -} - -void DDOPGeneratorGUI::render_device_process_data_settings(std::shared_ptr object) -{ - ImGui::InputText("Designator", designatorBuffer, IM_ARRAYSIZE(designatorBuffer)); - auto designator = std::string(designatorBuffer); - if (designator != object->get_designator()) + if (nullptr != currentObjectPool) { - object->set_designator(designator); + auto selectedObject = currentObjectPool->get_object_by_index(addChildComboIndex); + if (nullptr != selectedObject) + { + if (ImGui::BeginCombo("Add Child Object Reference", selectedObject->get_designator().c_str())) + { + for (int n = 0; n < currentObjectPool->size(); n++) + { + const bool is_selected = (addChildComboIndex == n); + selectedObject = currentObjectPool->get_object_by_index(n); + if ((nullptr != selectedObject) && + (selectedObject->get_object_type() != isobus::task_controller_object::ObjectTypes::Device) && + (selectedObject->get_object_type() != isobus::task_controller_object::ObjectTypes::DeviceElement)) + { + if (ImGui::Selectable((selectedObject->get_designator() + " (" + std::to_string(selectedObject->get_object_id()) + ")").c_str(), is_selected)) + { + addChildComboIndex = n; + } + } + + // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) + if (is_selected) + { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + } + + if (ImGui::Button("Add Object")) + { + auto childID = currentObjectPool->get_object_by_index(addChildComboIndex)->get_object_id(); + object->add_reference_to_child_object(childID); + } + } + else + { + addChildComboIndex = 0; + } } } -void DDOPGeneratorGUI::render_device_property_settings(std::shared_ptr object) +void DDOPGeneratorGUI::render_device_process_data_settings(std::shared_ptr object) { ImGui::InputText("Designator", designatorBuffer, IM_ARRAYSIZE(designatorBuffer)); @@ -945,25 +1097,282 @@ void DDOPGeneratorGUI::render_device_property_settings(std::shared_ptrset_designator(designator); } -} -void DDOPGeneratorGUI::render_device_presentation_settings(std::shared_ptr object) -{ - ImGui::InputText("Designator", designatorBuffer, IM_ARRAYSIZE(designatorBuffer)); + ImGui::InputInt("DDI", &ddiBuffer); + if (ddiBuffer < 0) + { + ddiBuffer = 0; + } + else if (ddiBuffer > 0xFFFF) + { + ddiBuffer = 0xFFFF; + } - auto designator = std::string(designatorBuffer); - if (designator != object->get_designator()) + if (ddiBuffer != object->get_ddi()) { - object->set_designator(designator); + object->set_ddi(ddiBuffer); } -} -void DDOPGeneratorGUI::render_current_selected_object_settings(std::shared_ptr object) -{ - if (nullptr != object) + ImGui::BeginDisabled(); + ImGui::InputInt("Object ID", &objectIDBuffer); + if (objectIDBuffer < 0) { - switch (object->get_object_type()) - { + objectIDBuffer = 0; + } + else if (objectIDBuffer > 0xFFFF) + { + objectIDBuffer = 0xFFFF; + } + + if ((objectIDBuffer != object->get_object_id()) && + (!currentObjectPool->get_object_by_id(objectIDBuffer))) + { + object->set_object_id(objectIDBuffer); + } + else + { + objectIDBuffer = object->get_object_id(); + } + ImGui::EndDisabled(); + + ImGui::InputInt("Presentation Object ID", &presentationObjectBuffer); + if (presentationObjectBuffer < 0) + { + presentationObjectBuffer = 0; + } + else if (presentationObjectBuffer > 0xFFFF) + { + presentationObjectBuffer = 0xFFFF; + } + + if (presentationObjectBuffer != object->get_device_value_presentation_object_id()) + { + object->set_device_value_presentation_object_id(presentationObjectBuffer); + } + + ImGui::Text("Properties"); + ImGui::Checkbox("Member of Default Set", &propertiesBitfieldBuffer[0]); + ImGui::Checkbox("Settable", &propertiesBitfieldBuffer[1]); + ImGui::Checkbox("Control Source", &propertiesBitfieldBuffer[2]); + ImGui::SameLine(); + ImGui::TextDisabled("(?)"); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort)) + { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted("Version 4 only, mutually exclusive wth 'Settable'"); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + + // Mutually exclusive bits + if (propertiesBitfieldBuffer[1] && propertiesBitfieldBuffer[2]) + { + propertiesBitfieldBuffer[2] = false; + } + + std::uint8_t propertiesBitfield = static_cast(propertiesBitfieldBuffer[0]) | + (static_cast(propertiesBitfieldBuffer[1]) << 1) | + (static_cast(propertiesBitfieldBuffer[2]) << 2); + if (propertiesBitfield != object->get_properties_bitfield()) + { + object->set_properties_bitfield(propertiesBitfield); + } + + ImGui::Text("Trigger Settings"); + ImGui::Checkbox("Time Interval", &triggerBitfieldBuffer[0]); + ImGui::Checkbox("Distance Interval", &triggerBitfieldBuffer[1]); + ImGui::Checkbox("Threshold Limits", &triggerBitfieldBuffer[2]); + ImGui::Checkbox("On Change", &triggerBitfieldBuffer[3]); + ImGui::Checkbox("Total", &triggerBitfieldBuffer[4]); + + std::uint8_t triggerBitfield = static_cast(triggerBitfieldBuffer[0]) | + (static_cast(triggerBitfieldBuffer[1]) << 1) | + (static_cast(triggerBitfieldBuffer[2]) << 2) | + (static_cast(triggerBitfieldBuffer[3]) << 3) | + (static_cast(triggerBitfieldBuffer[4]) << 4); + if (triggerBitfield != object->get_trigger_methods_bitfield()) + { + object->set_trigger_methods_bitfield(triggerBitfield); + } +} + +void DDOPGeneratorGUI::render_device_property_settings(std::shared_ptr object) +{ + ImGui::InputText("Designator", designatorBuffer, IM_ARRAYSIZE(designatorBuffer)); + + auto designator = std::string(designatorBuffer); + if (designator != object->get_designator()) + { + object->set_designator(designator); + } + + ImGui::InputInt("DDI", &ddiBuffer); + if (ddiBuffer < 0) + { + ddiBuffer = 0; + } + else if (ddiBuffer > 0xFFFF) + { + ddiBuffer = 0xFFFF; + } + + if (ddiBuffer != object->get_ddi()) + { + object->set_ddi(ddiBuffer); + } + + ImGui::InputInt("Value", &valueBuffer); + if (valueBuffer != object->get_value()) + { + object->set_value(valueBuffer); + } + + ImGui::InputInt("Presentation Object ID", &presentationObjectBuffer); + if (presentationObjectBuffer < 0) + { + presentationObjectBuffer = 0; + } + else if (presentationObjectBuffer > 0xFFFF) + { + presentationObjectBuffer = 0xFFFF; + } + + if (presentationObjectBuffer != object->get_device_value_presentation_object_id()) + { + object->set_device_value_presentation_object_id(presentationObjectBuffer); + } + + ImGui::BeginDisabled(); + ImGui::InputInt("Object ID", &objectIDBuffer); + if (objectIDBuffer < 0) + { + objectIDBuffer = 0; + } + else if (objectIDBuffer > 0xFFFF) + { + objectIDBuffer = 0xFFFF; + } + + if ((objectIDBuffer != object->get_object_id()) && + (!currentObjectPool->get_object_by_id(objectIDBuffer))) + { + object->set_object_id(objectIDBuffer); + } + else + { + objectIDBuffer = object->get_object_id(); + } + ImGui::EndDisabled(); +} + +void DDOPGeneratorGUI::render_device_presentation_settings(std::shared_ptr object) +{ + ImGui::InputText("Designator", designatorBuffer, IM_ARRAYSIZE(designatorBuffer)); + + auto designator = std::string(designatorBuffer); + if (designator != object->get_designator()) + { + object->set_designator(designator); + } + + ImGui::InputFloat("Scale", &scaleBuffer, 0.0f, 0.0f, "%.9f"); + if (scaleBuffer > 100000000.0f) + { + scaleBuffer = 100000000.0f; + } + else if (scaleBuffer < 0.000000001f) + { + scaleBuffer = 0.000000001f; + } + + if (object->get_scale() != scaleBuffer) + { + object->set_scale(scaleBuffer); + } + + ImGui::InputInt("Offset", &offsetBuffer); + if (object->get_offset() != offsetBuffer) + { + object->set_offset(offsetBuffer); + } + + ImGui::InputInt("Number Decimals", &numberDecimalsBuffer); + if (numberDecimalsBuffer > 7) + { + numberDecimalsBuffer = 7; + } + + if (object->get_number_of_decimals() != numberDecimalsBuffer) + { + object->set_number_of_decimals(numberDecimalsBuffer); + } + + ImGui::BeginDisabled(); + ImGui::InputInt("Object ID", &objectIDBuffer); + if (objectIDBuffer < 0) + { + objectIDBuffer = 0; + } + else if (objectIDBuffer > 0xFFFF) + { + objectIDBuffer = 0xFFFF; + } + + if ((objectIDBuffer != object->get_object_id()) && + (!currentObjectPool->get_object_by_id(objectIDBuffer))) + { + object->set_object_id(objectIDBuffer); + } + else + { + objectIDBuffer = object->get_object_id(); + } + ImGui::EndDisabled(); +} + +void DDOPGeneratorGUI::render_object_components(std::shared_ptr object) +{ + if (nullptr != object) + { + switch (object->get_object_type()) + { + case isobus::task_controller_object::ObjectTypes::DeviceElement: + { + render_device_element_components(std::dynamic_pointer_cast(object)); + } + break; + + case isobus::task_controller_object::ObjectTypes::DeviceProcessData: + { + render_device_process_data_components(std::dynamic_pointer_cast(object)); + } + break; + + case isobus::task_controller_object::ObjectTypes::DeviceProperty: + { + render_device_property_components(std::dynamic_pointer_cast(object)); + } + break; + + case isobus::task_controller_object::ObjectTypes::DeviceValuePresentation: + { + render_device_presentation_components(std::dynamic_pointer_cast(object)); + } + break; + + default: + break; + } + } +} + +void DDOPGeneratorGUI::render_current_selected_object_settings(std::shared_ptr object) +{ + if (nullptr != object) + { + switch (object->get_object_type()) + { case isobus::task_controller_object::ObjectTypes::Device: { render_device_settings(std::dynamic_pointer_cast(object)); @@ -997,10 +1406,208 @@ void DDOPGeneratorGUI::render_current_selected_object_settings(std::shared_ptr object) +{ + ImGui::Text("%s", ("Element Number: " + std::to_string(object->get_element_number())).c_str()); + ImGui::Text("%s", ("Type: " + get_element_type_string(object->get_type())).c_str()); +} + +void DDOPGeneratorGUI::render_device_process_data_components(std::shared_ptr object) +{ + ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "DDI: %u", object->get_ddi()); + + bool areAnyTriggers = false; + ImGui::Text("Triggers:"); + ImGui::Indent(); + for (std::uint8_t t = 0; t < 5; t++) + { + if (0 != ((1 << t) & object->get_trigger_methods_bitfield())) + { + switch (static_cast(1 << t)) + { + case isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::DistanceInterval: + { + ImGui::BulletText("Distance Interval"); + areAnyTriggers = true; + } + break; + + case isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::OnChange: + { + ImGui::BulletText("On Change"); + areAnyTriggers = true; + } + break; + + case isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::ThresholdLimits: + { + ImGui::BulletText("Threshold Limits"); + areAnyTriggers = true; + } + break; + + case isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::TimeInterval: + { + ImGui::BulletText("Time Interval"); + areAnyTriggers = true; + } + break; + + case isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::Total: + { + ImGui::BulletText("Total"); + areAnyTriggers = true; + } + break; + + default: + { + ImGui::BulletText("Proprietary or Reserved"); + areAnyTriggers = true; + } + break; + } + + if (!areAnyTriggers) + { + ImGui::BulletText("None"); + } + } + } + ImGui::Unindent(); + + bool areAnyProperties = false; + ImGui::Text("Properties:"); + ImGui::Indent(); + for (std::uint8_t t = 0; t < 3; t++) + { + if (0 != ((1 << t) & object->get_properties_bitfield())) + { + switch (static_cast(1 << t)) + { + case isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::Settable: + { + ImGui::BulletText("Settable"); + areAnyProperties = true; + } + break; + + case isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::MemberOfDefaultSet: + { + ImGui::BulletText("Member of Default Set"); + areAnyProperties = true; + } + break; + + case isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::ControlSource: + { + ImGui::BulletText("Control Source"); + areAnyProperties = true; + } + break; + + default: + { + ImGui::BulletText("Proprietary or Reserved"); + areAnyProperties = true; + } + break; + } + + if (!areAnyProperties) + { + ImGui::BulletText("None"); + } + } + } + ImGui::Unindent(); + + // Try and get the presentation + if (0xFFFF != object->get_device_value_presentation_object_id()) + { + auto currentPresentation = std::dynamic_pointer_cast(currentObjectPool->get_object_by_id(object->get_device_value_presentation_object_id())); + + if (nullptr != currentPresentation) + { + ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth; + if (selectedObjectID == currentPresentation->get_object_id()) + { + flags |= ImGuiTreeNodeFlags_Selected; + } + + ImGui::Indent(); + bool isOpen = ImGui::TreeNodeEx(("Presentation: " + currentPresentation->get_designator() + " (" + currentPresentation->get_table_id() + " " + std::to_string(currentPresentation->get_object_id()) + ")").c_str(), flags); + ImGui::Unindent(); + + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) + { + selectedObjectID = currentPresentation->get_object_id(); + on_selected_object_changed(currentPresentation); + } + + if (isOpen) + { + ImGui::Indent(); + ImGui::Text("Number of Decimals: %u", currentPresentation->get_number_of_decimals()); + ImGui::Text("Offset: %d", currentPresentation->get_offset()); + ImGui::Text("Scale: %f", currentPresentation->get_scale()); + ImGui::Unindent(); + ImGui::TreePop(); + } + } + } +} + +void DDOPGeneratorGUI::render_device_property_components(std::shared_ptr object) +{ + ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "DDI: %u", object->get_ddi()); + ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Value: %d", object->get_value()); + + // Try and get the presentation + if (0xFFFF != object->get_device_value_presentation_object_id()) + { + auto currentDVP = std::dynamic_pointer_cast(currentObjectPool->get_object_by_id(object->get_device_value_presentation_object_id())); + + if (nullptr != currentDVP) + { + ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth; + if (selectedObjectID == currentDVP->get_object_id()) + { + flags |= ImGuiTreeNodeFlags_Selected; + } + + ImGui::Indent(); + bool isOpen = ImGui::TreeNodeEx(("Presentation: " + currentDVP->get_designator() + " (" + currentDVP->get_table_id() + " " + std::to_string(currentDVP->get_object_id()) + ")").c_str(), flags); + ImGui::Unindent(); + + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) + { + selectedObjectID = currentDVP->get_object_id(); + on_selected_object_changed(currentDVP); + } + + if (isOpen) + { + render_device_presentation_components(currentDVP); + ImGui::TreePop(); + } + } + } +} + +void DDOPGeneratorGUI::render_device_presentation_components(std::shared_ptr object) +{ + ImGui::Text("Number of Decimals: %u", object->get_number_of_decimals()); + ImGui::Text("Offset: %d", object->get_offset()); + ImGui::Text("Scale: %f", object->get_scale()); +} + void DDOPGeneratorGUI::on_selected_object_changed(std::shared_ptr newObject) { memset(designatorBuffer, 0, sizeof(designatorBuffer)); memcpy(designatorBuffer, newObject->get_designator().c_str(), newObject->get_designator().length() <= 128 ? newObject->get_designator().length() : 128); + objectIDBuffer = newObject->get_object_id(); + addChildComboIndex = 0; switch (newObject->get_object_type()) { @@ -1031,6 +1638,8 @@ void DDOPGeneratorGUI::on_selected_object_changed(std::shared_ptr((localization.at(2) >> 4) & 0x03); decimalSymbol = static_cast((localization.at(2) >> 6) & 0x03); dateFormat = static_cast(localization.at(3)); @@ -1052,6 +1661,43 @@ void DDOPGeneratorGUI::on_selected_object_changed(std::shared_ptrget_parent_object(); } break; + + case isobus::task_controller_object::ObjectTypes::DeviceProcessData: + { + auto object = std::dynamic_pointer_cast(newObject); + presentationObjectBuffer = object->get_device_value_presentation_object_id(); + ddiBuffer = object->get_ddi(); + + for (std::uint8_t i = 0; i < 8; i++) + { + propertiesBitfieldBuffer[i] = false; + triggerBitfieldBuffer[i] = false; + propertiesBitfieldBuffer[i] = static_cast((object->get_properties_bitfield() >> i) & 0x01); + triggerBitfieldBuffer[i] = static_cast((object->get_trigger_methods_bitfield() >> i) & 0x01); + } + } + break; + + case isobus::task_controller_object::ObjectTypes::DeviceProperty: + { + auto object = std::dynamic_pointer_cast(newObject); + ddiBuffer = object->get_ddi(); + presentationObjectBuffer = object->get_device_value_presentation_object_id(); + valueBuffer = object->get_value(); + } + break; + + case isobus::task_controller_object::ObjectTypes::DeviceValuePresentation: + { + auto object = std::dynamic_pointer_cast(newObject); + numberDecimalsBuffer = object->get_number_of_decimals(); + offsetBuffer = object->get_offset(); + scaleBuffer = object->get_scale(); + } + break; + + default: + break; } } @@ -1155,12 +1801,64 @@ void DDOPGeneratorGUI::render_save() { bool shouldShowSaveFailed = false; bool shouldShowSaveSucceeded = false; + if (ImGui::BeginPopupModal("##Save Modal", NULL, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::Text("Are you sure you want to overwrite your DDOP file?"); + ImGui::Separator(); + if (ImGui::Button("Save", ImVec2(120, 0))) + { + ImGui::CloseCurrentPopup(); + std::vector binaryDDOP; + logger.logHistory.clear(); + auto serializationSuccess = currentObjectPool->generate_binary_object_pool(binaryDDOP); + + if (serializationSuccess) + { + std::ofstream outFile(lastFileName, std::ios_base::trunc | std::ios_base::binary); + + if (outFile) + { + outFile.write(reinterpret_cast(binaryDDOP.data()), binaryDDOP.size()); + shouldShowSaveSucceeded = true; + } + else + { + shouldShowSaveFailed = true; + } + } + else + { + shouldShowSaveFailed = true; + } + } + ImGui::SameLine(); + if (ImGui::Button("Cancel", ImVec2(120, 0))) + { + ImGui::CloseCurrentPopup(); + saveModal = false; + } + ImGui::EndPopup(); + } if (ImGui::BeginPopupModal("##Save As Modal", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Text("Enter file name"); ImGui::Separator(); ImGui::InputText("File Name", filePathBuffer, IM_ARRAYSIZE(filePathBuffer)); + const char *versions[] = { "Version 3", "Version 4" }; + ImGui::ListBox("TC Version", &versionIndex, versions, IM_ARRAYSIZE(versions), 2); + + if (nullptr != currentObjectPool) + { + if (0 == versionIndex) + { + currentObjectPool->set_task_controller_compatibility_level(3); + } + else + { + currentObjectPool->set_task_controller_compatibility_level(4); + } + } ImGui::SetItemDefaultFocus(); if (ImGui::Button("Save", ImVec2(120, 0))) @@ -1203,6 +1901,7 @@ void DDOPGeneratorGUI::render_save() if (ImGui::Button("Cancel", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); + saveAsModal = false; } ImGui::EndPopup(); } @@ -1223,6 +1922,8 @@ void DDOPGeneratorGUI::render_save() if (ImGui::Button("OK", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); + saveAsModal = false; + saveModal = false; } ImGui::EndPopup(); } @@ -1233,17 +1934,55 @@ void DDOPGeneratorGUI::render_save() ImGui::Separator(); for (auto &logString : logger.logHistory) { - ImGui::Text(logString.logText.c_str()); + ImGui::Text("%s", logString.logText.c_str()); } if (ImGui::Button("OK", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); + saveAsModal = false; + saveModal = false; } ImGui::EndPopup(); } } +void DDOPGeneratorGUI::render_all_objects() +{ + if (ImGui::TreeNode("All Objects")) + { + for (std::uint32_t j = 0; j < currentObjectPool->size(); j++) + { + auto currentObject = currentObjectPool->get_object_by_index(j); + + if (nullptr != currentObject) + { + ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth; + + if (selectedObjectID == currentObject->get_object_id()) + { + base_flags |= ImGuiTreeNodeFlags_Selected; + } + + bool isOpen = ImGui::TreeNodeEx((currentObject->get_designator() + "(" + currentObject->get_table_id() + " " + std::to_string(currentObject->get_object_id()) + ")").c_str(), base_flags); + + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) + { + selectedObjectID = currentObject->get_object_id(); + on_selected_object_changed(currentObject); + } + + if (isOpen) + { + render_object_components(currentObject); + ImGui::TreePop(); + } + } + } + ImGui::TreePop(); + } +} + const std::array DDOPGeneratorGUI::generate_localization_label() { std::array retVal = { 0 }; @@ -1272,3 +2011,25 @@ const std::array DDOPGeneratorGUI::generate_localization_label( retVal[6] = 0xFF; return retVal; } + +std::uint16_t DDOPGeneratorGUI::get_first_unused_id() const +{ + std::uint16_t retVal = 0; + + if (nullptr != currentObjectPool) + { + for (std::uint16_t i = 0; i < 0xFFFF; i++) + { + if (nullptr == currentObjectPool->get_object_by_id(i)) + { + retVal = i; + break; + } + } + } + else + { + retVal = 0xFFFF; + } + return retVal; +} diff --git a/submodules/agisostack b/submodules/agisostack index 78f62fa..0e43ba2 160000 --- a/submodules/agisostack +++ b/submodules/agisostack @@ -1 +1 @@ -Subproject commit 78f62faf9e1ffaf6e7d1631c8d613b4a40437816 +Subproject commit 0e43ba29137a7292c25f0e9623c836e72bf622c7