Initial commit of source code
This commit is contained in:
121
src/api/requirement.h
Normal file
121
src/api/requirement.h
Normal file
@@ -0,0 +1,121 @@
|
||||
#pragma once
|
||||
|
||||
#include <dbus/api/api.h>
|
||||
#include <filesystem>
|
||||
#include <sdi_toolBox/generic/uuid.h>
|
||||
|
||||
namespace api::requirement
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
enum class FileOperation : uint8_t
|
||||
{
|
||||
Create = 0,
|
||||
Open,
|
||||
Save,
|
||||
SaveAs
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
struct Requirement
|
||||
{
|
||||
std::string uuid;
|
||||
std::string parent_uuid;
|
||||
std::string id;
|
||||
std::string type;
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::vector<Requirement> sub_requirements;
|
||||
|
||||
/* Default constructor */
|
||||
Requirement()
|
||||
{
|
||||
uuid = sdi_toolBox::generic::UuidGenerator::uniqid(); // Generate a unique identifier for the requirement
|
||||
}
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
|
||||
/* --- */
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// 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")>
|
||||
{
|
||||
virtual ~RequirementEvent() = default; // Default destructor
|
||||
|
||||
Requirement requirementData;
|
||||
|
||||
[[nodiscard]] std::string toString() const override
|
||||
{
|
||||
return std::format("RequirementEvent: id={}, name={}, subRequirementsCount={}",
|
||||
requirementData.id,
|
||||
requirementData.name,
|
||||
requirementData.description,
|
||||
requirementData.sub_requirements.size());
|
||||
}
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
|
||||
/* --- */
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// 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
|
||||
* RequirementEvent containing the provided requirement data to the
|
||||
* bus, and return a PostReturnStatus indicating whether the post was
|
||||
* successful or if there were any issues (e.g., no subscribers,
|
||||
* timeout, etc.). */
|
||||
inline dBus::api::PostReturnStatus postRequirementRequest(dBus::Bus &bus,
|
||||
Requirement requirement)
|
||||
{
|
||||
auto eventData = RequirementEvent();
|
||||
eventData.requirementData = std::move(requirement);
|
||||
|
||||
return dBus::api::requestData<decltype(eventData), void>(bus, eventData.getID(), eventData, dBus::MessageCategory::UI);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace api::requirement
|
||||
45
src/core/coreManager.cpp
Normal file
45
src/core/coreManager.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "coreManager.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace core;
|
||||
//--------------------------------------------------------------
|
||||
/* Default constructor */
|
||||
CoreManager::CoreManager()
|
||||
{
|
||||
init();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Default destructor */
|
||||
CoreManager::~CoreManager()
|
||||
{
|
||||
release();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get a reference to the application bus */
|
||||
dBus::Bus &CoreManager::getBus() const
|
||||
{
|
||||
return *m_bus;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Initialize the core manager (e.g., set up the bus, etc.) */
|
||||
void CoreManager::init()
|
||||
{
|
||||
// Initialize the application bus
|
||||
m_bus = make_unique<dBus::Bus>();
|
||||
|
||||
// Initialize other subsystems (e.g., logger, etc.)
|
||||
m_logger = make_unique<logger::Logger>(*m_bus);
|
||||
m_requirementManager = make_unique<requirementManager::RequirementManager>(*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_logger.reset();
|
||||
|
||||
// Clean up the application bus
|
||||
m_bus.reset();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
35
src/core/coreManager.h
Normal file
35
src/core/coreManager.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "logger/logger.h"
|
||||
#include "requirementManager/requirementManager.h"
|
||||
|
||||
#include <dBus/dBus.h>
|
||||
#include <memory>
|
||||
|
||||
namespace core
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class CoreManager
|
||||
{
|
||||
public:
|
||||
CoreManager(); // Default constructor
|
||||
virtual ~CoreManager(); // Default destructor
|
||||
CoreManager(const CoreManager &obj) = delete; // Copy constructor
|
||||
CoreManager(CoreManager &&obj) noexcept = delete; // Move constructor
|
||||
CoreManager &operator=(const CoreManager &obj) = delete; // Copy assignment operator
|
||||
CoreManager &operator=(CoreManager &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
[[nodiscard]] dBus::Bus &getBus() const; // Get a reference to the application bus
|
||||
|
||||
protected:
|
||||
std::unique_ptr<dBus::Bus> m_bus; // Application data bus
|
||||
|
||||
std::unique_ptr<logger::Logger> m_logger; // Application logger system
|
||||
std::unique_ptr<requirementManager::RequirementManager> m_requirementManager; // Requirement manager system
|
||||
|
||||
private:
|
||||
void init(); // Initialize the core manager (e.g., set up the bus, etc.)
|
||||
void release(); // Release resources used by the core manager (e.g., clean up the bus, etc.)
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
} // namespace core
|
||||
172
src/core/logger/logger.cpp
Normal file
172
src/core/logger/logger.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
#include "logger.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <syncstream>
|
||||
|
||||
using namespace std;
|
||||
using namespace core::logger;
|
||||
//--------------------------------------------------------------
|
||||
/* Constructor */
|
||||
Logger::Logger(dBus::Bus &bus)
|
||||
: Node(bus)
|
||||
{
|
||||
// Initialize the log printing loop
|
||||
runPrintingLoop();
|
||||
|
||||
// Initialize the bus listener thread
|
||||
runBusListener();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Default destructor */
|
||||
Logger::~Logger()
|
||||
{
|
||||
// Stop the bus listener thread if it's running
|
||||
stopBusListener();
|
||||
|
||||
// Stop the log printing loop if it's running
|
||||
stopPrintingLoop();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Run the loop that periodically prints log entries to the console */
|
||||
void Logger::runPrintingLoop()
|
||||
{
|
||||
/* Start the printing thread to periodically print log entries to the console.
|
||||
* The thread will continuously wait for messages and process them until a stop is requested */
|
||||
// clang-format off
|
||||
m_printingThread = std::jthread([this](const std::stop_token &stopToken)
|
||||
{
|
||||
while (!stopToken.stop_requested())
|
||||
{
|
||||
// Sleep for a short duration to avoid busy waiting and reduce CPU usage
|
||||
this_thread::sleep_for(100ms);
|
||||
|
||||
// Print the log entries after processing the message
|
||||
if (m_logEntries.timer.isElapsed(LOG_PRINT_INTERVAL, true))
|
||||
printLogEntries();
|
||||
}
|
||||
});
|
||||
// clang-format on
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Stop the log printing loop */
|
||||
void Logger::stopPrintingLoop()
|
||||
{
|
||||
// Stop printing thread loop
|
||||
m_printingThread.request_stop();
|
||||
|
||||
// Despite a jthread will automatically join in its destructor, we
|
||||
// can explicitly join here to ensure that the thread has finished
|
||||
// executing before the Logger object is destroyed.
|
||||
// This is especially important if the thread is still running and
|
||||
// may access resources that are being cleaned up in the destructor.
|
||||
// By joining the thread, we can ensure a clean shutdown of the Logger
|
||||
// and avoid potential issues with dangling threads or accessing invalid resources.
|
||||
if (m_printingThread.joinable())
|
||||
m_printingThread.join();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Print the log entries to the console */
|
||||
void Logger::printLogEntries()
|
||||
{
|
||||
// Move the log entries to a local variable and clear the
|
||||
// original vector to minimize the time the mutex is locked
|
||||
std::vector<std::string> logEntries;
|
||||
{
|
||||
scoped_lock lock(m_logEntries.mtx);
|
||||
logEntries = std::move(m_logEntries.entries);
|
||||
m_logEntries.entries.clear();
|
||||
}
|
||||
|
||||
for (const auto &entry : logEntries)
|
||||
{
|
||||
// Use osyncstream to ensure thread-safe output to the console
|
||||
std::osyncstream syncOut(std::cout);
|
||||
syncOut << entry << std::endl;
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Process a log message received from the bus */
|
||||
void Logger::processUserMessage(const LogMessage_ptr &logMessage)
|
||||
{
|
||||
// Sanity check
|
||||
if (!logMessage)
|
||||
return;
|
||||
|
||||
// Add the log entry to the vector of log entries
|
||||
std::scoped_lock lock(m_logEntries.mtx);
|
||||
m_logEntries.entries.push_back(logMessage->toString());
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Process a system log message received from the bus */
|
||||
void Logger::processSystemMessage(const std::string &logMessage)
|
||||
{
|
||||
// Sanity check
|
||||
if (logMessage.empty())
|
||||
return;
|
||||
|
||||
// Add the log entry to the vector of log entries
|
||||
std::scoped_lock lock(m_logEntries.mtx);
|
||||
m_logEntries.entries.push_back(logMessage);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Run the bus listener thread */
|
||||
void Logger::runBusListener()
|
||||
{
|
||||
// Subscribe to the bus in broadcast mode to receive all messages posted to the bus
|
||||
m_bus.subscribeToBroadcast(this);
|
||||
|
||||
/* 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 Logger::stopBusListener()
|
||||
{
|
||||
// Stop event thread loop
|
||||
m_busListenerThread.request_stop();
|
||||
|
||||
// Wake up event thread if waiting
|
||||
notifyMessageQueue();
|
||||
|
||||
// Join the bus listener thread to ensure it has finished
|
||||
// executing before the Logger object is destroyed
|
||||
if (m_busListenerThread.joinable())
|
||||
m_busListenerThread.join();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Process a message received from the bus */
|
||||
void Logger::processMessageBus(const Message_ptr &message)
|
||||
{
|
||||
// Check the message type and process accordingly
|
||||
const auto messageType = message->getMessageTypeID();
|
||||
|
||||
if (messageType == dBus::makeID("log.message"))
|
||||
{
|
||||
// Process log message
|
||||
processUserMessage(message->as<dBus::EventMessage<api::log::LogMessage>>());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Process system log message (for any other message type, we will log it as a system message)
|
||||
processSystemMessage(message->toString());
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
53
src/core/logger/logger.h
Normal file
53
src/core/logger/logger.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <dBus/api/log.h>
|
||||
#include <dBus/dBus.h>
|
||||
#include <mutex>
|
||||
#include <sdi_toolBox/dateTime/timer.h>
|
||||
#include <thread>
|
||||
|
||||
namespace core::logger
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class Logger : public dBus::Node
|
||||
{
|
||||
static constexpr auto LOG_PRINT_INTERVAL = std::chrono::seconds{ 1 };
|
||||
|
||||
public:
|
||||
Logger() = delete; // Default constructor
|
||||
virtual ~Logger(); // Default destructor
|
||||
Logger(const Logger &obj) = delete; // Copy constructor
|
||||
Logger(Logger &&obj) noexcept = delete; // Move constructor
|
||||
Logger &operator=(const Logger &obj) = delete; // Copy assignment operator
|
||||
Logger &operator=(Logger &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
explicit Logger(dBus::Bus &bus); // Constructor
|
||||
|
||||
protected:
|
||||
struct
|
||||
{
|
||||
std::mutex mtx; // Mutex to protect access to the log entries vector
|
||||
sdi_toolBox::dateTime::Timer timer; // Timer to control the frequency of log printing
|
||||
std::vector<std::string> entries; // Vector to store log entries received from the bus
|
||||
} m_logEntries;
|
||||
|
||||
private:
|
||||
void runPrintingLoop(); // Run the loop that periodically prints log entries to the console
|
||||
void stopPrintingLoop(); // Stop the log printing loop
|
||||
void printLogEntries(); // Print the log entries to the console
|
||||
std::jthread m_printingThread; // Thread for printing log entries to the console
|
||||
|
||||
// Logger management
|
||||
using LogMessage_ptr = std::shared_ptr<dBus::EventMessage<api::log::LogMessage>>;
|
||||
void processUserMessage(const LogMessage_ptr &logMessage); // Process a log message received from the bus
|
||||
void processSystemMessage(const std::string &logMessage); // Process a system log message received from the bus
|
||||
|
||||
// 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::logger
|
||||
89
src/core/requirementManager/requirementManager.cpp
Normal file
89
src/core/requirementManager/requirementManager.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
38
src/core/requirementManager/requirementManager.h
Normal file
38
src/core/requirementManager/requirementManager.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#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
|
||||
219
src/dBus/api/api.h
Normal file
219
src/dBus/api/api.h
Normal file
@@ -0,0 +1,219 @@
|
||||
#pragma once
|
||||
|
||||
#include "logWrapper.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <dBus/dBus.h>
|
||||
#include <dBus/defs.h>
|
||||
#include <expected>
|
||||
#include <format>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace dBus::api
|
||||
{
|
||||
// Define a type alias for the return status of post
|
||||
// operations, using std::expected to represent success
|
||||
// or failure with an error code
|
||||
using PostReturnStatus = std::expected<void, dBus::ReturnStatus>;
|
||||
|
||||
//--------------------------------------------------------------
|
||||
struct BaseDefaultData
|
||||
{
|
||||
};
|
||||
|
||||
template<HashID ID>
|
||||
struct DefaultData : BaseDefaultData
|
||||
{
|
||||
DefaultData() = default; // Default constructor
|
||||
virtual ~DefaultData() = default; // Default destructor
|
||||
DefaultData(const DefaultData &obj) = default; // Copy constructor
|
||||
DefaultData(DefaultData &&obj) noexcept = default; // Move constructor
|
||||
DefaultData &operator=(const DefaultData &obj) = default; // Copy assignment operator
|
||||
DefaultData &operator=(DefaultData &&obj) noexcept = default; // Move assignment operator
|
||||
|
||||
constexpr HashID getID() const
|
||||
{
|
||||
return ID;
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual std::string toString() const = 0; // Convert the data to a string representation for logging or debugging
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
|
||||
/* --- */
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// Generic message API template for simple data transfer (fire-and-forget)
|
||||
template<class T>
|
||||
class MessageApi : public EventMessage<T>
|
||||
{
|
||||
public:
|
||||
MessageApi() = delete; // Default constructor
|
||||
virtual ~MessageApi() = default; // Default destructor
|
||||
MessageApi(const MessageApi &obj) = delete; // Copy constructor
|
||||
MessageApi(MessageApi &&obj) noexcept = delete; // Move constructor
|
||||
MessageApi &operator=(const MessageApi &obj) = delete; // Copy assignment operator
|
||||
MessageApi &operator=(MessageApi &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
explicit MessageApi(const HashID &messageTypeID, T data, const MessageCategory &category = MessageCategory::System) // Constructor
|
||||
: EventMessage<T>(messageTypeID, category, std::move(data))
|
||||
{
|
||||
}
|
||||
|
||||
// Serialize the message data to a string for logging or debugging
|
||||
[[nodiscard]] std::string serializeData() const override
|
||||
{
|
||||
if constexpr (std::is_base_of_v<BaseDefaultData, T>)
|
||||
{
|
||||
return this->value.toString();
|
||||
}
|
||||
else if constexpr (std::is_arithmetic_v<T>)
|
||||
{
|
||||
return std::to_string(this->value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
// Request message API template for data transfer with response
|
||||
template<class TRequest, class TResponse>
|
||||
class RequestMessageApi : public RequestMessage<TRequest, TResponse>
|
||||
{
|
||||
public:
|
||||
RequestMessageApi() = delete; // Default constructor
|
||||
virtual ~RequestMessageApi() = default; // Default destructor
|
||||
RequestMessageApi(const RequestMessageApi &obj) = delete; // Copy constructor
|
||||
RequestMessageApi(RequestMessageApi &&obj) noexcept = delete; // Move constructor
|
||||
RequestMessageApi &operator=(const RequestMessageApi &obj) = delete; // Copy assignment operator
|
||||
RequestMessageApi &operator=(RequestMessageApi &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
explicit RequestMessageApi(const HashID &messageTypeID, TRequest request, const MessageCategory &category = MessageCategory::System)
|
||||
: RequestMessage<TRequest, TResponse>(messageTypeID, category, std::move(request), createResponse())
|
||||
{
|
||||
}
|
||||
|
||||
// Serialize the message data to a string for logging or debugging
|
||||
[[nodiscard]] std::string serializeData() const override
|
||||
{
|
||||
if constexpr (std::is_base_of_v<BaseDefaultData, TRequest>)
|
||||
{
|
||||
return this->value.toString();
|
||||
}
|
||||
else if constexpr (std::is_arithmetic_v<TRequest>)
|
||||
{
|
||||
return std::to_string(this->value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static auto createResponse()
|
||||
{
|
||||
if constexpr (std::is_void_v<TResponse>)
|
||||
return nullptr; // Use nullptr for void response type since there is no value to return
|
||||
else
|
||||
return std::make_shared<TResponse>();
|
||||
}
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
|
||||
/* --- */
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// Post a simple data message (fire-and-forget)
|
||||
template<class T>
|
||||
bool postData(Bus &bus,
|
||||
const HashID &messageTypeID,
|
||||
T data,
|
||||
const MessageCategory &category)
|
||||
{
|
||||
auto msg = std::make_shared<MessageApi<T>>(messageTypeID, std::move(data), category);
|
||||
return bus.post(msg);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
// Post a data message with subscriber check
|
||||
template<class T>
|
||||
std::expected<void, ReturnStatus> postDataChecked(Bus &bus,
|
||||
const HashID &messageTypeID,
|
||||
T data,
|
||||
const MessageCategory &category)
|
||||
{
|
||||
auto msg = std::make_shared<MessageApi<T>>(messageTypeID, std::move(data), category);
|
||||
const auto subscribersFound = bus.post(msg);
|
||||
|
||||
if (!subscribersFound)
|
||||
{
|
||||
return std::unexpected(ReturnStatus::NoSubscribers);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
template<class TRequest, class TResponse>
|
||||
std::expected<TResponse, ReturnStatus> requestData(Bus &bus,
|
||||
const HashID &messageTypeID,
|
||||
TRequest request,
|
||||
const MessageCategory &category,
|
||||
const std::chrono::milliseconds timeout = DEFAULT_TIMEOUT)
|
||||
{
|
||||
auto msg = std::make_shared<RequestMessageApi<TRequest, TResponse>>(messageTypeID, std::move(request), category);
|
||||
auto future = msg->getFuture();
|
||||
|
||||
try
|
||||
{
|
||||
// Post the message to the bus and check if there are subscribers
|
||||
const auto subscribersFound = bus.post(msg);
|
||||
if (!subscribersFound)
|
||||
{
|
||||
::api::log::postLogWarning(bus, std::format("No subscribers found for message type: 0x{:08x}", msg->getMessageTypeID()));
|
||||
return std::unexpected(ReturnStatus::NoSubscribers);
|
||||
}
|
||||
|
||||
const auto status = future.wait_for(timeout);
|
||||
if (status == std::future_status::ready)
|
||||
{
|
||||
if constexpr (std::is_void_v<TResponse>)
|
||||
{
|
||||
// Wait for the future to be ready and check for exceptions,
|
||||
// but ignore the value since it's void
|
||||
future.get();
|
||||
|
||||
return {}; // Return success without a value
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wait for the future to be ready and return the value,
|
||||
// or propagate any exceptions that occurred during the
|
||||
// request handling
|
||||
return future.get(); // Return success with value
|
||||
}
|
||||
}
|
||||
else if (status == std::future_status::timeout)
|
||||
{
|
||||
::api::log::postLogWarning(bus, std::format("Timeout while waiting for response to message type: 0x{:08x}", msg->getMessageTypeID()));
|
||||
return std::unexpected(ReturnStatus::Timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
::api::log::postLogError(bus, std::format("Unexpected future status while waiting for response to message type: 0x{:08x}", msg->getMessageTypeID()));
|
||||
return std::unexpected(ReturnStatus::Error);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace dBus::api
|
||||
70
src/dBus/api/log.h
Normal file
70
src/dBus/api/log.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include "api.h"
|
||||
|
||||
#include <sdi_toolBox/dateTime/age.h>
|
||||
|
||||
namespace api::log
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
// Log levels
|
||||
//--------------------------------------------------------------
|
||||
enum class LogLevel : uint8_t
|
||||
{
|
||||
Message = 0,
|
||||
Debug,
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
|
||||
System, // Reserved for system-level logs, not intended for user messages
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
|
||||
/* --- */
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// Log message structure
|
||||
//--------------------------------------------------------------
|
||||
struct LogMessage : dBus::api::DefaultData<dBus::makeID("log.message")>
|
||||
{
|
||||
using Timestamp = std::chrono::steady_clock::time_point;
|
||||
|
||||
virtual ~LogMessage() = default;
|
||||
|
||||
Timestamp timestamp = std::chrono::steady_clock::now();
|
||||
LogLevel level = LogLevel::Message;
|
||||
std::string message;
|
||||
|
||||
[[nodiscard]] std::string toString() const override
|
||||
{
|
||||
const auto timestampFormatter = Age(std::chrono::duration_cast<std::chrono::nanoseconds>(timestamp.time_since_epoch()));
|
||||
const auto logEntry = std::format("{}{}",
|
||||
getLevelString(),
|
||||
message);
|
||||
return logEntry;
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] std::string getLevelString() const
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
using enum LogLevel;
|
||||
case Debug:
|
||||
return "[Debug] ";
|
||||
case Info:
|
||||
return "[Info] ";
|
||||
case Warning:
|
||||
return "[Warning] ";
|
||||
case Error:
|
||||
return "[Error] ";
|
||||
case System:
|
||||
return "[System] ";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
} // namespace api::log
|
||||
50
src/dBus/api/logWrapper.cpp
Normal file
50
src/dBus/api/logWrapper.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "logWrapper.h"
|
||||
|
||||
#include "log.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
void postMessage(dBus::Bus &bus, const api::log::LogLevel logLevel, const std::string &message)
|
||||
{
|
||||
api::log::LogMessage logMsg;
|
||||
logMsg.level = logLevel;
|
||||
logMsg.message = message;
|
||||
|
||||
// Post the log message to the bus with the appropriate category (Fire-and-forget, no response expected)
|
||||
(void)dBus::api::postData(bus, logMsg.getID(), logMsg, dBus::MessageCategory::Log);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace
|
||||
|
||||
//--------------------------------------------------------------
|
||||
void api::log::postLogMessage(dBus::Bus &bus, const std::string &message)
|
||||
{
|
||||
postMessage(bus, LogLevel::Message, message);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
void api::log::postLogDebug(dBus::Bus &bus, const std::string &message)
|
||||
{
|
||||
postMessage(bus, LogLevel::Debug, message);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
void api::log::postLogInfo(dBus::Bus &bus, const std::string &message)
|
||||
{
|
||||
postMessage(bus, LogLevel::Info, message);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
void api::log::postLogWarning(dBus::Bus &bus, const std::string &message)
|
||||
{
|
||||
postMessage(bus, LogLevel::Warning, message);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
void api::log::postLogError(dBus::Bus &bus, const std::string &message)
|
||||
{
|
||||
postMessage(bus, LogLevel::Error, message);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
void api::log::postLogSystem(dBus::Bus &bus, const std::string &message)
|
||||
{
|
||||
postMessage(bus, LogLevel::System, message);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
24
src/dBus/api/logWrapper.h
Normal file
24
src/dBus/api/logWrapper.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "../bus.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace api::log
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
// Post event helpers
|
||||
//--------------------------------------------------------------
|
||||
void postLogMessage(dBus::Bus &bus, const std::string &message);
|
||||
//--------------------------------------------------------------
|
||||
void postLogDebug(dBus::Bus &bus, const std::string &message);
|
||||
//--------------------------------------------------------------
|
||||
void postLogInfo(dBus::Bus &bus, const std::string &message);
|
||||
//--------------------------------------------------------------
|
||||
void postLogWarning(dBus::Bus &bus, const std::string &message);
|
||||
//--------------------------------------------------------------
|
||||
void postLogError(dBus::Bus &bus, const std::string &message);
|
||||
//--------------------------------------------------------------
|
||||
void postLogSystem(dBus::Bus &bus, const std::string &message);
|
||||
//--------------------------------------------------------------
|
||||
} // namespace api::log
|
||||
274
src/dBus/bus.cpp
Normal file
274
src/dBus/bus.cpp
Normal file
@@ -0,0 +1,274 @@
|
||||
#include "bus.h"
|
||||
|
||||
#include "dbus.h"
|
||||
|
||||
#include <ranges>
|
||||
#include <set>
|
||||
|
||||
using namespace std;
|
||||
using namespace dBus;
|
||||
//--------------------------------------------------------------
|
||||
/* Default constructor */
|
||||
Bus::Bus()
|
||||
{
|
||||
// Initialization
|
||||
m_startTimestamp = std::chrono::steady_clock::now();
|
||||
|
||||
// Start the monitoring thread
|
||||
m_monitoringThread = jthread([&](const stop_token &st)
|
||||
{
|
||||
while (!st.stop_requested())
|
||||
{
|
||||
this_thread::sleep_for(100ms);
|
||||
updateMonitoringData();
|
||||
} });
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the timestamp of when the bus started */
|
||||
TimePoint Bus::getStartTimestamp() const
|
||||
{
|
||||
return m_startTimestamp;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Subscribe a listener to a specific event type */
|
||||
void Bus::subscribe(const HashID &eventType, Node *node)
|
||||
{
|
||||
// Sanity check
|
||||
if (!node)
|
||||
throw runtime_error("invalid node handle");
|
||||
|
||||
std::scoped_lock lock(m_routingTable.mtx);
|
||||
|
||||
// Event type not registered yet, create a new entry with the node
|
||||
if (!m_routingTable.nodes.contains(eventType))
|
||||
{
|
||||
m_routingTable.nodes[eventType] = { node };
|
||||
m_routingTable.activeNodesCache.insert(node);
|
||||
return;
|
||||
}
|
||||
|
||||
// Event type already registered, add the node if not already subscribed
|
||||
auto &nodes = m_routingTable.nodes.at(eventType);
|
||||
if (std::ranges::find(nodes, node) == nodes.end())
|
||||
{
|
||||
nodes.push_back(node);
|
||||
m_routingTable.activeNodesCache.insert(node);
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Unsubscribe a listener from a specific event type */
|
||||
void Bus::unsubscribe(const HashID &eventType, Node *node)
|
||||
{
|
||||
// Sanity check
|
||||
if (!node)
|
||||
throw runtime_error("invalid node handle");
|
||||
|
||||
std::scoped_lock lock(m_routingTable.mtx);
|
||||
|
||||
// Event type not recorded, nothing to do
|
||||
if (!m_routingTable.nodes.contains(eventType))
|
||||
return;
|
||||
|
||||
// Event type registered, remove the listener if subscribed
|
||||
auto &nodes = m_routingTable.nodes.at(eventType);
|
||||
std::erase(nodes, node);
|
||||
|
||||
// Check if the node is still subscribed to any message type before removing it from the active nodes cache
|
||||
if (!isSubscribedEverywhere(node))
|
||||
m_routingTable.activeNodesCache.erase(node);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Unsubscribe a listener from all event types */
|
||||
void Bus::unsubscribeAll(Node *node)
|
||||
{
|
||||
// Sanity check
|
||||
if (!node)
|
||||
throw runtime_error("invalid node handle");
|
||||
|
||||
std::scoped_lock lock(m_routingTable.mtx);
|
||||
|
||||
// Iterate through all event types and remove the node from each list
|
||||
for (auto &nodeList : m_routingTable.nodes | std::views::values)
|
||||
{
|
||||
std::erase(nodeList, node);
|
||||
}
|
||||
|
||||
// Check if the node is still subscribed to any message type before removing it from the active nodes cache
|
||||
if (!isSubscribedEverywhere(node))
|
||||
m_routingTable.activeNodesCache.erase(node);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Check if a listener is subscribed to a specific event type */
|
||||
bool Bus::isSubscribed(const HashID &eventType, const Node *node) const
|
||||
{
|
||||
// Sanity check
|
||||
if (!node)
|
||||
throw runtime_error("invalid node handle");
|
||||
|
||||
std::scoped_lock lock(m_routingTable.mtx);
|
||||
|
||||
// Event type not recorded, node not subscribed
|
||||
if (!m_routingTable.nodes.contains(eventType))
|
||||
return false;
|
||||
|
||||
// Event type recorded, check if the node is in the list
|
||||
const auto &nodes = m_routingTable.nodes.at(eventType);
|
||||
return std::ranges::find(nodes, node) != nodes.end();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Subscribe a node to receive all messages (broadcast mode) */
|
||||
void Bus::subscribeToBroadcast(Node *node)
|
||||
{
|
||||
// Sanity check
|
||||
if (!node)
|
||||
throw runtime_error("invalid node handle");
|
||||
|
||||
std::scoped_lock lock(m_routingTable.mtx);
|
||||
|
||||
// Add the node to the broadcast nodes set
|
||||
m_routingTable.broadcastNodes.insert(node);
|
||||
m_routingTable.activeNodesCache.insert(node);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Unsubscribe a node from broadcast mode */
|
||||
void Bus::unsubscribeFromBroadcast(Node *node)
|
||||
{
|
||||
// Sanity check
|
||||
if (!node)
|
||||
throw runtime_error("invalid node handle");
|
||||
|
||||
std::scoped_lock lock(m_routingTable.mtx);
|
||||
|
||||
// Remove the node from the broadcast nodes set
|
||||
m_routingTable.broadcastNodes.erase(node);
|
||||
|
||||
// Check if the node is still subscribed to any message type before removing it from the active nodes cache
|
||||
if (!isSubscribedEverywhere(node))
|
||||
m_routingTable.activeNodesCache.erase(node);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Check if a node is subscribed to broadcast mode */
|
||||
bool Bus::isSubscribedToBroadcast(Node *node) const
|
||||
{
|
||||
// Sanity check
|
||||
if (!node)
|
||||
throw runtime_error("invalid node handle");
|
||||
|
||||
std::scoped_lock lock(m_routingTable.mtx);
|
||||
|
||||
// Check if the node is in the broadcast nodes set
|
||||
return m_routingTable.broadcastNodes.contains(node);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
bool Bus::post(const std::shared_ptr<Message> &message)
|
||||
{
|
||||
// Link the message to the bus (set the posted information for the message)
|
||||
message->linkToBus(this);
|
||||
|
||||
// Record the incoming message for monitoring purposes
|
||||
recordIncoming();
|
||||
|
||||
std::scoped_lock lock(m_routingTable.mtx);
|
||||
|
||||
// Get the list of nodes subscribed to the message type
|
||||
set<Node *> recipients;
|
||||
|
||||
// Add nodes subscribed to the specific message type
|
||||
if (m_routingTable.nodes.contains(message->getMessageTypeID()))
|
||||
{
|
||||
const auto &nodeList = m_routingTable.nodes.at(message->getMessageTypeID());
|
||||
for (const auto &node : nodeList)
|
||||
recipients.insert(node);
|
||||
}
|
||||
const bool subscribersFound = !recipients.empty();
|
||||
|
||||
// Add broadcast nodes
|
||||
recipients.insert(m_routingTable.broadcastNodes.begin(),
|
||||
m_routingTable.broadcastNodes.end());
|
||||
|
||||
// Dispatch the message to all recipients
|
||||
for (const auto &node : recipients)
|
||||
node->receiveMessageFromBus(message);
|
||||
|
||||
return subscribersFound;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the current monitoring data of the bus */
|
||||
Bus::MonitoringData Bus::getMonitoringData() const
|
||||
{
|
||||
std::scoped_lock lock(m_monitoring.mtx);
|
||||
return m_monitoring.data;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Set the age of the message (to be called by the message when it is destroyed) */
|
||||
void Bus::setMessageAge(std::chrono::microseconds age)
|
||||
{
|
||||
scoped_lock lock(m_monitoring.mtx);
|
||||
|
||||
m_monitoring.data.maxAge = std::max(m_monitoring.data.maxAge, age);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Check if a node is subscribed to any message type (internal, assumes lock is held) */
|
||||
bool Bus::isSubscribedEverywhere(Node *node) const
|
||||
{
|
||||
// Sanity check
|
||||
if (!node)
|
||||
throw runtime_error("invalid node handle");
|
||||
|
||||
// Check if the node is still subscribed to any message type
|
||||
for (const auto &nodeList : m_routingTable.nodes | std::views::values)
|
||||
{
|
||||
if (std::ranges::find(nodeList, node) != nodeList.end())
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the node is subscribed to broadcast mode
|
||||
if (m_routingTable.broadcastNodes.contains(node))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Incoming messages for monitoring purposes */
|
||||
void Bus::recordIncoming()
|
||||
{
|
||||
m_monitoring.messageCounter++;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Update the monitoring data of the bus */
|
||||
void Bus::updateMonitoringData()
|
||||
{
|
||||
if (!m_monitoring.frequencyTimer.isElapsed(10s))
|
||||
return;
|
||||
|
||||
// Update monitoring data
|
||||
MonitoringData monitoringData;
|
||||
{
|
||||
std::scoped_lock lock(m_routingTable.mtx, m_monitoring.mtx);
|
||||
|
||||
// Update the global message count with the number of messages received in the last interval
|
||||
m_monitoring.data.globalMessageCount += m_monitoring.messageCounter;
|
||||
|
||||
// Calculate the frequency of messages being posted to the bus (messages per second)
|
||||
const auto duration_s = static_cast<double>(chrono::duration_cast<chrono::milliseconds>(m_monitoring.frequencyTimer.getElapsed()).count()) / 1000.0;
|
||||
m_monitoring.data.frequencyHz = static_cast<double>(m_monitoring.messageCounter) / duration_s;
|
||||
|
||||
// Update the current message count and maximum age of messages on the bus
|
||||
m_monitoring.data.currentMessageCount = 0;
|
||||
for (const auto &node : m_routingTable.activeNodesCache)
|
||||
m_monitoring.data.currentMessageCount += node->getMessageCount();
|
||||
|
||||
// Copy the monitoring data to a local variable for posting the monitoring message after releasing the locks
|
||||
monitoringData = m_monitoring.data;
|
||||
|
||||
// Reset all monitoring counters and timers for the next interval
|
||||
m_monitoring.data.maxAge = {};
|
||||
m_monitoring.messageCounter = 0;
|
||||
m_monitoring.frequencyTimer.reset();
|
||||
}
|
||||
|
||||
// Post monitoring message to the bus
|
||||
// TODO
|
||||
//postMonitoringMsg(*this, monitoringData);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
83
src/dBus/bus.h
Normal file
83
src/dBus/bus.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
#include "Message.h"
|
||||
#include "Node.h"
|
||||
#include "defs.h"
|
||||
|
||||
#include <expected>
|
||||
#include <mutex>
|
||||
#include <sdi_toolBox/dateTime/timer.h>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace dBus
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class Bus
|
||||
{
|
||||
public:
|
||||
struct MonitoringData
|
||||
{
|
||||
size_t globalMessageCount{ 0 }; // Total count of messages posted to the bus
|
||||
double frequencyHz = { 0 }; // Current frequency of messages being posted to the bus (calculated as messages per second)
|
||||
|
||||
size_t currentMessageCount{ 0 }; // Current count of messages on the bus
|
||||
std::chrono::microseconds maxAge{}; // Maximum age of messages on the bus
|
||||
};
|
||||
|
||||
public:
|
||||
Bus(); // Default constructor
|
||||
virtual ~Bus() = default; // Default destructor
|
||||
Bus(const Bus &obj) = delete; // Copy constructor
|
||||
Bus(Bus &&obj) noexcept = delete; // Move constructor
|
||||
Bus &operator=(const Bus &obj) = delete; // Copy assignment operator
|
||||
Bus &operator=(Bus &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
[[nodiscard]] TimePoint getStartTimestamp() const; // Get the timestamp of when the bus started
|
||||
|
||||
// Registration and unregistration of listeners
|
||||
void subscribe(const HashID &eventType, Node *node); // Subscribe a listener to a specific event type
|
||||
void unsubscribe(const HashID &eventType, Node *node); // Unsubscribe a listener from a specific event type
|
||||
void unsubscribeAll(Node *node); // Unsubscribe a listener from all event types
|
||||
[[nodiscard]] bool isSubscribed(const HashID &eventType, const Node *node) const; // Check if a listener is subscribed to a specific event type
|
||||
|
||||
// Broadcast management
|
||||
void subscribeToBroadcast(Node *node); // Subscribe a node to receive all messages (broadcast mode)
|
||||
void unsubscribeFromBroadcast(Node *node); // Unsubscribe a node from broadcast mode
|
||||
[[nodiscard]] bool isSubscribedToBroadcast(Node *node) const; // Check if a node is subscribed to broadcast mode
|
||||
|
||||
// Data transmission
|
||||
bool post(const std::shared_ptr<Message> &message); // Post a message to the bus
|
||||
|
||||
// Monitoring
|
||||
MonitoringData getMonitoringData() const; // Get the current monitoring data of the bus (message count, frequency, etc.)
|
||||
void setMessageAge(std::chrono::microseconds age); // Set the age of the message
|
||||
|
||||
protected:
|
||||
TimePoint m_startTimestamp; // Timestamp of when the bus started
|
||||
std::jthread m_monitoringThread; // Thread for monitoring the performance of the bus
|
||||
|
||||
struct
|
||||
{
|
||||
mutable std::mutex mtx; // Mutex for thread-safe access to the nodes map
|
||||
std::unordered_map<HashID, std::vector<Node *>> nodes; // Map of message type IDs to lists of subscribed nodes
|
||||
std::set<Node *> broadcastNodes; // Set of nodes subscribed to receive all messages (broadcast mode)
|
||||
std::set<Node *> activeNodesCache; // Cache of active nodes for quick access during message routing
|
||||
} m_routingTable;
|
||||
|
||||
struct
|
||||
{
|
||||
mutable std::mutex mtx; // Mutex for thread-safe access to the monitoring data
|
||||
MonitoringData data;
|
||||
|
||||
sdi_toolBox::dateTime::Timer frequencyTimer; // Timer for calculating message frequency
|
||||
size_t messageCounter = { 0 }; // Counter for calculating message frequency
|
||||
} m_monitoring;
|
||||
|
||||
private:
|
||||
bool isSubscribedEverywhere(Node *node) const; // Check if a node is subscribed to any message type (used for managing the active nodes cache)
|
||||
void recordIncoming(); // Incoming messages for monitoring purposes
|
||||
void updateMonitoringData(); // Update the monitoring data of the bus (message count, frequency, etc.)
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
} // namespace dBus
|
||||
8
src/dBus/dBus.h
Normal file
8
src/dBus/dBus.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
//--------------------------------------------------------------
|
||||
#include "bus.h"
|
||||
#include "defs.h"
|
||||
#include "message.h"
|
||||
#include "node.h"
|
||||
//--------------------------------------------------------------
|
||||
105
src/dBus/defs.h
Normal file
105
src/dBus/defs.h
Normal file
@@ -0,0 +1,105 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <format>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace dBus
|
||||
{
|
||||
using HashID = uint32_t;
|
||||
using TimePoint = std::chrono::steady_clock::time_point;
|
||||
|
||||
constexpr auto DEFAULT_TIMEOUT = std::chrono::milliseconds(100);
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// Hash function for IDs using FNV-1a algorithm
|
||||
constexpr HashID makeID(const std::string_view id) noexcept
|
||||
{
|
||||
constexpr uint32_t FNV_offset_basis = 2166136261u;
|
||||
constexpr uint32_t FNV_prime = 16777619u;
|
||||
|
||||
uint32_t hash_value = FNV_offset_basis;
|
||||
for (const char c : id)
|
||||
{
|
||||
hash_value ^= static_cast<uint32_t>(static_cast<uint8_t>(c));
|
||||
hash_value *= FNV_prime;
|
||||
}
|
||||
return hash_value;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
template<typename T, typename = void>
|
||||
struct has_to_string : std::false_type
|
||||
{
|
||||
};
|
||||
template<typename T>
|
||||
struct has_to_string<T, std::void_t<decltype(std::to_string(std::declval<T>()))>>
|
||||
: std::true_type
|
||||
{
|
||||
};
|
||||
template<typename T>
|
||||
constexpr bool has_to_string_v = has_to_string<T>::value;
|
||||
//--------------------------------------------------------------
|
||||
enum class ReturnStatus : uint8_t
|
||||
{
|
||||
Success = 0,
|
||||
Timeout,
|
||||
NoSubscribers,
|
||||
Error,
|
||||
Exception,
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
enum class MessageCategory : uint32_t
|
||||
{
|
||||
None = 0,
|
||||
Log = 1 << 0,
|
||||
Hardware = 1 << 1,
|
||||
Dataset = 1 << 2,
|
||||
Database = 1 << 3,
|
||||
System = 1 << 4,
|
||||
UI = 1 << 5,
|
||||
All = 0xFFFFFFFF
|
||||
};
|
||||
|
||||
// Convert LogCategory to a string representation
|
||||
inline std::string logCategoryToString(const MessageCategory &cat)
|
||||
{
|
||||
static const std::unordered_map<MessageCategory, std::string> names = {
|
||||
{ MessageCategory::None, "None" },
|
||||
{ MessageCategory::Log, "Log" },
|
||||
{ MessageCategory::Hardware, "Hardware" },
|
||||
{ MessageCategory::Dataset, "Dataset" },
|
||||
{ MessageCategory::Database, "Database" },
|
||||
{ MessageCategory::System, "System" },
|
||||
{ MessageCategory::UI, "UI" },
|
||||
{ MessageCategory::All, "All" }
|
||||
};
|
||||
|
||||
const auto it = names.find(cat);
|
||||
return (it != names.end()) ? it->second : "Composite/Unknown";
|
||||
}
|
||||
|
||||
// Bitwise OR operator for LogCategory to allow combining categories
|
||||
inline MessageCategory operator|(const MessageCategory lhs, const MessageCategory rhs)
|
||||
{
|
||||
return static_cast<MessageCategory>(
|
||||
static_cast<uint32_t>(lhs) | static_cast<uint32_t>(rhs));
|
||||
}
|
||||
|
||||
// Compound assignment operator for LogCategory
|
||||
inline MessageCategory &operator|=(MessageCategory &lhs, const MessageCategory rhs)
|
||||
{
|
||||
lhs = lhs | rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
// Bitwise AND operator for LogCategory to check if a category is included in a combination
|
||||
inline bool operator&(const MessageCategory lhs, const MessageCategory rhs)
|
||||
{
|
||||
return (static_cast<uint32_t>(lhs) & static_cast<uint32_t>(rhs)) != 0;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace dBus
|
||||
125
src/dBus/message.cpp
Normal file
125
src/dBus/message.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
#include "Message.h"
|
||||
|
||||
#include "bus.h"
|
||||
#include "defs.h"
|
||||
|
||||
#include <sdi_toolBox/dateTime/age.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dBus;
|
||||
//--------------------------------------------------------------
|
||||
/* Constructor */
|
||||
Message::Message(const HashID &messageTypeID, const MessageCategory &category)
|
||||
{
|
||||
// Default initialization
|
||||
m_postedAt = std::chrono::steady_clock::now();
|
||||
|
||||
// Set the message parameters
|
||||
m_messageTypeID = messageTypeID;
|
||||
m_category = category;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Default destructor */
|
||||
Message::~Message()
|
||||
{
|
||||
// Update the age of the message (to be used when logging the message in the bus)
|
||||
if (m_bus)
|
||||
{
|
||||
const auto finalAge = getAge();
|
||||
m_bus->setMessageAge(finalAge);
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the timestamp of when the message was posted */
|
||||
TimePoint Message::getPostedAt() const
|
||||
{
|
||||
return m_postedAt;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Set the message parameters when the message is posted to the bus (to be called by the bus when the message is posted) */
|
||||
void Message::linkToBus(Bus *bus)
|
||||
{
|
||||
m_bus = bus;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the age of the message in microseconds */
|
||||
std::chrono::microseconds Message::getAge() const
|
||||
{
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const auto age = now - m_postedAt;
|
||||
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(age);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Check if the message is of a specific type */
|
||||
bool Message::isType(const HashID &eventType) const
|
||||
{
|
||||
return m_messageTypeID == eventType;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the unique identifier for the message type */
|
||||
HashID Message::getMessageTypeID() const
|
||||
{
|
||||
return m_messageTypeID;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the log category of the message */
|
||||
MessageCategory Message::getCategory() const
|
||||
{
|
||||
return m_category;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Serialize the message data to a string */
|
||||
std::string Message::serializeData() const
|
||||
{
|
||||
return "";
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get a string representation of the message for logging purposes */
|
||||
std::string Message::toString() const
|
||||
{
|
||||
const auto serializedData = serializeData();
|
||||
|
||||
// Determine the age of the message for logging purposes
|
||||
string age = "N/A";
|
||||
if (m_bus)
|
||||
{
|
||||
const auto &busStartTimestamp = m_bus->getStartTimestamp();
|
||||
const auto dt = m_postedAt - busStartTimestamp;
|
||||
age = Age(dt).toString();
|
||||
}
|
||||
|
||||
// Format message output
|
||||
auto str = std::format("[{:}] - 0x{:08x} ({:})",
|
||||
age,
|
||||
m_messageTypeID,
|
||||
logCategoryToString(m_category));
|
||||
if (!serializedData.empty())
|
||||
str += " - " + serializedData;
|
||||
return str;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get a formatted log line representation of the message for logging purposes */
|
||||
std::string Message::toLogLine() const
|
||||
{
|
||||
const auto serializedData = serializeData();
|
||||
|
||||
// Determine the age of the message for logging purposes
|
||||
std::chrono::microseconds age{ 0 };
|
||||
if (m_bus)
|
||||
{
|
||||
const auto &busStartTimestamp = m_bus->getStartTimestamp();
|
||||
const auto dt = m_postedAt - busStartTimestamp;
|
||||
age = Age(dt).getMicroseconds();
|
||||
}
|
||||
|
||||
// Format message output
|
||||
auto str = std::format("[{:}] - 0x{:08x} ({:})",
|
||||
age.count(),
|
||||
m_messageTypeID,
|
||||
logCategoryToString(m_category));
|
||||
if (!serializedData.empty())
|
||||
str += " - " + serializedData;
|
||||
return str;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
152
src/dBus/message.h
Normal file
152
src/dBus/message.h
Normal file
@@ -0,0 +1,152 @@
|
||||
#pragma once
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
#include <future>
|
||||
|
||||
namespace dBus
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class Message : public std::enable_shared_from_this<Message>
|
||||
{
|
||||
friend class Bus;
|
||||
|
||||
public:
|
||||
Message() = default; // Default constructor
|
||||
virtual ~Message(); // Default destructor
|
||||
Message(const Message &obj) = delete; // Copy constructor
|
||||
Message(Message &&obj) noexcept = delete; // Move constructor
|
||||
Message &operator=(const Message &obj) = delete; // Copy assignment operator
|
||||
Message &operator=(Message &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
explicit Message(const HashID &messageTypeID, // Constructor
|
||||
const MessageCategory &category);
|
||||
|
||||
// Message management
|
||||
TimePoint getPostedAt() const; // Get the timestamp of when the message was posted
|
||||
[[nodiscard]] std::chrono::microseconds getAge() const; // Get the age of the message in microseconds
|
||||
[[nodiscard]] bool isType(const HashID &eventType) const; // Check if the message is of a specific type
|
||||
|
||||
// Trace information
|
||||
[[nodiscard]] HashID getMessageTypeID() const; // Get the unique identifier for the message type
|
||||
[[nodiscard]] MessageCategory getCategory() const; // Get the log category of the message
|
||||
|
||||
[[nodiscard]] virtual std::string serializeData() const; // Serialize the message data to a string
|
||||
[[nodiscard]] virtual std::string toString() const; // Get a string representation of the message for logging purposes
|
||||
[[nodiscard]] virtual std::string toLogLine() const; // Get a formatted log line representation of the message for logging purposes
|
||||
|
||||
template<class Derived>
|
||||
[[nodiscard]] std::shared_ptr<Derived> as(); // Template method to cast the message to a specific derived type
|
||||
template<class Derived>
|
||||
[[nodiscard]] std::shared_ptr<const Derived> as() const; // Template method to cast the message to a specific derived type
|
||||
|
||||
protected:
|
||||
HashID m_messageTypeID = 0; // Unique identifier for the message type
|
||||
MessageCategory m_category = MessageCategory::None; // Log category of the message
|
||||
|
||||
Bus *m_bus = nullptr; // Pointer to the bus this message is posted to (to be set by the bus when the message is posted)
|
||||
TimePoint m_postedAt; // Timestamp of when the message was posted
|
||||
|
||||
private:
|
||||
void linkToBus(Bus *bus); // Set the message parameters when the message is posted to the bus
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Template method to cast the message to a specific derived type */
|
||||
template<class Derived>
|
||||
std::shared_ptr<Derived> Message::as()
|
||||
{
|
||||
return std::dynamic_pointer_cast<Derived>(shared_from_this());
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Template method to cast the message to a specific derived type */
|
||||
template<class Derived>
|
||||
std::shared_ptr<const Derived> Message::as() const
|
||||
{
|
||||
return std::dynamic_pointer_cast<const Derived>(shared_from_this());
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
|
||||
/* --- */
|
||||
|
||||
//--------------------------------------------------------------
|
||||
template<class T>
|
||||
class EventMessage : public Message
|
||||
{
|
||||
public:
|
||||
T value; // Value associated with the event message
|
||||
|
||||
EventMessage() = delete; // Default constructor
|
||||
virtual ~EventMessage() = default; // Default destructor
|
||||
EventMessage(const EventMessage &obj) = delete; // Copy constructor
|
||||
EventMessage(EventMessage &&obj) noexcept = delete; // Move constructor
|
||||
EventMessage &operator=(const EventMessage &obj) = delete; // Copy assignment operator
|
||||
EventMessage &operator=(EventMessage &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
explicit EventMessage(const HashID &messageTypeID, // Constructor
|
||||
const MessageCategory &category,
|
||||
T v);
|
||||
|
||||
[[nodiscard]] std::string serializeData() const override; // Serialize the message data to a string
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/* Constructor */
|
||||
template<class T>
|
||||
EventMessage<T>::EventMessage(const HashID &messageTypeID, const MessageCategory &category, T v)
|
||||
: Message(messageTypeID, category)
|
||||
, value(std::move(v))
|
||||
{
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Serialize the message data to a string */
|
||||
template<class T>
|
||||
std::string EventMessage<T>::serializeData() const
|
||||
{
|
||||
if constexpr (std::is_arithmetic_v<T>)
|
||||
return std::to_string(value);
|
||||
return "";
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
|
||||
/* --- */
|
||||
|
||||
//--------------------------------------------------------------
|
||||
template<class T, class U>
|
||||
class RequestMessage : public Message
|
||||
{
|
||||
public:
|
||||
T value; // Value associated with the event message
|
||||
std::promise<U> responsePromise; // Promise to be fulfilled with the response data once the request is processed
|
||||
|
||||
RequestMessage() = delete; // Default constructor
|
||||
virtual ~RequestMessage() = default; // Default destructor
|
||||
RequestMessage(const RequestMessage &obj) = delete; // Copy constructor
|
||||
RequestMessage(RequestMessage &&obj) noexcept = delete; // Move constructor
|
||||
RequestMessage &operator=(const RequestMessage &obj) = delete; // Copy assignment operator
|
||||
RequestMessage &operator=(RequestMessage &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
explicit RequestMessage(const HashID &messageTypeID, // Constructor
|
||||
const MessageCategory &category,
|
||||
T v,
|
||||
std::shared_ptr<U> p);
|
||||
|
||||
std::future<U> getFuture(); // Get the future associated with the response promise
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Constructor */
|
||||
template<class T, class U>
|
||||
RequestMessage<T, U>::RequestMessage(const HashID &messageTypeID, const MessageCategory &category, T v, std::shared_ptr<U> p)
|
||||
: Message(messageTypeID, category)
|
||||
, value(std::move(v))
|
||||
, responsePromise(std::promise<U>{})
|
||||
{
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the future associated with the response promise */
|
||||
template<class T, class U>
|
||||
std::future<U> RequestMessage<T, U>::getFuture()
|
||||
{
|
||||
return std::move(responsePromise.get_future());
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace dBus
|
||||
121
src/dBus/node.cpp
Normal file
121
src/dBus/node.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
#include "node.h"
|
||||
|
||||
#include "bus.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace dBus;
|
||||
//--------------------------------------------------------------
|
||||
/* Constructor */
|
||||
Node::Node(Bus &bus)
|
||||
: m_bus(bus)
|
||||
{
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Default destructor */
|
||||
Node::~Node()
|
||||
{
|
||||
unsubscribeAll();
|
||||
|
||||
// Notify the node thread to stop waiting for messages and exit the loop (if applicable)
|
||||
notifyMessageQueue();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Subscribe to a specific message type */
|
||||
void Node::subscribe(const HashID &eventType)
|
||||
{
|
||||
m_bus.subscribe(eventType, this);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Unsubscribe from a specific message type */
|
||||
void Node::unsubscribe(const HashID &eventType)
|
||||
{
|
||||
m_bus.unsubscribe(eventType, this);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Unsubscribe from all message types */
|
||||
void Node::unsubscribeAll()
|
||||
{
|
||||
m_bus.unsubscribeAll(this);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Check if is subscribed to a specific message type */
|
||||
bool Node::isSubscribed(const HashID &eventType) const
|
||||
{
|
||||
return m_bus.isSubscribed(eventType, this);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Wait for a message to be received */
|
||||
void Node::syncWaitForMessage()
|
||||
{
|
||||
// Check if there are already messages in the queue before waiting
|
||||
// to avoid missing notifications if a message is received between
|
||||
// the last check and the wait call
|
||||
{
|
||||
std::scoped_lock lock(m_dBusMessages.mtx);
|
||||
if (!m_dBusMessages.messageQueue.empty())
|
||||
return; // Messages already in the queue, no need to wait
|
||||
}
|
||||
|
||||
// Wait until a message is received (the bus will call notifyMessageFromBus()
|
||||
// to wake up the node thread when a message is posted)
|
||||
m_nodeWaitFlag = false;
|
||||
m_nodeWaitFlag.wait(false);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Post a message to the bus */
|
||||
void Node::postMessage(const std::shared_ptr<Message> &message) const
|
||||
{
|
||||
m_bus.post(message);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the maximum age of messages in the queue */
|
||||
std::chrono::microseconds Node::getMessageMaxAge() const
|
||||
{
|
||||
std::scoped_lock lock(m_dBusMessages.mtx);
|
||||
|
||||
// If the queue is empty, return a default value (e.g., zero)
|
||||
if (m_dBusMessages.messageQueue.empty())
|
||||
return {};
|
||||
|
||||
// Get the age of the oldest message in the queue
|
||||
// We can assume that the messages are ordered by age, so we can check the front of the queue
|
||||
const auto &oldestMessage = m_dBusMessages.messageQueue.front();
|
||||
return oldestMessage->getAge();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the number of received messages */
|
||||
size_t Node::getMessageCount() const
|
||||
{
|
||||
std::scoped_lock lock(m_dBusMessages.mtx);
|
||||
return m_dBusMessages.messageQueue.size();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the next message from the queue */
|
||||
std::shared_ptr<Message> Node::popNextMessage()
|
||||
{
|
||||
std::scoped_lock lock(m_dBusMessages.mtx);
|
||||
if (m_dBusMessages.messageQueue.empty())
|
||||
return nullptr;
|
||||
|
||||
auto event = m_dBusMessages.messageQueue.front();
|
||||
m_dBusMessages.messageQueue.pop();
|
||||
|
||||
return event;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Receive a message from the bus */
|
||||
void Node::receiveMessageFromBus(std::shared_ptr<Message> message)
|
||||
{
|
||||
std::scoped_lock lock(m_dBusMessages.mtx);
|
||||
m_dBusMessages.messageQueue.push(std::move(message));
|
||||
|
||||
notifyMessageQueue();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Notify the node of a new message from the bus */
|
||||
void Node::notifyMessageQueue()
|
||||
{
|
||||
m_nodeWaitFlag.store(true);
|
||||
m_nodeWaitFlag.notify_one();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
57
src/dBus/node.h
Normal file
57
src/dBus/node.h
Normal file
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include "Message.h"
|
||||
#include "defs.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
|
||||
namespace dBus
|
||||
{
|
||||
class Bus;
|
||||
//--------------------------------------------------------------
|
||||
class Node
|
||||
{
|
||||
friend class Bus;
|
||||
|
||||
public:
|
||||
Node() = delete; // Default constructor
|
||||
virtual ~Node(); // Default destructor
|
||||
Node(const Node &obj) = delete; // Copy constructor
|
||||
Node(Node &&obj) noexcept = delete; // Move constructor
|
||||
Node &operator=(const Node &obj) = delete; // Copy assignment operator
|
||||
Node &operator=(Node &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
explicit Node(Bus &bus); // Constructor
|
||||
|
||||
// Event subscription management
|
||||
void subscribe(const HashID &eventType); // Subscribe to a specific message type
|
||||
void unsubscribe(const HashID &eventType); // Unsubscribe from a specific message type
|
||||
void unsubscribeAll(); // Unsubscribe from all message types
|
||||
[[nodiscard]] bool isSubscribed(const HashID &eventType) const; // Check if is subscribed to a specific message type
|
||||
|
||||
void syncWaitForMessage(); // Wait for a message to be received
|
||||
|
||||
// Data transmission
|
||||
void postMessage(const std::shared_ptr<Message> &message) const; // Post a message to the bus
|
||||
|
||||
[[nodiscard]] std::chrono::microseconds getMessageMaxAge() const; // Get the maximum age of messages in the queue
|
||||
[[nodiscard]] size_t getMessageCount() const; // Get the number of received messages
|
||||
[[nodiscard]] std::shared_ptr<Message> popNextMessage(); // Get the next message from the queue
|
||||
|
||||
protected:
|
||||
virtual void receiveMessageFromBus(std::shared_ptr<Message> message); // Receive a message from the bus (to be called by the bus when a message is posted)
|
||||
void notifyMessageQueue(); // Notify the node of a new message from the bus
|
||||
|
||||
Bus &m_bus; // Reference to the bus this node is connected to
|
||||
|
||||
std::atomic_bool m_nodeWaitFlag = false; // Flag to control the node thread loop
|
||||
|
||||
struct
|
||||
{
|
||||
mutable std::mutex mtx; // Mutex for thread-safe access to the event queue
|
||||
std::queue<std::shared_ptr<Message>> messageQueue; // Queue of received events
|
||||
} m_dBusMessages;
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
} // namespace dBus
|
||||
39
src/gui/bars/menuBar.cpp
Normal file
39
src/gui/bars/menuBar.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "menuBar.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace gui;
|
||||
//--------------------------------------------------------------
|
||||
MenuBar::MenuBar(wxFrame *parent)
|
||||
{
|
||||
// Initialization
|
||||
parent->SetMenuBar(this);
|
||||
|
||||
// Creating menus
|
||||
createMenus();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Create menus */
|
||||
void MenuBar::createMenus()
|
||||
{
|
||||
// File menu
|
||||
const auto fileMenu = new wxMenu();
|
||||
fileMenu->Append(static_cast<int>(IDs::ID_MENU_FILE_NEW), _("New\tCTRL+N"), _("Create a new project"));
|
||||
fileMenu->Append(static_cast<int>(IDs::ID_MENU_FILE_OPEN), _("Open...\tCTRL+O"), _("Open a project file"));
|
||||
fileMenu->Append(static_cast<int>(IDs::ID_MENU_FILE_SAVE), _("Save\tCTRL+S"), _("Save the project file"));
|
||||
fileMenu->Append(static_cast<int>(IDs::ID_MENU_FILE_SAVE_AS), _("Save as...\tCTRL+SHIFT+S"), _("Save the project to a new file"));
|
||||
fileMenu->Append(static_cast<int>(IDs::ID_MENU_FILE_CLOSE), _("Close\tCTRL+F4"), _("Close the project"));
|
||||
fileMenu->AppendSeparator();
|
||||
fileMenu->Append(static_cast<int>(IDs::ID_MENU_FILE_QUIT), _("Quit\tALT+F4"), _("Quit the application"));
|
||||
Append(fileMenu, _("File"));
|
||||
|
||||
// Requirements menu
|
||||
const auto reqMenu = new wxMenu();
|
||||
reqMenu->Append(static_cast<int>(IDs::ID_MENU_REQ_CREATE), _("Create Requirements\tCTRL+R"), _("Create a new requirement"));
|
||||
Append(reqMenu, _("Requirements"));
|
||||
|
||||
// Help menu
|
||||
const auto helpMenu = new wxMenu();
|
||||
helpMenu->Append(static_cast<int>(IDs::ID_MENU_HELP_ABOUT), _("About\tF1"), _("Show info about the application"));
|
||||
Append(helpMenu, _("Help"));
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
41
src/gui/bars/menuBar.h
Normal file
41
src/gui/bars/menuBar.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
namespace gui
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class MenuBar : public wxMenuBar
|
||||
{
|
||||
public:
|
||||
enum class IDs : uint16_t
|
||||
{
|
||||
// File menu
|
||||
ID_MENU_FILE_NEW = wxID_HIGHEST + 1, // File->New project
|
||||
ID_MENU_FILE_OPEN, // File->Open project
|
||||
ID_MENU_FILE_SAVE, // File->Save project
|
||||
ID_MENU_FILE_SAVE_AS, // File->Save project as
|
||||
ID_MENU_FILE_CLOSE, // File->Close project
|
||||
ID_MENU_FILE_QUIT, // File->Quit
|
||||
|
||||
// Requirements menu
|
||||
ID_MENU_REQ_CREATE, // Requirements->Add
|
||||
|
||||
// Help menu
|
||||
ID_MENU_HELP_ABOUT, // Help->About
|
||||
};
|
||||
|
||||
public:
|
||||
MenuBar() = delete; // Default constructor
|
||||
explicit MenuBar(wxFrame *parent); // Constructor
|
||||
virtual ~MenuBar() = default; // Default destructor
|
||||
MenuBar(MenuBar &obj) = delete; // Copy constructor
|
||||
MenuBar(MenuBar &&obj) = delete; // Move constructor
|
||||
MenuBar &operator=(const MenuBar &obj) = delete; // Copy assignment operator
|
||||
MenuBar &operator=(MenuBar &&obj) = delete; // Move assignment operator
|
||||
|
||||
private:
|
||||
void createMenus(); // Create menus
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
} // namespace gui
|
||||
11
src/gui/bars/statusBar.cpp
Normal file
11
src/gui/bars/statusBar.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "statusBar.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace gui;
|
||||
//--------------------------------------------------------------
|
||||
StatusBar::StatusBar(wxFrame *parent)
|
||||
: wxStatusBar(parent)
|
||||
{
|
||||
parent->SetStatusBar(this);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
20
src/gui/bars/statusBar.h
Normal file
20
src/gui/bars/statusBar.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
namespace gui
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class StatusBar : public wxStatusBar
|
||||
{
|
||||
public:
|
||||
StatusBar() = delete; // Default constructor
|
||||
explicit StatusBar(wxFrame *parent); // Constructor
|
||||
virtual ~StatusBar() = default; // Default destructor
|
||||
StatusBar(StatusBar &obj) = delete; // Copy constructor
|
||||
StatusBar(StatusBar &&obj) = delete; // Move constructor
|
||||
StatusBar &operator=(const StatusBar &obj) = delete; // Copy assignment operator
|
||||
StatusBar &operator=(StatusBar &&obj) = delete; // Move assignment operator
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
} // namespace gui
|
||||
23
src/gui/bars/toolBar.cpp
Normal file
23
src/gui/bars/toolBar.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "toolBar.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace gui;
|
||||
//--------------------------------------------------------------
|
||||
ToolBar::ToolBar(wxWindow *parent)
|
||||
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER)
|
||||
{
|
||||
// Initialization
|
||||
const auto minSize = wxSize(-1, 64);
|
||||
SetMinSize(minSize);
|
||||
SetSize(minSize);
|
||||
|
||||
// Creating menus
|
||||
createTools();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Create tools */
|
||||
void ToolBar::createTools()
|
||||
{
|
||||
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
23
src/gui/bars/toolBar.h
Normal file
23
src/gui/bars/toolBar.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
namespace gui
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class ToolBar : public wxPanel
|
||||
{
|
||||
public:
|
||||
ToolBar() = delete; // Default constructor
|
||||
explicit ToolBar(wxWindow *parent); // Constructor
|
||||
virtual ~ToolBar() = default; // Default destructor
|
||||
ToolBar(ToolBar &obj) = delete; // Copy constructor
|
||||
ToolBar(ToolBar &&obj) = delete; // Move constructor
|
||||
ToolBar &operator=(const ToolBar &obj) = delete; // Copy assignment operator
|
||||
ToolBar &operator=(ToolBar &&obj) = delete; // Move assignment operator
|
||||
|
||||
private:
|
||||
void createTools(); // Create tools
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
} // namespace gui
|
||||
151
src/gui/mainFrame.cpp
Normal file
151
src/gui/mainFrame.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
#include "mainFrame.h"
|
||||
|
||||
#include "bars/menuBar.h"
|
||||
#include "bars/statusBar.h"
|
||||
#include "bars/toolBar.h"
|
||||
|
||||
/* --- */
|
||||
#include "api/requirement.h"
|
||||
/* --- */
|
||||
|
||||
using namespace std;
|
||||
using namespace gui;
|
||||
//--------------------------------------------------------------
|
||||
/* Constructor */
|
||||
MainFrame::MainFrame(dBus::Bus &bus)
|
||||
: wxFrame(nullptr, wxID_ANY, _("kwa.Fr"), wxDefaultPosition, wxDefaultSize)
|
||||
, m_bus(bus)
|
||||
{
|
||||
// Initialization
|
||||
SetIcon(wxICON(AAAA_ICON));
|
||||
|
||||
const wxSize minSize(1200, 900);
|
||||
SetMinSize(minSize);
|
||||
SetSize(minSize);
|
||||
|
||||
// Creating controls
|
||||
createControls();
|
||||
|
||||
// Post-initialization
|
||||
m_menuBar->Bind(wxEVT_MENU, &MainFrame::on_menubarItemClick, this);
|
||||
|
||||
CenterOnScreen();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Creating controls */
|
||||
void MainFrame::createControls()
|
||||
{
|
||||
m_menuBar = new MenuBar(this);
|
||||
m_statusBar = new StatusBar(this);
|
||||
const auto framePanel = new wxPanel(this /*, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER*/);
|
||||
m_toolBar = new ToolBar(framePanel);
|
||||
|
||||
m_pageNotebook = new wxNotebook(framePanel, wxID_ANY);
|
||||
|
||||
// Controls positioning
|
||||
const auto mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
mainSizer->Add(m_toolBar, wxSizerFlags(0).Expand());
|
||||
mainSizer->Add(m_pageNotebook, wxSizerFlags(1).Expand().Border(wxALL, 5));
|
||||
|
||||
framePanel->SetSizer(mainSizer);
|
||||
Layout();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Create a new project */
|
||||
void MainFrame::createProject()
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto ret = postFileOperationRequest(m_bus, api::requirement::FileOperation::Create);
|
||||
if (!ret)
|
||||
{
|
||||
wxMessageBox("No manager found for project creation request.\n\n"
|
||||
"The manager responsible for handling project creation "
|
||||
"did not respond to the request. Please ensure that "
|
||||
"the project management component is running and try again.",
|
||||
"Failed...",
|
||||
wxOK | wxICON_WARNING,
|
||||
this);
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
wxMessageBox("An unexpected error occurred while creating a new project\n\n" + wxString(e.what()),
|
||||
"Critical Error",
|
||||
wxOK | wxICON_ERROR,
|
||||
this);
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Open an existing project */
|
||||
void MainFrame::openProject()
|
||||
{
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Save the current project */
|
||||
void MainFrame::saveProject()
|
||||
{
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Save the current project as... */
|
||||
void MainFrame::saveProjectAs()
|
||||
{
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Close the current project */
|
||||
void MainFrame::closeProject() const
|
||||
{
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Menu bar item click event */
|
||||
void MainFrame::on_menubarItemClick(wxCommandEvent &event)
|
||||
{
|
||||
switch (static_cast<MenuBar::IDs>(event.GetId()))
|
||||
{
|
||||
using enum MenuBar::IDs;
|
||||
// File menu
|
||||
case ID_MENU_FILE_NEW:
|
||||
{
|
||||
createProject();
|
||||
break;
|
||||
}
|
||||
case ID_MENU_FILE_OPEN:
|
||||
{
|
||||
openProject();
|
||||
break;
|
||||
}
|
||||
case ID_MENU_FILE_SAVE:
|
||||
{
|
||||
saveProject();
|
||||
break;
|
||||
}
|
||||
case ID_MENU_FILE_SAVE_AS:
|
||||
{
|
||||
saveProjectAs();
|
||||
break;
|
||||
}
|
||||
case ID_MENU_FILE_CLOSE:
|
||||
{
|
||||
closeProject();
|
||||
break;
|
||||
}
|
||||
case ID_MENU_FILE_QUIT:
|
||||
{
|
||||
Close();
|
||||
break;
|
||||
}
|
||||
|
||||
// Requirement menu
|
||||
case ID_MENU_REQ_CREATE:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Help menu
|
||||
|
||||
default:
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
48
src/gui/mainFrame.h
Normal file
48
src/gui/mainFrame.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <wx/notebook.h>
|
||||
#include <wx/wx.h>
|
||||
#include <dBus/dBus.h>
|
||||
|
||||
namespace gui
|
||||
{
|
||||
class MenuBar;
|
||||
class StatusBar;
|
||||
class ToolBar;
|
||||
//--------------------------------------------------------------
|
||||
class MainFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
MainFrame() = delete; // Default constructor
|
||||
virtual ~MainFrame() = default; // Default destructor
|
||||
MainFrame(MainFrame &obj) = delete; // Copy constructor
|
||||
MainFrame(MainFrame &&obj) noexcept = delete; // Move constructor
|
||||
MainFrame &operator=(const MainFrame &obj) = delete; // Copy assignment operator
|
||||
MainFrame &operator=(MainFrame &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
explicit MainFrame(dBus::Bus &bus); // Constructor
|
||||
|
||||
protected:
|
||||
dBus::Bus &m_bus; // Reference to the application bus
|
||||
|
||||
MenuBar *m_menuBar = nullptr; // Menu bar handle
|
||||
StatusBar *m_statusBar = nullptr; // Status bar handle
|
||||
ToolBar *m_toolBar = nullptr; // Tool bar handle
|
||||
wxNotebook *m_pageNotebook = nullptr; // Main notebook handle
|
||||
|
||||
private:
|
||||
void createControls(); // Creating controls
|
||||
|
||||
void createProject(); // Create a new project
|
||||
void openProject(); // Open an existing project
|
||||
void saveProject(); // Save the current project
|
||||
void saveProjectAs(); // Save the current project as...
|
||||
void closeProject() const; // Close the current project
|
||||
|
||||
// Events
|
||||
public:
|
||||
void on_menubarItemClick(wxCommandEvent &event); // Menu bar item click event
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
} // namespace gui
|
||||
BIN
src/kwaFr.rc
Normal file
BIN
src/kwaFr.rc
Normal file
Binary file not shown.
50
src/kwaFr_app.cpp
Normal file
50
src/kwaFr_app.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "kwaFr_app.h"
|
||||
|
||||
#include "gui/mainFrame.h"
|
||||
#include "misc/logConsoleGUI/logConsoleGUI.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <wx/image.h>
|
||||
|
||||
wxIMPLEMENT_APP(kwaFr_app);
|
||||
using namespace std;
|
||||
//--------------------------------------------------------------
|
||||
/* Default destructor */
|
||||
kwaFr_app::~kwaFr_app()
|
||||
{
|
||||
// Clean up the application context
|
||||
m_coreManager.reset();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
bool kwaFr_app::OnInit()
|
||||
{
|
||||
// Initialize the application context
|
||||
m_coreManager = make_unique<core::CoreManager>();
|
||||
|
||||
// Initialize console
|
||||
logConsole = make_unique<sdiTools::logConsoleGUI>();
|
||||
#ifdef DEBUG
|
||||
constexpr auto attachOnly = false;
|
||||
// logConsole->initConsole();
|
||||
#else
|
||||
constexpr auto attachOnly = true;
|
||||
#endif
|
||||
logConsole->initConsole(attachOnly);
|
||||
|
||||
// wxWidgets configuration
|
||||
wxInitAllImageHandlers();
|
||||
|
||||
// Creating the main frame
|
||||
const auto frame = new gui::MainFrame(m_coreManager->getBus());
|
||||
frame->Show();
|
||||
|
||||
api::log::postLogMessage(m_coreManager->getBus(), "Application started");
|
||||
return true;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
int kwaFr_app::OnExit()
|
||||
{
|
||||
// Application exit
|
||||
return wxApp::OnExit();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
25
src/kwaFr_app.h
Normal file
25
src/kwaFr_app.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/coreManager.h"
|
||||
|
||||
#include <memory>
|
||||
#include <wx/app.h>
|
||||
|
||||
//--------------------------------------------------------------
|
||||
class kwaFr_app : public wxApp
|
||||
{
|
||||
public:
|
||||
kwaFr_app() = default; // Default constructor
|
||||
virtual ~kwaFr_app(); // Default destructor
|
||||
kwaFr_app(const kwaFr_app &obj) = delete; // Copy constructor
|
||||
kwaFr_app(kwaFr_app &&obj) noexcept = delete; // Move constructor
|
||||
kwaFr_app &operator=(const kwaFr_app &obj) = delete; // Copy assignment operator
|
||||
kwaFr_app &operator=(kwaFr_app &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
bool OnInit() override;
|
||||
int OnExit() override;
|
||||
|
||||
protected:
|
||||
std::unique_ptr<core::CoreManager> m_coreManager; // Core manager instance
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
15
src/misc/json/json.h
Normal file
15
src/misc/json/json.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
//--------------------------------------------------------------
|
||||
#include "misc/json/nlohmann/json.hpp"
|
||||
//--------------------------------------------------------------
|
||||
using json = nlohmann::ordered_json;
|
||||
//--------------------------------------------------------------
|
||||
template<class T>
|
||||
inline T json_readData(const json &jsonData, const std::string &key, const T &defaultValue = {})
|
||||
{
|
||||
if (jsonData.contains(key))
|
||||
return jsonData.at(key).get<T>();
|
||||
return defaultValue;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
24765
src/misc/json/nlohmann/json.hpp
Normal file
24765
src/misc/json/nlohmann/json.hpp
Normal file
File diff suppressed because it is too large
Load Diff
176
src/misc/json/nlohmann/json_fwd.hpp
Normal file
176
src/misc/json/nlohmann/json_fwd.hpp
Normal file
@@ -0,0 +1,176 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
||||
#define INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
||||
|
||||
#include <cstdint> // int64_t, uint64_t
|
||||
#include <map> // map
|
||||
#include <memory> // allocator
|
||||
#include <string> // string
|
||||
#include <vector> // vector
|
||||
|
||||
// #include <nlohmann/detail/abi_macros.hpp>
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
|
||||
|
||||
// This file contains all macro definitions affecting or depending on the ABI
|
||||
|
||||
#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK
|
||||
#if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH)
|
||||
#if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 3
|
||||
#warning "Already included a different version of the library!"
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum)
|
||||
#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum)
|
||||
#define NLOHMANN_JSON_VERSION_PATCH 3 // NOLINT(modernize-macro-to-enum)
|
||||
|
||||
#ifndef JSON_DIAGNOSTICS
|
||||
#define JSON_DIAGNOSTICS 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
#define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
|
||||
#endif
|
||||
|
||||
#if JSON_DIAGNOSTICS
|
||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||
#else
|
||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS
|
||||
#endif
|
||||
|
||||
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp
|
||||
#else
|
||||
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
#endif
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
||||
#endif
|
||||
|
||||
// Construct the namespace ABI tags component
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b)
|
||||
|
||||
#define NLOHMANN_JSON_ABI_TAGS \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||
NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
|
||||
NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON)
|
||||
|
||||
// Construct the namespace version component
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
||||
_v ## major ## _ ## minor ## _ ## patch
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \
|
||||
NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch)
|
||||
|
||||
#if NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION
|
||||
#else
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION \
|
||||
NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \
|
||||
NLOHMANN_JSON_VERSION_MINOR, \
|
||||
NLOHMANN_JSON_VERSION_PATCH)
|
||||
#endif
|
||||
|
||||
// Combine namespace components
|
||||
#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b
|
||||
#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \
|
||||
NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b)
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE
|
||||
#define NLOHMANN_JSON_NAMESPACE \
|
||||
nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \
|
||||
NLOHMANN_JSON_ABI_TAGS, \
|
||||
NLOHMANN_JSON_NAMESPACE_VERSION)
|
||||
#endif
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
#define NLOHMANN_JSON_NAMESPACE_BEGIN \
|
||||
namespace nlohmann \
|
||||
{ \
|
||||
inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \
|
||||
NLOHMANN_JSON_ABI_TAGS, \
|
||||
NLOHMANN_JSON_NAMESPACE_VERSION) \
|
||||
{
|
||||
#endif
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE_END
|
||||
#define NLOHMANN_JSON_NAMESPACE_END \
|
||||
} /* namespace (inline namespace) NOLINT(readability/namespace) */ \
|
||||
} // namespace nlohmann
|
||||
#endif
|
||||
|
||||
|
||||
/*!
|
||||
@brief namespace for Niels Lohmann
|
||||
@see https://github.com/nlohmann
|
||||
@since version 1.0.0
|
||||
*/
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
/*!
|
||||
@brief default JSONSerializer template argument
|
||||
|
||||
This serializer ignores the template arguments and uses ADL
|
||||
([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
|
||||
for serialization.
|
||||
*/
|
||||
template<typename T = void, typename SFINAE = void>
|
||||
struct adl_serializer;
|
||||
|
||||
/// a class to store JSON values
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/
|
||||
template<template<typename U, typename V, typename... Args> class ObjectType =
|
||||
std::map,
|
||||
template<typename U, typename... Args> class ArrayType = std::vector,
|
||||
class StringType = std::string, class BooleanType = bool,
|
||||
class NumberIntegerType = std::int64_t,
|
||||
class NumberUnsignedType = std::uint64_t,
|
||||
class NumberFloatType = double,
|
||||
template<typename U> class AllocatorType = std::allocator,
|
||||
template<typename T, typename SFINAE = void> class JSONSerializer =
|
||||
adl_serializer,
|
||||
class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
|
||||
class CustomBaseClass = void>
|
||||
class basic_json;
|
||||
|
||||
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/
|
||||
template<typename RefStringType>
|
||||
class json_pointer;
|
||||
|
||||
/*!
|
||||
@brief default specialization
|
||||
@sa https://json.nlohmann.me/api/json/
|
||||
*/
|
||||
using json = basic_json<>;
|
||||
|
||||
/// @brief a minimal map-like container that preserves insertion order
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/
|
||||
template<class Key, class T, class IgnoredLess, class Allocator>
|
||||
struct ordered_map;
|
||||
|
||||
/// @brief specialization that maintains the insertion order of object keys
|
||||
/// @sa https://json.nlohmann.me/api/ordered_json/
|
||||
using ordered_json = basic_json<nlohmann::ordered_map>;
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
||||
295
src/misc/logConsoleGUI/logConsoleGUI.cpp
Normal file
295
src/misc/logConsoleGUI/logConsoleGUI.cpp
Normal file
@@ -0,0 +1,295 @@
|
||||
/* --- logConsoleGUI.cpp ---------------------------------------
|
||||
System: Log system for gui applications
|
||||
Status: Version 1.0 Release 2
|
||||
Language: C++
|
||||
|
||||
(c) Copyright SD-Innovation 2016
|
||||
|
||||
Author: Sylvain Schneider
|
||||
------------------------------------------------------------- */
|
||||
|
||||
#include "logConsoleGUI.h"
|
||||
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <Windows.h>
|
||||
#endif // defined WIN32
|
||||
|
||||
t_logConsoleGUI logConsole;
|
||||
|
||||
using namespace std;
|
||||
using namespace sdiTools;
|
||||
//--------------------------------------------------------------
|
||||
logConsoleGUI::logConsoleGUI(const std::string &logFilepath)
|
||||
{
|
||||
// Constructor
|
||||
if (!logFilepath.empty())
|
||||
openLogFile(logFilepath);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
logConsoleGUI::~logConsoleGUI()
|
||||
{
|
||||
// Destructor
|
||||
releaseConsole();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
void logConsoleGUI::openLogFile(const std::string &logFilepath)
|
||||
{
|
||||
// Open log file
|
||||
m_logFile.open(logFilepath.c_str(), std::ofstream::out | std::ofstream::app);
|
||||
if (!m_logFile.is_open())
|
||||
throw logic_error("Unable to open log file !");
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
void logConsoleGUI::closeLogFile()
|
||||
{
|
||||
// Close log file
|
||||
if (m_logFile.is_open())
|
||||
m_logFile.close();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
int64_t logConsoleGUI::getLogFileSize()
|
||||
{
|
||||
// Return opened log file size
|
||||
if (!m_logFile.is_open())
|
||||
return -1;
|
||||
return m_logFile.tellp();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
void logConsoleGUI::initConsole(bool attachOnly)
|
||||
{
|
||||
// Init application console and attach debug console under MSW
|
||||
#ifdef _WIN32
|
||||
|
||||
if (hasAttachedConsole())
|
||||
return;
|
||||
|
||||
bool hasParentConsole = true;
|
||||
if (!AttachConsole(ATTACH_PARENT_PROCESS)) // Attach application to console
|
||||
{
|
||||
if (attachOnly)
|
||||
return;
|
||||
|
||||
// No console was available
|
||||
hasParentConsole = false;
|
||||
if (!AllocConsole()) // Create console and attach application to it
|
||||
{
|
||||
// Error during creating console
|
||||
throw logic_error("Unable to attach application to debug console");
|
||||
}
|
||||
}
|
||||
|
||||
int err;
|
||||
err = freopen_s(&m_stdoutFile, "CONOUT$", "w", stdout);
|
||||
if (err != 0)
|
||||
{
|
||||
// Error during reopen on stdout
|
||||
throw logic_error("Unable to reopen stdout");
|
||||
}
|
||||
err = freopen_s(&m_stderrFile, "CONOUT$", "w", stderr);
|
||||
if (err != 0)
|
||||
{
|
||||
// Error during reopen on stderr
|
||||
throw logic_error("Unable to reopen stderr");
|
||||
}
|
||||
|
||||
cout.clear();
|
||||
cerr.clear();
|
||||
|
||||
if (hasParentConsole)
|
||||
{
|
||||
cout << endl;
|
||||
}
|
||||
// cout << "logConsoleGUI is ready !" << endl;
|
||||
|
||||
#endif // defined WIN32
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
void logConsoleGUI::releaseConsole()
|
||||
{
|
||||
// Release application console
|
||||
#ifdef _WIN32
|
||||
|
||||
if (!hasAttachedConsole())
|
||||
return;
|
||||
|
||||
cout << "logConsoleGUI is terminated !" << endl;
|
||||
cout.clear();
|
||||
cerr.clear();
|
||||
|
||||
FreeConsole();
|
||||
|
||||
fclose(m_stdoutFile);
|
||||
fclose(m_stderrFile);
|
||||
|
||||
#endif // defined WIN32
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
bool logConsoleGUI::hasAttachedConsole()
|
||||
{
|
||||
// Returns true if console was attached, false otherwise
|
||||
#ifdef _WIN32
|
||||
|
||||
if (GetConsoleWindow())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
#endif // defined WIN32
|
||||
return false;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
void logConsoleGUI::disable_quickEditMode()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||
if (hInput == INVALID_HANDLE_VALUE || hInput == NULL)
|
||||
{
|
||||
DWORD errorCode = GetLastError();
|
||||
throw runtime_error("Unable to get input console handle");
|
||||
}
|
||||
|
||||
BOOL bRet;
|
||||
DWORD consoleMode;
|
||||
|
||||
bRet = GetConsoleMode(hInput, &consoleMode);
|
||||
if (bRet == 0)
|
||||
{
|
||||
DWORD errorCode = GetLastError();
|
||||
throw runtime_error("Unable to get console mode");
|
||||
}
|
||||
|
||||
consoleMode = ENABLE_EXTENDED_FLAGS | (consoleMode & ~ENABLE_QUICK_EDIT_MODE);
|
||||
|
||||
bRet = SetConsoleMode(hInput, consoleMode);
|
||||
if (bRet == 0)
|
||||
{
|
||||
// DWORD errorCode = GetLastError();
|
||||
throw runtime_error("Unable to set console mode");
|
||||
}
|
||||
|
||||
// Disable cursor
|
||||
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hOutput == INVALID_HANDLE_VALUE || hOutput == NULL)
|
||||
{
|
||||
DWORD errorCode = GetLastError();
|
||||
throw runtime_error("Unable to get output console handle");
|
||||
}
|
||||
|
||||
CONSOLE_CURSOR_INFO cursorInfo;
|
||||
bRet = GetConsoleCursorInfo(hOutput, &cursorInfo);
|
||||
if (bRet == 0)
|
||||
{
|
||||
throw runtime_error("Unable to get console cursor info");
|
||||
}
|
||||
|
||||
cursorInfo.bVisible = false; // set the cursor visibility
|
||||
bRet = SetConsoleCursorInfo(hOutput, &cursorInfo);
|
||||
if (bRet == 0)
|
||||
{
|
||||
throw runtime_error("Unable to set console cursor info");
|
||||
}
|
||||
|
||||
#endif // defined WIN32
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
void logConsoleGUI::alwaysOnTop()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
HWND hWin = GetConsoleWindow();
|
||||
if (!hWin)
|
||||
throw runtime_error("Unable to get window handle");
|
||||
|
||||
BOOL bRet;
|
||||
|
||||
bRet = SetWindowPos(
|
||||
hWin, // window handle
|
||||
HWND_TOPMOST, // "handle to the window to precede
|
||||
// the positioned window in the Z order
|
||||
// OR one of the following:"
|
||||
// HWND_BOTTOM or HWND_NOTOPMOST or HWND_TOP or HWND_TOPMOST
|
||||
0,
|
||||
80, // X, Y position of the window (in client coordinates)
|
||||
0,
|
||||
0, // cx, cy => width & height of the window in pixels
|
||||
SWP_DRAWFRAME | /* SWP_NOMOVE | */ SWP_NOSIZE | SWP_SHOWWINDOW // The window sizing and positioning flags.
|
||||
);
|
||||
if (bRet == 0)
|
||||
{
|
||||
DWORD errorCode = GetLastError();
|
||||
throw runtime_error("Unable to change window mode");
|
||||
}
|
||||
|
||||
#endif // defined WIN32
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
void logConsoleGUI::log(const std::string &msg, e_logLevel level)
|
||||
{
|
||||
// Log message into console and log file
|
||||
string levelStr = "";
|
||||
switch (level)
|
||||
{
|
||||
case level_DEBUG_COMMUNICATION:
|
||||
#ifndef DEBUG
|
||||
return;
|
||||
#endif
|
||||
levelStr = "[DEBUG_COMM] ";
|
||||
break;
|
||||
case level_DEBUG:
|
||||
#ifndef DEBUG
|
||||
return;
|
||||
#endif
|
||||
levelStr = "[DEBUG] ";
|
||||
break;
|
||||
case level_INFORMATIONS:
|
||||
break;
|
||||
case level_NOTICE:
|
||||
levelStr = "[NOTICE] ";
|
||||
break;
|
||||
case level_WARNING:
|
||||
levelStr = "[WARNING] ";
|
||||
break;
|
||||
case level_ERROR:
|
||||
levelStr = "[ERROR] ";
|
||||
break;
|
||||
case level_CRITICAL:
|
||||
levelStr = "[CRITICAL] ";
|
||||
break;
|
||||
case level_ALERT:
|
||||
levelStr = "[ALERT] ";
|
||||
break;
|
||||
case level_EMERGENCY:
|
||||
levelStr = "[EMERGENCY] ";
|
||||
break;
|
||||
}
|
||||
|
||||
string logMsg = header() + levelStr + msg;
|
||||
|
||||
cout << logMsg << endl;
|
||||
if (m_logFile.is_open())
|
||||
m_logFile << logMsg << endl;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
std::string logConsoleGUI::header()
|
||||
{
|
||||
// Define header to console log
|
||||
char buff[20];
|
||||
time_t now = time(nullptr);
|
||||
struct tm tm;
|
||||
|
||||
#ifdef _WIN32
|
||||
localtime_s(&tm, &now);
|
||||
#elif __linux__
|
||||
tm = *localtime(&now);
|
||||
#endif
|
||||
|
||||
strftime(buff, 20, "%Y-%m-%d %H:%M:%S", &tm);
|
||||
|
||||
ostringstream oss;
|
||||
oss << buff << " - ";
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
91
src/misc/logConsoleGUI/logConsoleGUI.h
Normal file
91
src/misc/logConsoleGUI/logConsoleGUI.h
Normal file
@@ -0,0 +1,91 @@
|
||||
/* --- logConsoleGUI.h -----------------------------------------
|
||||
System: Log system for gui applications
|
||||
Status: Version 1.0 Release 2
|
||||
Language: C++
|
||||
|
||||
(c) Copyright SD-Innovation 2016
|
||||
|
||||
Address:
|
||||
SD-INNOVATION SAS
|
||||
Site Eiffel Energie / ZAC Ban La Dame
|
||||
48, square Eugène Herzog
|
||||
54390 FROUARD
|
||||
FRANCE
|
||||
|
||||
Author: Sylvain Schneider
|
||||
E-Mail: s.schneider@sd-innovation.fr
|
||||
|
||||
Description: Header file for logConsoleGUI
|
||||
This class allows you to attach GUI application to text console
|
||||
or file to write log informations.
|
||||
|
||||
Thread Safe: No
|
||||
Platform Dependencies: None (i.e.: Linux/Intel, IRIX/Mips, Solaris/SPARC)
|
||||
Compiler Options:
|
||||
|
||||
Change History:
|
||||
Date Author Description
|
||||
2016-09-28 Sylvain Schneider Closing the console window when it released
|
||||
2016-08-23 Sylvain Schneider First version
|
||||
------------------------------------------------------------- */
|
||||
|
||||
#ifndef LOGCONSOLEGUI_H
|
||||
#define LOGCONSOLEGUI_H
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
|
||||
enum e_logLevel
|
||||
{
|
||||
level_DEBUG_COMMUNICATION, // Information useful to developers for debugging the communication application
|
||||
level_DEBUG, // Information useful to developers for debugging the application
|
||||
level_INFORMATIONS, // Normal operational messages that require no action (An application has started, paused or ended successfully)
|
||||
level_NOTICE, // Events that are unusual, but not error conditions
|
||||
level_WARNING, // May indicate that an error will occur if action is not taken
|
||||
level_ERROR, // Error conditions
|
||||
level_CRITICAL, // Critical conditions
|
||||
level_ALERT, // Should be corrected immediately
|
||||
level_EMERGENCY, // Application is unusable
|
||||
};
|
||||
|
||||
namespace sdiTools
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class logConsoleGUI
|
||||
{
|
||||
public:
|
||||
|
||||
public:
|
||||
logConsoleGUI(const std::string &logFilepath = ""); // Constructor
|
||||
virtual ~logConsoleGUI(); // Destructor
|
||||
|
||||
void openLogFile(const std::string &logFilepath); // Open log file
|
||||
void closeLogFile(); // Close log file
|
||||
int64_t getLogFileSize(); // Return opened log file size
|
||||
|
||||
void initConsole(bool attachOnly = false); // Init application console
|
||||
void releaseConsole(); // Release application console
|
||||
bool hasAttachedConsole(); // Returns true if console was attached, false otherwise
|
||||
|
||||
void disable_quickEditMode();
|
||||
void alwaysOnTop();
|
||||
|
||||
void log(const std::string &msg, // Log message into console and log file
|
||||
e_logLevel level = level_INFORMATIONS);
|
||||
virtual std::string header(); // Define header to console log
|
||||
|
||||
private:
|
||||
std::ofstream m_logFile;
|
||||
|
||||
FILE *m_stdoutFile = nullptr;
|
||||
FILE *m_stderrFile = nullptr;
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdiTools
|
||||
|
||||
typedef std::unique_ptr<sdiTools::logConsoleGUI> t_logConsoleGUI;
|
||||
extern t_logConsoleGUI logConsole;
|
||||
|
||||
#endif // LOGCONSOLEGUI_H
|
||||
17
src/misc/tools.h
Normal file
17
src/misc/tools.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
//--------------------------------------------------------------
|
||||
inline uint64_t encodeTimepoint(const std::chrono::system_clock::time_point &timepoint)
|
||||
{
|
||||
const auto duration = timepoint.time_since_epoch();
|
||||
return static_cast<uint64_t>(duration.count());
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
inline std::chrono::system_clock::time_point decodeTimepoint(const uint64_t value)
|
||||
{
|
||||
const auto duration = std::chrono::system_clock::duration(static_cast<std::chrono::seconds>(value));
|
||||
return std::chrono::system_clock::time_point(duration);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
16
src/misc/wxCustom.h
Normal file
16
src/misc/wxCustom.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <wx/statline.h>
|
||||
#include <wx/wx.h>
|
||||
|
||||
//--------------------------------------------------------------
|
||||
inline wxStaticLine *createHorizontalLine(wxWindow *parent, const wxWindowID id = wxID_ANY)
|
||||
{
|
||||
return new wxStaticLine(parent, id, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
inline wxStaticLine *createVerticalLine(wxWindow *parent, const wxWindowID id = wxID_ANY)
|
||||
{
|
||||
return new wxStaticLine(parent, id, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
BIN
src/resources/appIcon.ico
Normal file
BIN
src/resources/appIcon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 107 KiB |
BIN
src/resources/bin2c.exe
Normal file
BIN
src/resources/bin2c.exe
Normal file
Binary file not shown.
26
src/resources/resources.h
Normal file
26
src/resources/resources.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <wx/animate.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/mstream.h>
|
||||
|
||||
//--------------------------------------------------------------
|
||||
|
||||
//--------------------------------------------------------------
|
||||
#define wxGetBitmapFromMemory(name) _wxGetBitmapFromMemory(name, sizeof(name))
|
||||
inline wxBitmap _wxGetBitmapFromMemory(const unsigned char *data, int length)
|
||||
{
|
||||
wxMemoryInputStream is(data, length);
|
||||
return { wxImage(is, wxBITMAP_TYPE_ANY, -1), -1 };
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
#define wxGetAnimationFromMemory(name) _wxGetAnimationFromMemory(name, sizeof(name))
|
||||
inline wxAnimation _wxGetAnimationFromMemory(const unsigned char *data, int length)
|
||||
{
|
||||
wxAnimation animate;
|
||||
wxMemoryInputStream is(data, length);
|
||||
animate.Load(is);
|
||||
return animate;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
13
src/version/appVersion.conf
Normal file
13
src/version/appVersion.conf
Normal file
@@ -0,0 +1,13 @@
|
||||
[buildInformation]
|
||||
buildDate = 2022-10-14 09:36:16
|
||||
buildNumber = 155
|
||||
companyName = SD-Innovation S.A.S.
|
||||
copyright = Copyright SDi (C) 2019
|
||||
fileDecription = Aggregometer application manager
|
||||
internalName = thromboSoft.exe
|
||||
majorRevision = 0
|
||||
majorVersion = 2
|
||||
minorRevision = 0
|
||||
minorVersion = 3
|
||||
originalFilename = thromboSoft.exe
|
||||
productName = ThromboSoft
|
||||
13
src/version/appVersion.h
Normal file
13
src/version/appVersion.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
namespace version
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
static constexpr std::string_view appName = "ThromboSoft";
|
||||
static constexpr std::string_view fullVersion = "2.0.3.0 debugAndTests";
|
||||
static constexpr std::string_view shortVersion = "2.0.3.0 debugAndTests";
|
||||
//--------------------------------------------------------------
|
||||
} // namespace version
|
||||
19
src/version/appVersion.rc
Normal file
19
src/version/appVersion.rc
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#define COMPANY_NAME "SD-Innovation S.A.S."
|
||||
#define PRODUCT_NAME "ThromboSoft"
|
||||
#define FILE_DESCRIPTION "ThromboAggregometer application manager"
|
||||
#define ORIGINAL_FILENAME "thromboSoft.exe"
|
||||
#define INTERNAL_NAME "thromboSoft.exe"
|
||||
#define COPYRIGHT "Copyright SDi (C) 2024"
|
||||
#define FULL_VERSION "2.0.3.0"
|
||||
#define LONG_VERSION "2.0.3.0"
|
||||
#define SHORT_VERSION "2.0"
|
||||
#define BUILD_DATE "2024-10-25 16:10:19"
|
||||
#define BUILD_NUMBER 435
|
||||
#define MAJOR_VERSION 2
|
||||
#define MINOR_VERSION 0
|
||||
#define MAJOR_REVISION 3
|
||||
#define MINOR_REVISION 0
|
||||
|
||||
//#endif // APPVERSION_20221014093616_H
|
||||
BIN
src/version/autoVersion.exe
Normal file
BIN
src/version/autoVersion.exe
Normal file
Binary file not shown.
BIN
src/version/semverApp.exe
Normal file
BIN
src/version/semverApp.exe
Normal file
Binary file not shown.
14
src/version/version.xml
Normal file
14
src/version/version.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<SDi_semver Version="1.0.0">
|
||||
<General>
|
||||
<companyName>SD Innovation S.A.S.</companyName>
|
||||
<productName>SDi semVer</productName>
|
||||
<copyright>(c) SDi 2023</copyright>
|
||||
</General>
|
||||
<Build>
|
||||
<major>0</major>
|
||||
<minor>0</minor>
|
||||
<patch>0</patch>
|
||||
<build>144</build>
|
||||
<buildDate>1698656154</buildDate>
|
||||
</Build>
|
||||
</SDi_semver>
|
||||
Reference in New Issue
Block a user