Optimizing project management and its requirements

This commit is contained in:
Sylvain Schneider
2026-03-12 18:56:19 +01:00
parent f25c5789ea
commit e64921702b
13 changed files with 354 additions and 190 deletions

80
src/api/project.h Normal file
View File

@@ -0,0 +1,80 @@
#pragma once
#include <dbus/api/api.h>
#include <filesystem>
namespace api::project
{
//--------------------------------------------------------------
enum class OperationType : uint8_t
{
Create = 0,
Open,
Save,
SaveAs,
Close,
};
//--------------------------------------------------------------
/* --- */
//--------------------------------------------------------------
// Post event structs
//--------------------------------------------------------------
struct ProjectOperationEvent : dBus::api::DefaultData<dBus::makeID("project.file.operation")>
{
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<decltype(eventData), void>(bus, eventData.getID(), eventData, dBus::MessageCategory::UI);
}
//--------------------------------------------------------------
} // namespace api::project

View File

@@ -1,20 +1,11 @@
#pragma once #pragma once
#include <dbus/api/api.h> #include <dbus/api/api.h>
#include <filesystem>
#include <sdi_toolBox/generic/uuid.h> #include <sdi_toolBox/generic/uuid.h>
namespace api::requirement namespace api::requirement
{ {
//-------------------------------------------------------------- //--------------------------------------------------------------
enum class FileOperation : uint8_t
{
Create = 0,
Open,
Save,
SaveAs
};
//--------------------------------------------------------------
struct Requirement struct Requirement
{ {
std::string uuid; std::string uuid;
@@ -28,7 +19,7 @@ struct Requirement
/* Default constructor */ /* Default constructor */
Requirement() 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 // Post event structs
//-------------------------------------------------------------- //--------------------------------------------------------------
struct FileOperationEvent : dBus::api::DefaultData<dBus::makeID("requirement.file.operation")>
{
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<dBus::makeID("requirement.requirementEvent")> struct RequirementEvent : dBus::api::DefaultData<dBus::makeID("requirement.requirementEvent")>
{ {
virtual ~RequirementEvent() = default; // Default destructor virtual ~RequirementEvent() = default; // Default destructor
@@ -95,15 +51,6 @@ struct RequirementEvent : dBus::api::DefaultData<dBus::makeID("requirement.requi
//-------------------------------------------------------------- //--------------------------------------------------------------
// Post event helpers // Post event helpers
//-------------------------------------------------------------- //--------------------------------------------------------------
inline dBus::api::PostReturnStatus postFileOperationRequest(dBus::Bus &bus, const FileOperation &fileOperation, const std::filesystem::path &filePath = "")
{
auto eventData = FileOperationEvent();
eventData.fileOperation = std::move(fileOperation);
eventData.filePath = std::move(filePath);
return dBus::api::requestData<decltype(eventData), void>(bus, eventData.getID(), eventData, dBus::MessageCategory::UI);
}
//--------------------------------------------------------------
/* Post a requirement event to the bus. This function will post a /* Post a requirement event to the bus. This function will post a
* RequirementEvent containing the provided requirement data to the * RequirementEvent containing the provided requirement data to the
* bus, and return a PostReturnStatus indicating whether the post was * bus, and return a PostReturnStatus indicating whether the post was

View File

@@ -28,15 +28,15 @@ void CoreManager::init()
m_bus = make_unique<dBus::Bus>(); m_bus = make_unique<dBus::Bus>();
// Initialize other subsystems (e.g., logger, etc.) // Initialize other subsystems (e.g., logger, etc.)
m_logger = make_unique<logger::Logger>(*m_bus); m_logger = make_unique<logger::Logger>(*m_bus);
m_requirementManager = make_unique<requirementManager::RequirementManager>(*m_bus); m_projectManager = make_unique<project::ProjectManager>(*m_bus);
} }
//-------------------------------------------------------------- //--------------------------------------------------------------
/* Release resources used by the core manager (e.g., clean up the bus, etc.) */ /* Release resources used by the core manager (e.g., clean up the bus, etc.) */
void CoreManager::release() void CoreManager::release()
{ {
// Clean up other subsystems (e.g., logger, etc.) // Clean up other subsystems (e.g., logger, etc.)
m_requirementManager.reset(); m_projectManager.reset();
m_logger.reset(); m_logger.reset();
// Clean up the application bus // Clean up the application bus

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
#include "logger/logger.h" #include "logger/logger.h"
#include "requirementManager/requirementManager.h" #include "projectManager/projectManager.h"
#include <dBus/dBus.h> #include <dBus/dBus.h>
#include <memory> #include <memory>
@@ -24,8 +24,8 @@ class CoreManager
protected: protected:
std::unique_ptr<dBus::Bus> m_bus; // Application data bus std::unique_ptr<dBus::Bus> m_bus; // Application data bus
std::unique_ptr<logger::Logger> m_logger; // Application logger system std::unique_ptr<logger::Logger> m_logger; // Application logger system
std::unique_ptr<requirementManager::RequirementManager> m_requirementManager; // Requirement manager system std::unique_ptr<project::ProjectManager> m_projectManager; // Requirement manager system
private: private:
void init(); // Initialize the core manager (e.g., set up the bus, etc.) void init(); // Initialize the core manager (e.g., set up the bus, etc.)

View File

@@ -11,7 +11,7 @@ namespace core::logger
//-------------------------------------------------------------- //--------------------------------------------------------------
class Logger : public dBus::Node 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: public:
Logger() = delete; // Default constructor Logger() = delete; // Default constructor

View File

@@ -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;
}
//--------------------------------------------------------------

View File

@@ -0,0 +1,29 @@
#pragma once
#include <api/requirement.h>
#include <memory>
namespace core::project
{
//--------------------------------------------------------------
class ProjectData
{
using RequirementPtr = std::shared_ptr<api::requirement::Requirement>;
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

View File

@@ -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<dBus::RequestMessage<api::project::ProjectOperationEvent, void>>());
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<ProjectData>();
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();
}
//--------------------------------------------------------------

View File

@@ -0,0 +1,52 @@
#pragma once
#include <api/project.h>
#include <api/requirement.h>
#include <dBus/dBus.h>
#include <memory>
#include <thread>
namespace core::project
{
class ProjectData;
//--------------------------------------------------------------
class ProjectManager : public dBus::Node
{
using ProjectPtr = std::shared_ptr<ProjectData>;
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<dBus::Message>;
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<dBus::RequestMessage<api::project::ProjectOperationEvent, void>>;
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

View File

@@ -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<dBus::RequestMessage<api::requirement::FileOperationEvent, void>>();
if (!requestMessage)
break;
requestMessage->responsePromise.set_exception(std::make_exception_ptr(std::runtime_error("Failed to process file operation request")));
break;
}
}
}
//--------------------------------------------------------------

View File

@@ -1,38 +0,0 @@
#pragma once
#include <api/requirement.h>
#include <dBus/dBus.h>
#include <thread>
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<dBus::Message>;
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

View File

@@ -212,7 +212,8 @@ std::expected<TResponse, ReturnStatus> requestData(Bus
catch (const std::exception &e) catch (const std::exception &e)
{ {
::api::log::postLogError(bus, std::format("Exception in requestData for message type: 0x{:08x}: {}", msg->getMessageTypeID(), e.what())); ::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;
} }
} }
//-------------------------------------------------------------- //--------------------------------------------------------------

View File

@@ -26,7 +26,7 @@ void Node::subscribe(const HashID &eventType)
m_bus.subscribe(eventType, this); m_bus.subscribe(eventType, this);
} }
//-------------------------------------------------------------- //--------------------------------------------------------------
/* Unsubscribe from a specific message type */ /* Unsubscribe from a specific message type */
void Node::unsubscribe(const HashID &eventType) void Node::unsubscribe(const HashID &eventType)
{ {
m_bus.unsubscribe(eventType, this); m_bus.unsubscribe(eventType, this);