diff --git a/src/api/project.h b/src/api/project.h new file mode 100644 index 0000000..92ab05f --- /dev/null +++ b/src/api/project.h @@ -0,0 +1,80 @@ +#pragma once + +#include +#include + +namespace api::project +{ +//-------------------------------------------------------------- +enum class OperationType : uint8_t +{ + Create = 0, + Open, + Save, + SaveAs, + Close, +}; +//-------------------------------------------------------------- + +/* --- */ + +//-------------------------------------------------------------- +// Post event structs +//-------------------------------------------------------------- +struct ProjectOperationEvent : dBus::api::DefaultData +{ + ProjectOperationEvent() = default; // Default constructor + virtual ~ProjectOperationEvent() = default; // Default destructor + ProjectOperationEvent(const ProjectOperationEvent &obj) = default; // Copy constructor + ProjectOperationEvent(ProjectOperationEvent &&obj) noexcept = default; // Move constructor + ProjectOperationEvent &operator=(const ProjectOperationEvent &obj) = default; // Copy assignment operator + ProjectOperationEvent &operator=(ProjectOperationEvent &&obj) noexcept = default; // Move assignment operator + + OperationType operationType; + std::filesystem::path filePath; + + [[nodiscard]] std::string toString() const override + { + if (operationType == OperationType::Open || operationType == OperationType::SaveAs) + return std::format("ProjectOperationEvent: operation={}, filePath={}", getOperationString(), filePath.string()); + else + return std::format("ProjectOperationEvent: operation={}", getOperationString()); + } + + private: + [[nodiscard]] std::string getOperationString() const + { + switch (operationType) + { + using enum OperationType; + case Create: + return "Create"; + case Open: + return "Open"; + case Save: + return "Save"; + case SaveAs: + return "SaveAs"; + case Close: + return "Close"; + } + throw std::runtime_error("Invalid project operation"); + } +}; +//-------------------------------------------------------------- + +/* --- */ + +//-------------------------------------------------------------- +// Post event helpers +//-------------------------------------------------------------- +inline dBus::api::PostReturnStatus postProjectOperationRequest(dBus::Bus &bus, const OperationType &operationType, const std::filesystem::path &filePath = "") +{ + auto eventData = ProjectOperationEvent(); + eventData.operationType = std::move(operationType); + eventData.filePath = std::move(filePath); + + return dBus::api::requestData(bus, eventData.getID(), eventData, dBus::MessageCategory::UI); +} +//-------------------------------------------------------------- +} // namespace api::project diff --git a/src/api/requirement.h b/src/api/requirement.h index 27099bc..887254d 100644 --- a/src/api/requirement.h +++ b/src/api/requirement.h @@ -1,20 +1,11 @@ #pragma once #include -#include #include namespace api::requirement { //-------------------------------------------------------------- -enum class FileOperation : uint8_t -{ - Create = 0, - Open, - Save, - SaveAs -}; -//-------------------------------------------------------------- struct Requirement { std::string uuid; @@ -28,7 +19,7 @@ struct Requirement /* Default constructor */ Requirement() { - uuid = sdi_toolBox::generic::UuidGenerator::uniqid(); // Generate a unique identifier for the requirement + uuid = sdi_toolBox::generic::UuidGenerator::uuid_v4(); // Generate a unique UUID for the requirement } }; //-------------------------------------------------------------- @@ -38,41 +29,6 @@ struct Requirement //-------------------------------------------------------------- // Post event structs //-------------------------------------------------------------- -struct FileOperationEvent : dBus::api::DefaultData -{ - virtual ~FileOperationEvent() = default; // Default destructor - - FileOperation fileOperation; - std::filesystem::path filePath; - - [[nodiscard]] std::string toString() const override - { - if (fileOperation == FileOperation::Open || fileOperation == FileOperation::SaveAs) - return std::format("FileOperationEvent: operation={}, filePath={}", getOperationString(), filePath.string()); - else - return std::format("FileOperationEvent: operation={}", getOperationString()); - } - - private: - [[nodiscard]] std::string getOperationString() const - { - switch (fileOperation) - { - using enum FileOperation; - case Create: - return "Create"; - case Open: - return "Open"; - case Save: - return "Save"; - case SaveAs: - return "SaveAs"; - default: - return ""; - } - } -}; -//-------------------------------------------------------------- struct RequirementEvent : dBus::api::DefaultData { virtual ~RequirementEvent() = default; // Default destructor @@ -95,15 +51,6 @@ struct RequirementEvent : dBus::api::DefaultData(bus, eventData.getID(), eventData, dBus::MessageCategory::UI); -} -//-------------------------------------------------------------- /* Post a requirement event to the bus. This function will post a * RequirementEvent containing the provided requirement data to the * bus, and return a PostReturnStatus indicating whether the post was diff --git a/src/core/coreManager.cpp b/src/core/coreManager.cpp index 06fc3d8..0353f98 100644 --- a/src/core/coreManager.cpp +++ b/src/core/coreManager.cpp @@ -28,15 +28,15 @@ void CoreManager::init() m_bus = make_unique(); // Initialize other subsystems (e.g., logger, etc.) - m_logger = make_unique(*m_bus); - m_requirementManager = make_unique(*m_bus); + m_logger = make_unique(*m_bus); + m_projectManager = make_unique(*m_bus); } //-------------------------------------------------------------- /* Release resources used by the core manager (e.g., clean up the bus, etc.) */ void CoreManager::release() { // Clean up other subsystems (e.g., logger, etc.) - m_requirementManager.reset(); + m_projectManager.reset(); m_logger.reset(); // Clean up the application bus diff --git a/src/core/coreManager.h b/src/core/coreManager.h index b4bbf00..91d78a6 100644 --- a/src/core/coreManager.h +++ b/src/core/coreManager.h @@ -1,7 +1,7 @@ #pragma once #include "logger/logger.h" -#include "requirementManager/requirementManager.h" +#include "projectManager/projectManager.h" #include #include @@ -24,8 +24,8 @@ class CoreManager protected: std::unique_ptr m_bus; // Application data bus - std::unique_ptr m_logger; // Application logger system - std::unique_ptr m_requirementManager; // Requirement manager system + std::unique_ptr m_logger; // Application logger system + std::unique_ptr m_projectManager; // Requirement manager system private: void init(); // Initialize the core manager (e.g., set up the bus, etc.) diff --git a/src/core/logger/logger.h b/src/core/logger/logger.h index 6ec620a..4b6b856 100644 --- a/src/core/logger/logger.h +++ b/src/core/logger/logger.h @@ -11,7 +11,7 @@ namespace core::logger //-------------------------------------------------------------- class Logger : public dBus::Node { - static constexpr auto LOG_PRINT_INTERVAL = std::chrono::seconds{ 1 }; + static constexpr auto LOG_PRINT_INTERVAL = std::chrono::milliseconds{ 500 }; public: Logger() = delete; // Default constructor diff --git a/src/core/projectManager/projectData.cpp b/src/core/projectManager/projectData.cpp new file mode 100644 index 0000000..371b5fe --- /dev/null +++ b/src/core/projectManager/projectData.cpp @@ -0,0 +1,11 @@ +#include "projectData.h" + +using namespace std; +using namespace core::project; +//-------------------------------------------------------------- +/* Get the root requirement */ +ProjectData::RequirementPtr ProjectData::getRootRequirement() +{ + return m_rootRequirement; +} +//-------------------------------------------------------------- \ No newline at end of file diff --git a/src/core/projectManager/projectData.h b/src/core/projectManager/projectData.h new file mode 100644 index 0000000..99ec345 --- /dev/null +++ b/src/core/projectManager/projectData.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +namespace core::project +{ +//-------------------------------------------------------------- +class ProjectData +{ + using RequirementPtr = std::shared_ptr; + + public: + ProjectData() = default; // Default constructor + virtual ~ProjectData() = default; // Default destructor + ProjectData(const ProjectData &obj) = delete; // Copy constructor + ProjectData(ProjectData &&obj) noexcept = delete; // Move constructor + ProjectData &operator=(const ProjectData &obj) = delete; // Copy assignment operator + ProjectData &operator=(ProjectData &&obj) noexcept = delete; // Move assignment operator + + RequirementPtr getRootRequirement(); // Get the root requirement + + protected: + RequirementPtr m_rootRequirement; // Root requirement of the current project + + private: +}; +//-------------------------------------------------------------- +} // namespace core::project diff --git a/src/core/projectManager/projectManager.cpp b/src/core/projectManager/projectManager.cpp new file mode 100644 index 0000000..d09930f --- /dev/null +++ b/src/core/projectManager/projectManager.cpp @@ -0,0 +1,171 @@ +#include "projectManager.h" + +#include "projectData.h" + +using namespace std; +using namespace core::project; +//-------------------------------------------------------------- +/* Constructor */ +ProjectManager::ProjectManager(dBus::Bus &bus) + : Node(bus) +{ + // Initialize the bus listener thread + runBusListener(); +} +//-------------------------------------------------------------- +/* Default destructor */ +ProjectManager::~ProjectManager() +{ + // Stop the bus listener thread if it's running + stopBusListener(); +} +//-------------------------------------------------------------- +/* Open a project file and load the requirements */ +void ProjectManager::openFile(const std::filesystem::path &filePath) +{ +} +//-------------------------------------------------------------- +/* Save the current requirements to a project file */ +void ProjectManager::saveFile(const std::filesystem::path &filePath) +{ +} +//-------------------------------------------------------------- +/* Run the bus listener thread */ +void ProjectManager::runBusListener() +{ + // Subscribe to the bus for messages related to log entries + subscribe(dBus::makeID("project.file.operation")); + + /* Start the bus listener thread to process incoming messages related to requirements. + * The thread will continuously wait for messages and process them until a stop is requested. */ + // clang-format off + m_busListenerThread = std::jthread([this](const std::stop_token &stopToken) + { + while (!stopToken.stop_requested()) + { + // Wait for a message to be received + syncWaitForMessage(); + + // Process all received messages + while (getMessageCount() > 0) + { + auto message = popNextMessage(); + if (message) + processMessageBus(message); + } + } + }); + // clang-format on +} +//-------------------------------------------------------------- +/* Stop the bus listener thread */ +void ProjectManager::stopBusListener() +{ + // Stop event thread loop + m_busListenerThread.request_stop(); + + // Wake up event thread if waiting + notifyMessageQueue(); + + /* No need to join the thread explicitly as std::jthread will + * automatically join in its destructor. + * Subscribe to the bus will be automatically cleaned up in the + * Node destructor. */ +} +//-------------------------------------------------------------- +/* Process a message received from the bus */ +void ProjectManager::processMessageBus(const Message_ptr &message) +{ + const auto messageType = message->getMessageTypeID(); + switch (messageType) + { + case dBus::makeID("project.file.operation"): + { + // Process project operation request + on_projectOperationEvent(message->as>()); + break; + } + } +} +//-------------------------------------------------------------- +/* Process a file operation event */ +void ProjectManager::on_projectOperationEvent(const ProjectOperationMessage_ptr &message) +{ + // Sanity check + if (!message) + return; + + switch (message->value.operationType) + { + using enum api::project::OperationType; + case Create: + on_projectCreateFileEvent(message); + break; + case Open: + on_projectOpenFileEvent(message); + break; + case Save: + case SaveAs: + on_projectSaveFileEvent(message); + break; + case Close: + on_projectCloseFileEvent(message); + break; + } +} +//-------------------------------------------------------------- +/* Process a file creation event */ +void ProjectManager::on_projectCreateFileEvent(const ProjectOperationMessage_ptr &message) +{ + m_projectData = make_shared(); + + message->responsePromise.set_value(); +} +//-------------------------------------------------------------- +/* Process a file open event */ +void ProjectManager::on_projectOpenFileEvent(const ProjectOperationMessage_ptr &message) +{ + // message->responsePromise.set_exception(std::make_exception_ptr(std::runtime_error("Open operation not implemented"))); + try + { + openFile(message->value.filePath); + } + catch (const std::exception &e) + { + message->responsePromise.set_exception(std::make_exception_ptr(e)); + } + message->responsePromise.set_value(); +} +//-------------------------------------------------------------- +/* Process a file save event */ +void ProjectManager::on_projectSaveFileEvent(const ProjectOperationMessage_ptr &message) +{ + try + { + if (message->value.operationType == api::project::OperationType::SaveAs) + { + if (message->value.filePath.empty()) + throw std::runtime_error("File path is empty for SaveAs operation"); + saveFile(message->value.filePath); + } + else if (message->value.operationType == api::project::OperationType::Save) + { + if (m_filePath.empty()) + throw std::runtime_error("File path is empty for Save operation"); + saveFile(); + } + } + catch (const std::exception &e) + { + message->responsePromise.set_exception(std::make_exception_ptr(e)); + } + message->responsePromise.set_value(); +} +//-------------------------------------------------------------- +/* Process a file close event */ +void ProjectManager::on_projectCloseFileEvent(const ProjectOperationMessage_ptr &message) +{ + m_projectData = {}; + message->responsePromise.set_value(); +} +//-------------------------------------------------------------- diff --git a/src/core/projectManager/projectManager.h b/src/core/projectManager/projectManager.h new file mode 100644 index 0000000..a9eb5e3 --- /dev/null +++ b/src/core/projectManager/projectManager.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace core::project +{ +class ProjectData; +//-------------------------------------------------------------- +class ProjectManager : public dBus::Node +{ + using ProjectPtr = std::shared_ptr; + + public: + ProjectManager() = delete; // Default constructor + virtual ~ProjectManager(); // Default destructor + ProjectManager(const ProjectManager &obj) = delete; // Copy constructor + ProjectManager(ProjectManager &&obj) noexcept = delete; // Move constructor + ProjectManager &operator=(const ProjectManager &obj) = delete; // Copy assignment operator + ProjectManager &operator=(ProjectManager &&obj) noexcept = delete; // Move assignment operator + + explicit ProjectManager(dBus::Bus &bus); // Constructor + + protected: + std::filesystem::path m_filePath; // Path of the currently opened project file + + ProjectPtr m_projectData; // Current project data, including the root requirement and all its sub-requirements + + private: + void openFile(const std::filesystem::path &filePath); // Open a project file and load the requirements + void saveFile(const std::filesystem::path &filePath = ""); // Save the current requirements to a project file + + // dBus management + using Message_ptr = std::shared_ptr; + void runBusListener(); // Run the bus listener thread + void stopBusListener(); // Stop the bus listener thread + void processMessageBus(const Message_ptr &message); // Process a message received from the bus + std::jthread m_busListenerThread; // Thread for listening to bus messages related to requirements + + // Events processing + using ProjectOperationMessage_ptr = std::shared_ptr>; + void on_projectOperationEvent(const ProjectOperationMessage_ptr &message); // Process a file operation event + void on_projectCreateFileEvent(const ProjectOperationMessage_ptr &message); // Process a file creation event + void on_projectOpenFileEvent(const ProjectOperationMessage_ptr &message); // Process a file opening event + void on_projectSaveFileEvent(const ProjectOperationMessage_ptr &message); // Process a file saving event + void on_projectCloseFileEvent(const ProjectOperationMessage_ptr &message); // Process a file closing event +}; +//-------------------------------------------------------------- +} // namespace core::project diff --git a/src/core/requirementManager/requirementManager.cpp b/src/core/requirementManager/requirementManager.cpp deleted file mode 100644 index c5921d9..0000000 --- a/src/core/requirementManager/requirementManager.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include "requirementManager.h" - -using namespace std; -using namespace core::requirementManager; -//-------------------------------------------------------------- -/* Constructor */ -RequirementManager::RequirementManager(dBus::Bus &bus) - : Node(bus) -{ - // Initialize the bus listener thread - runBusListener(); -} -//-------------------------------------------------------------- -/* Default destructor */ -RequirementManager::~RequirementManager() -{ - // Stop the bus listener thread if it's running - stopBusListener(); -} -//-------------------------------------------------------------- -/* Get the root requirement */ -api::requirement::Requirement &RequirementManager::getRootRequirement() -{ - return m_rootRequirement; -} -//-------------------------------------------------------------- -/* Run the bus listener thread */ -void RequirementManager::runBusListener() -{ - // Subscribe to the bus for messages related to log entries - subscribe(dBus::makeID("requirement.file.operation")); - - /* Start the bus listener thread to process incoming messages related to requirements. - * The thread will continuously wait for messages and process them until a stop is requested. */ - // clang-format off - m_busListenerThread = std::jthread([this](const std::stop_token &stopToken) - { - while (!stopToken.stop_requested()) - { - // Wait for a message to be received - syncWaitForMessage(); - - // Process all received messages - while (getMessageCount() > 0) - { - auto message = popNextMessage(); - if (message) - processMessageBus(message); - } - } - }); - // clang-format on -} -//-------------------------------------------------------------- -/* Stop the bus listener thread */ -void RequirementManager::stopBusListener() -{ - // Stop event thread loop - m_busListenerThread.request_stop(); - - // Wake up event thread if waiting - notifyMessageQueue(); - - /* No need to join the thread explicitly as std::jthread will - * automatically join in its destructor. - * Subscribe to the bus will be automatically cleaned up in the - * Node destructor. */ -} -//-------------------------------------------------------------- -/* Process a message received from the bus */ -void RequirementManager::processMessageBus(const Message_ptr &message) -{ - const auto messageType = message->getMessageTypeID(); - switch (messageType) - { - case dBus::makeID("requirement.file.operation"): - { - // Process file operation request - const auto requestMessage = message->as>(); - if (!requestMessage) - break; - - requestMessage->responsePromise.set_exception(std::make_exception_ptr(std::runtime_error("Failed to process file operation request"))); - - break; - } - } -} -//-------------------------------------------------------------- diff --git a/src/core/requirementManager/requirementManager.h b/src/core/requirementManager/requirementManager.h deleted file mode 100644 index 12c3bbb..0000000 --- a/src/core/requirementManager/requirementManager.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace core::requirementManager -{ -//-------------------------------------------------------------- -class RequirementManager : public dBus::Node -{ - public: - RequirementManager() = delete; // Default constructor - virtual ~RequirementManager(); // Default destructor - RequirementManager(const RequirementManager &obj) = delete; // Copy constructor - RequirementManager(RequirementManager &&obj) noexcept = delete; // Move constructor - RequirementManager &operator=(const RequirementManager &obj) = delete; // Copy assignment operator - RequirementManager &operator=(RequirementManager &&obj) noexcept = delete; // Move assignment operator - - explicit RequirementManager(dBus::Bus &bus); // Constructor - - protected: - // dBus::Bus &m_bus; // Reference to the application bus - - api::requirement::Requirement m_rootRequirement; - - private: - api::requirement::Requirement &getRootRequirement(); // Get the root requirement - - // dBus management - using Message_ptr = std::shared_ptr; - void runBusListener(); // Run the bus listener thread - void stopBusListener(); // Stop the bus listener thread - void processMessageBus(const Message_ptr &message); // Process a message received from the bus - std::jthread m_busListenerThread; // Thread for listening to bus messages related to requirements -}; -//-------------------------------------------------------------- -} // namespace core::requirementManager diff --git a/src/dBus/api/api.h b/src/dBus/api/api.h index 945397a..2bd1187 100644 --- a/src/dBus/api/api.h +++ b/src/dBus/api/api.h @@ -212,7 +212,8 @@ std::expected requestData(Bus catch (const std::exception &e) { ::api::log::postLogError(bus, std::format("Exception in requestData for message type: 0x{:08x}: {}", msg->getMessageTypeID(), e.what())); - return std::unexpected(ReturnStatus::Exception); + // return std::unexpected(ReturnStatus::Exception); + throw; } } //-------------------------------------------------------------- diff --git a/src/dBus/node.cpp b/src/dBus/node.cpp index 825c9cd..eabf15c 100644 --- a/src/dBus/node.cpp +++ b/src/dBus/node.cpp @@ -26,7 +26,7 @@ void Node::subscribe(const HashID &eventType) m_bus.subscribe(eventType, this); } //-------------------------------------------------------------- -/* Unsubscribe from a specific message type */ +/* Unsubscribe from a specific message type */ void Node::unsubscribe(const HashID &eventType) { m_bus.unsubscribe(eventType, this);