Work on the graphical interface for presenting requirements details

This commit is contained in:
Sylvain Schneider
2026-03-13 16:34:34 +01:00
parent 25fc14d6bd
commit 03d2e94f8b
17 changed files with 859 additions and 82 deletions

View File

@@ -1,4 +1,4 @@
#include "RequirementManager.h"
#include "requirementManager.h"
#include "requirementItem.h"

View File

@@ -35,8 +35,6 @@ class RequirementManager
protected:
RequirementItemPtr m_rootRequirement; // Root requirement of the current project
std::unordered_map<std::string, RequirementItemPtr> m_requirementsByUuid; // Map of requirement UUID to requirement pointer for quick lookup
private:
};
//--------------------------------------------------------------
} // namespace core::project

View File

@@ -1,6 +1,7 @@
#include "projectManager.h"
#include "RequirementManager.h"
#include "requirementItem.h"
#include "requirementManager.h"
using namespace std;
using namespace core::project;
@@ -33,8 +34,13 @@ 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 to the bus for messages related to project file and requirement operations
subscribe(dBus::makeID("project.file.operation"));
subscribe(dBus::makeID("requirement.get"));
subscribe(dBus::makeID("requirement.requirement.getChildren"));
subscribe(dBus::makeID("requirement.add"));
subscribe(dBus::makeID("requirement.update"));
subscribe(dBus::makeID("requirement.delete"));
/* 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. */
@@ -79,43 +85,75 @@ void ProjectManager::processMessageBus(const Message_ptr &message)
const auto messageType = message->getMessageTypeID();
switch (messageType)
{
// Process project events
case dBus::makeID("project.file.operation"):
{
// Process project operation request
on_projectOperationEvent(message->as<dBus::RequestMessage<api::project::ProjectOperationEvent, void>>());
on_projectOperationEvent(message);
break;
}
// Process requirement events
case dBus::makeID("requirement.get"):
{
on_requirementGetEvent(message->as<dBus::RequestMessage<api::requirement::GetRequirementEvent, api::requirement::Requirement>>());
break;
}
case dBus::makeID("requirement.getChildren"):
{
on_requirementGetChildrenEvent(message->as<dBus::RequestMessage<api::requirement::GetChildrenRequirementEvent, std::vector<api::requirement::Requirement>>>());
break;
}
case dBus::makeID("requirement.add"):
{
on_requirementAddEvent(message->as<dBus::RequestMessage<api::requirement::AddRequirementEvent, void>>());
break;
}
case dBus::makeID("requirement.update"):
{
on_requirementUpdateEvent(message->as<dBus::RequestMessage<api::requirement::UpdateRequirementEvent, void>>());
break;
}
case dBus::makeID("requirement.delete"):
{
on_requirementDeleteEvent(message->as<dBus::RequestMessage<api::requirement::DeleteRequirementEvent, void>>());
break;
}
default:
break;
}
}
//--------------------------------------------------------------
/* Process a file operation event */
void ProjectManager::on_projectOperationEvent(const ProjectOperationMessage_ptr &message)
void ProjectManager::on_projectOperationEvent(const Message_ptr &message)
{
const auto specializedMessage = message->as<dBus::RequestMessage<api::project::ProjectOperationEvent, void>>();
// Sanity check
if (!message)
if (!specializedMessage)
return;
switch (message->value.operationType)
switch (specializedMessage->value.operationType)
{
using enum api::project::OperationType;
case Create:
on_projectCreateFileEvent(message);
on_projectCreateFileEvent(specializedMessage);
break;
case Open:
on_projectOpenFileEvent(message);
on_projectOpenFileEvent(specializedMessage);
break;
case Save:
case SaveAs:
on_projectSaveFileEvent(message);
on_projectSaveFileEvent(specializedMessage);
break;
case Close:
on_projectCloseFileEvent(message);
on_projectCloseFileEvent(specializedMessage);
break;
}
}
//--------------------------------------------------------------
/* Process a file creation event */
void ProjectManager::on_projectCreateFileEvent(const ProjectOperationMessage_ptr &message)
void ProjectManager::on_projectCreateFileEvent(const api::project::ProjectOperationMessage_ptr &message)
{
m_requirementManager = make_shared<RequirementManager>();
@@ -123,22 +161,22 @@ void ProjectManager::on_projectCreateFileEvent(const ProjectOperationMessage_ptr
}
//--------------------------------------------------------------
/* Process a file open event */
void ProjectManager::on_projectOpenFileEvent(const ProjectOperationMessage_ptr &message)
void ProjectManager::on_projectOpenFileEvent(const api::project::ProjectOperationMessage_ptr &message)
{
// message->responsePromise.set_exception(std::make_exception_ptr(std::runtime_error("Open operation not implemented")));
try
{
openFile(message->value.filePath);
message->responsePromise.set_value();
}
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)
void ProjectManager::on_projectSaveFileEvent(const api::project::ProjectOperationMessage_ptr &message)
{
try
{
@@ -154,18 +192,176 @@ void ProjectManager::on_projectSaveFileEvent(const ProjectOperationMessage_ptr &
throw std::runtime_error("File path is empty for Save operation");
saveFile();
}
message->responsePromise.set_value();
}
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)
void ProjectManager::on_projectCloseFileEvent(const api::project::ProjectOperationMessage_ptr &message)
{
m_requirementManager = {};
message->responsePromise.set_value();
}
//--------------------------------------------------------------
/* Process a requirement get event */
void ProjectManager::on_requirementGetEvent(const api::requirement::GetRequirementMessage_ptr &message) const
{
// Sanity check
if (!message)
return;
try
{
// Sanity check
if (!m_requirementManager)
throw std::runtime_error("No project file is currently opened");
// Get requirement handle
const auto requirement = m_requirementManager->getRequirement(message->value.uuid);
if (!requirement)
throw std::runtime_error("Requirement not found");
message->responsePromise.set_value(requirement->toRequirement());
}
catch (const std::exception &e)
{
message->responsePromise.set_exception(std::make_exception_ptr(e));
}
}
//--------------------------------------------------------------
/* Process a requirement get children event */
void ProjectManager::on_requirementGetChildrenEvent(const api::requirement::GetChildrenRequirementMessage_ptr &message) const
{
// Sanity check
if (!message)
return;
try
{
// Sanity check
if (!m_requirementManager)
throw std::runtime_error("No project file is currently opened");
// Get requirement handle
const auto requirement = m_requirementManager->getRequirement(message->value.uuid);
if (!requirement)
throw std::runtime_error("Requirement not found");
// Get children requirements
const auto &children = requirement->getChildren();
vector<api::requirement::Requirement> childrenData;
childrenData.reserve(children.size());
for (const auto &child : children)
childrenData.push_back(child->toRequirement());
message->responsePromise.set_value(childrenData);
}
catch (const std::exception &e)
{
message->responsePromise.set_exception(std::make_exception_ptr(e));
}
}
//--------------------------------------------------------------
/* Process a requirement addition event */
void ProjectManager::on_requirementAddEvent(const api::requirement::AddRequirementMessage_ptr &message) const
{
// Sanity check
if (!message)
return;
try
{
// Sanity check
if (!m_requirementManager)
throw std::runtime_error("No project file is currently opened");
// Get parent requirement handle
const auto parentRequirement = message->value.parentUuid.empty()
? m_requirementManager->getRootRequirement()
: m_requirementManager->getRequirement(message->value.parentUuid);
if (!parentRequirement)
throw std::runtime_error("Parent requirement not found");
// Append the new requirement to the parent requirement
parentRequirement->appendChild(message->value.requirementData);
message->responsePromise.set_value();
}
catch (const std::exception &e)
{
message->responsePromise.set_exception(std::make_exception_ptr(e));
}
}
//--------------------------------------------------------------
/* Process a requirement update event */
void ProjectManager::on_requirementUpdateEvent(const api::requirement::UpdateRequirementMessage_ptr &message) const
{
// Sanity check
if (!message)
return;
try
{
// Sanity check
if (!m_requirementManager)
throw std::runtime_error("No project file is currently opened");
// Get requirement handle
const auto requirement = m_requirementManager->getRequirement(message->value.requirementData.metadata.uuid);
if (!requirement)
throw std::runtime_error("Requirement not found");
// Update the requirement data
requirement->update(message->value.requirementData);
message->responsePromise.set_value();
}
catch (const std::exception &e)
{
message->responsePromise.set_exception(std::make_exception_ptr(e));
}
}
//--------------------------------------------------------------
/* Process a requirement deletion event */
void ProjectManager::on_requirementDeleteEvent(const api::requirement::DeleteRequirementMessage_ptr &message) const
{
// Sanity check
if (!message)
return;
try
{
// Sanity check
if (!m_requirementManager)
throw std::runtime_error("No project file is currently opened");
// Get requirement handle
const auto requirement = m_requirementManager->getRequirement(message->value.requirementData.metadata.uuid);
if (!requirement)
throw std::runtime_error("Requirement not found");
// Get parent requirement handle
const auto parentRequirement = requirement->getParent();
if (!parentRequirement)
{
// If the requirement to be deleted is the root requirement,
// we cannot delete it as it is the base of the requirements hierarchy
throw std::runtime_error("Cannot delete root requirement");
}
// Remove the requirement from its parent
parentRequirement->removeChild(requirement->metadata.uuid);
message->responsePromise.set_value();
}
catch (const std::exception &e)
{
message->responsePromise.set_exception(std::make_exception_ptr(e));
}
}
//--------------------------------------------------------------

View File

@@ -41,12 +41,16 @@ class ProjectManager : public dBus::Node
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
void on_projectOperationEvent(const Message_ptr &message); // Process a file operation event
void on_projectCreateFileEvent(const api::project::ProjectOperationMessage_ptr &message); // Process a file creation event
void on_projectOpenFileEvent(const api::project::ProjectOperationMessage_ptr &message); // Process a file opening event
void on_projectSaveFileEvent(const api::project::ProjectOperationMessage_ptr &message); // Process a file saving event
void on_projectCloseFileEvent(const api::project::ProjectOperationMessage_ptr &message); // Process a file closing event
void on_requirementGetEvent(const api::requirement::GetRequirementMessage_ptr &message) const; // Process a requirement get event
void on_requirementGetChildrenEvent(const api::requirement::GetChildrenRequirementMessage_ptr &message) const; // Process a requirement get children event
void on_requirementAddEvent(const api::requirement::AddRequirementMessage_ptr &message) const; // Process a requirement addition event
void on_requirementUpdateEvent(const api::requirement::UpdateRequirementMessage_ptr &message) const; // Process a requirement update event
void on_requirementDeleteEvent(const api::requirement::DeleteRequirementMessage_ptr &message) const; // Process a requirement deletion event
};
//--------------------------------------------------------------
} // namespace core::project

View File

@@ -1,6 +1,6 @@
#include "requirementItem.h"
#include "RequirementManager.h"
#include "requirementManager.h"
using namespace std;
using namespace core::project;
@@ -17,21 +17,45 @@ RequirementItem::RequirementItem(RequirementManager &manager, const std::shared_
m_requirementManager.registerRequirement(shared_from_this());
}
//--------------------------------------------------------------
/* Convert this requirement item to an api::requirement::Requirement struct and return it */
api::requirement::Requirement RequirementItem::toRequirement() const
{
return { .metadata = metadata,
.details = details,
.classification = classification };
}
//--------------------------------------------------------------
/* Update the data of this requirement item with the provided updated requirement data */
void RequirementItem::update(const api::requirement::Requirement &updatedData)
{
const auto currentUUID = metadata.uuid; // Store the current UUID before updating
// Update the requirement data with the provided updated requirement data
metadata = updatedData.metadata;
details = updatedData.details;
classification = updatedData.classification;
// Restore the original UUID to maintain consistency
metadata.uuid = currentUUID;
}
//--------------------------------------------------------------
/* Get the m_parent requirement item of this requirement item (nullptr for root) */
RequirementItem::RequirementItemPtr RequirementItem::getParent() const
{
// Return the m_parent requirement item pointer if it exists
// or nullptr if this requirement item is the root
return m_parent.lock();
}
//--------------------------------------------------------------
/* Append a child requirement item to this requirement item */
void RequirementItem::appendChild(const api::requirement::Requirement &child)
void RequirementItem::appendChild(const Requirement &child)
{
// Create a new requirement item for the child requirement and set this requirement item as its m_parent
const auto childItem = make_shared<RequirementItem>(m_requirementManager);
childItem->metadata = child.metadata;
childItem->details = child.details;
childItem->classification = child.classification;
const auto childItem = make_shared<RequirementItem>(m_requirementManager, shared_from_this());
childItem->update(child); // Update the child requirement item with the provided child requirement data
// Prepare requirement links
childItem->metadata.uuid = m_requirementManager.generateUniqueUuid(); // Generate a unique UUID for this requirement item
childItem->m_parent = shared_from_this();
// Register this requirement item in the manager's internal map
m_requirementManager.registerRequirement(childItem);
// Append the child requirement item to the vector of children
m_children.push_back(childItem);
}
//--------------------------------------------------------------
/* Remove a child requirement item from this requirement item by its UUID */
@@ -41,11 +65,24 @@ void RequirementItem::removeChild(const std::string &childUuid)
m_requirementManager.unregisterRequirement(childUuid);
// Find the child requirement item with the given UUID
const auto it = ranges::find_if(m_children,
[&childUuid](const RequirementItemPtr &child)
{ return child->metadata.uuid == childUuid; });
if (it != m_children.end())
m_children.erase(it); // Remove the child requirement item from the vector of children
const auto childIt = ranges::find_if(m_children,
[&childUuid](const RequirementItemPtr &child)
{ return child->metadata.uuid == childUuid; });
if (childIt == m_children.end())
throw std::runtime_error("Child requirement item with UUID " + childUuid + " not found");
(*childIt)->removeChildren(); // Recursively remove all child requirement items of the child requirement item to be removed
m_children.erase(childIt); // Remove the child requirement item from the vector of children
}
//--------------------------------------------------------------
/* Remove all child requirement items from this requirement item */
void RequirementItem::removeChildren()
{
while (!m_children.empty())
{
const auto child = m_children.back(); // Get the last child requirement item
removeChild(child->metadata.uuid);
}
}
//--------------------------------------------------------------
/* Get the vector of child requirement items of this requirement item */

View File

@@ -24,10 +24,16 @@ class RequirementItem : public std::enable_shared_from_this<RequirementItem>
explicit RequirementItem(RequirementManager &manager, // Constructor
const std::shared_ptr<RequirementItem> &parentRequirement = nullptr);
// Management functions
[[nodiscard]] Requirement toRequirement() const; // Convert this requirement item to an api::requirement::Requirement struct and return it
void update(const Requirement &updatedData); // Update the data of this requirement item with the provided updated requirement data
// Children management functions
void appendChild(const api::requirement::Requirement &child); // Append a child requirement item to this requirement item
void removeChild(const std::string &childUuid); // Remove a child requirement item from this requirement item by its UUID
[[nodiscard]] std::vector<RequirementItemPtr> getChildren() const; // Get the vector of child requirement items of this requirement item
RequirementItemPtr getParent() const; // Get the m_parent requirement item of this requirement item
void appendChild(const Requirement &child); // Append a child requirement item to this requirement item
void removeChild(const std::string &childUuid); // Remove a child requirement item from this requirement item by its UUID
void removeChildren(); // Remove all child requirement items from this requirement item
[[nodiscard]] std::vector<RequirementItemPtr> getChildren() const; // Get the vector of child requirement items of this requirement item
protected:
RequirementManager &m_requirementManager; // Reference to the requirement manager that owns this requirement item