mirror of
https://github.com/eclipse-openvehicle-api/openvehicle-api.git
synced 2026-02-05 15:18:45 +00:00
22
sdv_services/data_dispatch_service/CMakeLists.txt
Normal file
22
sdv_services/data_dispatch_service/CMakeLists.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
# Define project
|
||||
project(sdv_service_datadispatchservice VERSION 1.0 LANGUAGES CXX)
|
||||
|
||||
# Define target
|
||||
add_library(data_dispatch_service SHARED
|
||||
"dispatchservice.cpp"
|
||||
"dispatchservice.h"
|
||||
"transaction.cpp"
|
||||
"transaction.h"
|
||||
"signal.cpp"
|
||||
"signal.h" "trigger.h" "trigger.cpp")
|
||||
target_link_libraries(data_dispatch_service ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_link_options(data_dispatch_service PRIVATE)
|
||||
target_include_directories(data_dispatch_service PRIVATE ./include/)
|
||||
set_target_properties(data_dispatch_service PROPERTIES PREFIX "")
|
||||
set_target_properties(data_dispatch_service PROPERTIES SUFFIX ".sdv")
|
||||
|
||||
# Build dependencies
|
||||
add_dependencies(data_dispatch_service CompileCoreIDL)
|
||||
|
||||
# Appending the service in the service list
|
||||
set(SDV_Service_List ${SDV_Service_List} data_dispatch_service PARENT_SCOPE)
|
||||
215
sdv_services/data_dispatch_service/dispatchservice.cpp
Normal file
215
sdv_services/data_dispatch_service/dispatchservice.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
#include "dispatchservice.h"
|
||||
|
||||
|
||||
CDispatchService::CDispatchService()
|
||||
{
|
||||
CreateDirectTransactionID();
|
||||
}
|
||||
|
||||
sdv::IInterfaceAccess* CDispatchService::CreateTxTrigger(uint32_t uiCycleTime, uint32_t uiDelayTime, uint32_t uiBehaviorFlags,
|
||||
sdv::IInterfaceAccess* pTriggerCallback)
|
||||
{
|
||||
if (m_eObjectStatus != sdv::EObjectStatus::configuring) return nullptr;
|
||||
|
||||
// Check for parameter validity
|
||||
if (!uiCycleTime && !(uiBehaviorFlags & static_cast<uint32_t>(sdv::core::ISignalTransmission::ETxTriggerBehavior::spontaneous)))
|
||||
return nullptr;
|
||||
if (!pTriggerCallback)
|
||||
return nullptr;
|
||||
sdv::core::ITxTriggerCallback* pCallback = pTriggerCallback->GetInterface<sdv::core::ITxTriggerCallback>();
|
||||
if (!pCallback) return nullptr;
|
||||
|
||||
std::unique_ptr<CTrigger> ptrTrigger = std::make_unique<CTrigger>(*this, uiCycleTime, uiDelayTime, uiBehaviorFlags, pCallback);
|
||||
// Ignore cppcheck warning; normally the returned pointer should always have a value at this stage (otherwise an
|
||||
// exception was triggered).
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!ptrTrigger)
|
||||
return nullptr;
|
||||
CTrigger* pObject = ptrTrigger.get();
|
||||
// Ignore cppcheck warning; normally the returned pointer should always have a value at this stage (otherwise an
|
||||
// exception was triggered).
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!pObject)
|
||||
return nullptr;
|
||||
if (!ptrTrigger->IsValid()) return nullptr;
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mtxTriggers);
|
||||
return m_mapTriggers.emplace(pObject, std::move(ptrTrigger)).second ? pObject : nullptr;
|
||||
}
|
||||
|
||||
sdv::IInterfaceAccess* CDispatchService::RegisterTxSignal(/*in*/ const sdv::u8string& ssSignalName, /*in*/ sdv::any_t anyDefVal)
|
||||
{
|
||||
if (m_eObjectStatus != sdv::EObjectStatus::configuring) return nullptr;
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mtxSignals);
|
||||
|
||||
auto prSignal = m_mapTxSignals.try_emplace(ssSignalName, *this, ssSignalName, sdv::core::ESignalDirection::sigdir_tx, anyDefVal);
|
||||
return prSignal.first->second.CreateConsumer();
|
||||
}
|
||||
|
||||
sdv::IInterfaceAccess* CDispatchService::RegisterRxSignal(/*in*/ const sdv::u8string& ssSignalName)
|
||||
{
|
||||
if (m_eObjectStatus != sdv::EObjectStatus::configuring) return nullptr;
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mtxSignals);
|
||||
auto prSignal = m_mapRxSignals.try_emplace(ssSignalName, *this, ssSignalName, sdv::core::ESignalDirection::sigdir_rx);
|
||||
return prSignal.first->second.CreateProvider();
|
||||
}
|
||||
|
||||
sdv::IInterfaceAccess* CDispatchService::RequestSignalPublisher(/*in*/ const sdv::u8string& ssSignalName)
|
||||
{
|
||||
if (m_eObjectStatus != sdv::EObjectStatus::configuring) return nullptr;
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mtxSignals);
|
||||
auto itSignal = m_mapTxSignals.find(ssSignalName);
|
||||
if (itSignal == m_mapTxSignals.end()) return nullptr;
|
||||
if (itSignal->second.GetDirection() != sdv::core::ESignalDirection::sigdir_tx)
|
||||
return nullptr;
|
||||
return itSignal->second.CreateProvider();
|
||||
}
|
||||
|
||||
sdv::IInterfaceAccess* CDispatchService::AddSignalSubscription(/*in*/ const sdv::u8string& ssSignalName, /*in*/ IInterfaceAccess* pSubscriber)
|
||||
{
|
||||
if (m_eObjectStatus != sdv::EObjectStatus::configuring) return nullptr;
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mtxSignals);
|
||||
auto itSignal = m_mapRxSignals.find(ssSignalName);
|
||||
if (itSignal == m_mapRxSignals.end()) return 0;
|
||||
if (itSignal->second.GetDirection() != sdv::core::ESignalDirection::sigdir_rx)
|
||||
return 0ull;
|
||||
return itSignal->second.CreateConsumer(pSubscriber);
|
||||
}
|
||||
|
||||
sdv::sequence<sdv::core::SSignalRegistration> CDispatchService::GetRegisteredSignals() const
|
||||
{
|
||||
sdv::sequence<sdv::core::SSignalRegistration> seqRegistrations;
|
||||
std::unique_lock<std::mutex> lock(m_mtxSignals);
|
||||
for (const auto& prSignal : m_mapRxSignals)
|
||||
seqRegistrations.push_back({prSignal.first, prSignal.second.GetDirection()});
|
||||
for (const auto& prSignal : m_mapTxSignals)
|
||||
seqRegistrations.push_back({prSignal.first, prSignal.second.GetDirection()});
|
||||
return seqRegistrations;
|
||||
}
|
||||
|
||||
sdv::IInterfaceAccess* CDispatchService::CreateTransaction()
|
||||
{
|
||||
// NOTE: Transactions can take place at any time (not only when running). DO not restrict transactions to any
|
||||
// operation mode.
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mtxTransactions);
|
||||
auto itTransaction = m_lstTransactions.emplace(m_lstTransactions.end(), *this);
|
||||
if (itTransaction == m_lstTransactions.end()) return nullptr;
|
||||
itTransaction->SetIterator(itTransaction);
|
||||
return &(*itTransaction);
|
||||
}
|
||||
|
||||
uint64_t CDispatchService::GetNextTransactionID()
|
||||
{
|
||||
return m_uiTransactionCnt++;
|
||||
}
|
||||
|
||||
void CDispatchService::CreateDirectTransactionID()
|
||||
{
|
||||
m_uiDirectTransactionID = GetNextTransactionID();
|
||||
}
|
||||
|
||||
uint64_t CDispatchService::GetDirectTransactionID() const
|
||||
{
|
||||
return m_uiDirectTransactionID;
|
||||
}
|
||||
|
||||
void CDispatchService::Initialize(const sdv::u8string& /*ssObjectConfig*/)
|
||||
{
|
||||
m_eObjectStatus = sdv::EObjectStatus::initializing;
|
||||
m_scheduler.Start();
|
||||
m_eObjectStatus = sdv::EObjectStatus::initialized;
|
||||
}
|
||||
|
||||
sdv::EObjectStatus CDispatchService::GetStatus() const
|
||||
{
|
||||
return m_eObjectStatus;
|
||||
}
|
||||
|
||||
void CDispatchService::SetOperationMode(sdv::EOperationMode eMode)
|
||||
{
|
||||
switch (eMode)
|
||||
{
|
||||
case sdv::EOperationMode::configuring:
|
||||
if (m_eObjectStatus == sdv::EObjectStatus::running || m_eObjectStatus == sdv::EObjectStatus::initialized)
|
||||
m_eObjectStatus = sdv::EObjectStatus::configuring;
|
||||
break;
|
||||
case sdv::EOperationMode::running:
|
||||
if (m_eObjectStatus == sdv::EObjectStatus::configuring || m_eObjectStatus == sdv::EObjectStatus::initialized)
|
||||
m_eObjectStatus = sdv::EObjectStatus::running;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CDispatchService::Shutdown()
|
||||
{
|
||||
m_eObjectStatus = sdv::EObjectStatus::shutdown_in_progress;
|
||||
|
||||
m_scheduler.Stop();
|
||||
|
||||
m_eObjectStatus = sdv::EObjectStatus::initialization_pending;
|
||||
}
|
||||
|
||||
void CDispatchService::UnregisterSignal(/*in*/ const sdv::u8string& ssSignalName, sdv::core::ESignalDirection eDirection)
|
||||
{
|
||||
// NOTE: Normally the remove function should be called in the configuration mode. Since it doesn't give
|
||||
// feedback and the associated caller might delete any receiving function, allow the removal to take place even
|
||||
// when running.
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mtxSignals);
|
||||
|
||||
// Remove the signal
|
||||
if (eDirection == sdv::core::ESignalDirection::sigdir_rx)
|
||||
m_mapRxSignals.erase(ssSignalName);
|
||||
else
|
||||
m_mapTxSignals.erase(ssSignalName);
|
||||
}
|
||||
|
||||
CSignal* CDispatchService::FindSignal(const sdv::u8string& rssSignalName, sdv::core::ESignalDirection eDirection)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mtxSignals);
|
||||
|
||||
if (eDirection == sdv::core::ESignalDirection::sigdir_rx)
|
||||
{
|
||||
auto itSignal = m_mapRxSignals.find(rssSignalName);
|
||||
return itSignal == m_mapRxSignals.end() ? nullptr : &itSignal->second;
|
||||
} else
|
||||
{
|
||||
auto itSignal = m_mapTxSignals.find(rssSignalName);
|
||||
return itSignal == m_mapTxSignals.end() ? nullptr : &itSignal->second;
|
||||
}
|
||||
}
|
||||
|
||||
void CDispatchService::FinishTransaction(const CTransaction* pTransaction)
|
||||
{
|
||||
// NOTE: Transactions can take place at any time (not only when running). DO not restrict transactions to any
|
||||
// operation mode.
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mtxTransactions);
|
||||
|
||||
// Delete the transaction
|
||||
auto itTransaction = pTransaction->GetIterator();
|
||||
m_lstTransactions.erase(itTransaction);
|
||||
}
|
||||
|
||||
CScheduler& CDispatchService::GetScheduler()
|
||||
{
|
||||
return m_scheduler;
|
||||
}
|
||||
|
||||
void CDispatchService::RemoveTxTrigger(CTrigger* pTrigger)
|
||||
{
|
||||
// NOTE: Normally the remove function should be called in the configuration mode. Since it doesn't give
|
||||
// feedback and the associated caller might delete any receiving function, allow the removal to take place even
|
||||
// when running.
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mtxTriggers);
|
||||
m_scheduler.RemoveFromSchedule(pTrigger);
|
||||
m_mapTriggers.erase(pTrigger);
|
||||
}
|
||||
220
sdv_services/data_dispatch_service/dispatchservice.h
Normal file
220
sdv_services/data_dispatch_service/dispatchservice.h
Normal file
@@ -0,0 +1,220 @@
|
||||
#ifndef DISPATCH_SERVICE_H
|
||||
#define DISPATCH_SERVICE_H
|
||||
|
||||
#include <interfaces/dispatch.h>
|
||||
#include <support/component_impl.h>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <set>
|
||||
|
||||
// Data dispatch service for CAN:
|
||||
//
|
||||
// - CAN Link object for Lotus Eletre registers all the CAN signals as follows:
|
||||
// - Rx Signals are being registered to receive data from the vehicle and supply data to the vehicle devices.
|
||||
// - Signals are all grouped per message and per node/ECU
|
||||
// - One event is sent to update the data in the vehicle devices
|
||||
// - Data is updated first and then the event is broadcasted (toggle buffer)
|
||||
// - Tx Signals are being registered to receive data from the vehicle devices and supply data to the vehicle
|
||||
// - Signals are grouped per message and per node/ECU
|
||||
// - (Default values are available for each signal; either one time reset or reset after each send) -> service task?
|
||||
// - Sending per event or per timer
|
||||
|
||||
#include "transaction.h"
|
||||
#include "signal.h"
|
||||
#include "trigger.h"
|
||||
|
||||
/**
|
||||
* @brief data dispatch service to read/write and react on signal changes
|
||||
*/
|
||||
class CDispatchService : public sdv::CSdvObject, public sdv::core::ISignalTransmission, public sdv::core::ISignalAccess,
|
||||
public sdv::core::IDispatchTransaction, public sdv::IObjectControl
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
CDispatchService();
|
||||
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::core::ISignalTransmission)
|
||||
SDV_INTERFACE_ENTRY(sdv::core::ISignalAccess)
|
||||
SDV_INTERFACE_ENTRY(sdv::core::IDispatchTransaction)
|
||||
SDV_INTERFACE_ENTRY(sdv::IObjectControl)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
// Object declarations
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::SystemObject)
|
||||
DECLARE_OBJECT_CLASS_NAME("DataDispatchService")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
DECLARE_OBJECT_DEPENDENCIES("TaskTimerService")
|
||||
|
||||
/**
|
||||
* @brief Create a TX trigger object that defines how to trigger the signal transmission. Overload of
|
||||
* sdv::core::ISignalTransmission::CreateTxTrigger.
|
||||
* @param[in] uiCycleTime When set to any value other than 0, provides a cyclic trigger (ms). Could be 0 if cyclic
|
||||
* triggering is not required.
|
||||
* @param[in] uiDelayTime When set to any value other than 0, ensures a minimum time between two triggers. Could be 0
|
||||
* if minimum time should not be enforced.
|
||||
* @param[in] uiBehaviorFlags Zero or more flags from sdv::core::ETxTriggerBehavior.
|
||||
* @param[in] pTriggerCallback Pointer to the trigger callback object. This object needs to expose the ITxTriggerCallback
|
||||
* interface. This interface must stay valid during the lifetime of the generated trigger object.
|
||||
* @returns On success, returns an interface to the trigger object. Use the ITxTrigger interface to assign signals to
|
||||
* the trigger. Returns null when a trigger was requested without cycletime and without trigger behavior (which would
|
||||
* mean it would never trigger). Use IObjectDestroy to destroy the trigger object.
|
||||
*/
|
||||
IInterfaceAccess* CreateTxTrigger(uint32_t uiCycleTime, uint32_t uiDelayTime, uint32_t uiBehaviorFlags,
|
||||
sdv::IInterfaceAccess* pTriggerCallback);
|
||||
|
||||
/**
|
||||
* @brief Register a signal for sending over the network; reading from the dispatch service. Data is provided by the
|
||||
* signal publisher and dependable on the requested behavior stored until it is sent. Overload of
|
||||
* sdv::core::ISignalTransmission::RegisterTxSignal.
|
||||
* @param[in] ssSignalName Name of the signal. To guarantee uniqueness, it is preferred to add the group hierarchy to the
|
||||
* signal name separated by a dot. E.g. with CAN: MAB.BcmChas1Fr03.SteerReCtrlReqAgReq
|
||||
* @param[in] anyDefVal The default value of the signal.
|
||||
* @return Returns the IInterfaceAccess interface that allows access to the ISignalRead interface for reading the
|
||||
* signal value.
|
||||
*/
|
||||
virtual sdv::IInterfaceAccess* RegisterTxSignal(/*in*/ const sdv::u8string& ssSignalName, /*in*/ sdv::any_t anyDefVal) override;
|
||||
|
||||
/**
|
||||
* @brief Register a signal for reception over the network; providing to the dispatch service. Overload of
|
||||
* sdv::core::ISignalTransmission::RegisterRxSignal.
|
||||
* @param[in] ssSignalName Name of the signal. To guarantee uniqueness, it is preferred to add the group hierarchy to the
|
||||
* signal name separated by a dot. E.g. with CAN: MAB.BcmChas1Fr03.SteerReCtrlReqAgReq
|
||||
* @return Returns the IInterfaceAccess interface that allows access to the ISignalWrite interface for writing the
|
||||
* signal value.
|
||||
*/
|
||||
virtual sdv::IInterfaceAccess* RegisterRxSignal(/*in*/ const sdv::u8string& ssSignalName) override;
|
||||
|
||||
/**
|
||||
* @brief Requested a registered signal for publication (send signal). Overload of
|
||||
* sdv::core::ISignalAccess::RequestSignalPublisher.
|
||||
* @param[in] ssSignalName Name of the signal. To guarantee uniqueness, it is preferred to add the group hierarchy to the
|
||||
* signal name separated by a dot. E.g. with CAN: MAB.BcmChas1Fr03.SteerReCtrlReqAgReq
|
||||
* @return Returns the IInterfaceAccess interface that allows access to the ISignalWrite interface for writing the
|
||||
* signal value.
|
||||
*/
|
||||
virtual sdv::IInterfaceAccess* RequestSignalPublisher(/*in*/ const sdv::u8string& ssSignalName) override;
|
||||
|
||||
/**
|
||||
* @brief Add a registered signal for subscription (receive signal). Overload of
|
||||
* sdv::core::ISignalAccess::AddSignalSubscription.
|
||||
* @param[in] ssSignalName Name of the signal. To guarantee uniqueness, it is preferred to add the group hierarchy to the
|
||||
* signal name separated by a dot. E.g. with CAN: MAB.BcmChas1Fr03.SteerReCtrlReqAgReq
|
||||
* @param[in] pSubscriber Pointer to the IInterfaceAccess of the subscriber. The subscriber should implement the
|
||||
* ISignalReceiveEvent interface.
|
||||
* @return Returns an interface that can be used to manage the subscription. Use IObjectDestroy to destroy the signal object.
|
||||
*/
|
||||
virtual sdv::IInterfaceAccess* AddSignalSubscription(/*in*/ const sdv::u8string& ssSignalName, /*in*/ sdv::IInterfaceAccess* pSubscriber) override;
|
||||
|
||||
/**
|
||||
* @brief Get a list of registered signals.
|
||||
* @return List of registration functions.
|
||||
*/
|
||||
virtual sdv::sequence<sdv::core::SSignalRegistration> GetRegisteredSignals() const override;
|
||||
|
||||
/**
|
||||
* @brief CreateTransaction a transaction. Overload of sdv::core::IDispatchTransaction::CreateTransaction.
|
||||
* @details When starting a group transaction, any writing to a signal will not be reflected yet until the transaction
|
||||
* is finalized. For the data link layer, this also allows freezing the reading values until all values have been read.
|
||||
* @return Returns the transaction interface or NULL when the transaction could not be started. Use IObjectDestroy to
|
||||
* destroy the transaction object.
|
||||
*/
|
||||
virtual sdv::IInterfaceAccess* CreateTransaction() override;
|
||||
|
||||
/**
|
||||
* @brief Get the next transaction ID.
|
||||
* @return Returns the next transaction ID.
|
||||
*/
|
||||
uint64_t GetNextTransactionID();
|
||||
|
||||
/**
|
||||
* @brief Create a new direct transaction ID.
|
||||
* @details The read transaction ID is used fir direct transmission after a read transaction was started. This prevents direct
|
||||
* transmission overwriting the protected read transaction.
|
||||
*/
|
||||
void CreateDirectTransactionID();
|
||||
|
||||
/**
|
||||
* @brief Get the current direct transaction ID.
|
||||
* @return The transaction ID.
|
||||
*/
|
||||
uint64_t GetDirectTransactionID() const;
|
||||
|
||||
/**
|
||||
* @brief Initialize the object. Overload of sdv::IObjectControl::Initialize.
|
||||
* @param[in] ssObjectConfig Optional configuration string.
|
||||
*/
|
||||
void Initialize(const sdv::u8string& ssObjectConfig) override;
|
||||
|
||||
/**
|
||||
* @brief Get the current status of the object. Overload of sdv::IObjectControl::GetStatus.
|
||||
* @return Return the current status of the object.
|
||||
*/
|
||||
sdv::EObjectStatus GetStatus() const override;
|
||||
|
||||
/**
|
||||
* @brief Set the component operation mode. Overload of sdv::IObjectControl::SetOperationMode.
|
||||
* @param[in] eMode The operation mode, the component should run in.
|
||||
*/
|
||||
void SetOperationMode(sdv::EOperationMode eMode) override;
|
||||
|
||||
/**
|
||||
* @brief Shutdown called before the object is destroyed. Overload of sdv::IObjectControl::Shutdown.
|
||||
*/
|
||||
void Shutdown() override;
|
||||
|
||||
/**
|
||||
* @brief Unregister a previously registered signal. This will render all subscriptions and provider connections invalid.
|
||||
* @param[in] ssSignalName Name of the signal to unregister.
|
||||
* @param[in] eDirection The signal direction determines from which map the signal should be unregistered.
|
||||
*/
|
||||
void UnregisterSignal(const sdv::u8string& ssSignalName, sdv::core::ESignalDirection eDirection);
|
||||
|
||||
/**
|
||||
* @brief Find the signal with the supplied name.
|
||||
* @param[in] rssSignalName Name of the signal to find.
|
||||
* @param[in] eDirection The signal direction determines at which map the signal should be searched for.
|
||||
* @return Pointer to the signal or NULL when the signal could not be found.
|
||||
*/
|
||||
CSignal* FindSignal(const sdv::u8string& rssSignalName, sdv::core::ESignalDirection eDirection);
|
||||
|
||||
/**
|
||||
* @brief Finalize a transaction transaction. Any update made on this interface between the start and the finalize will be
|
||||
* in effect at once.
|
||||
* @param[in] pTransaction The transaction to finalize.
|
||||
*/
|
||||
void FinishTransaction(const CTransaction* pTransaction);
|
||||
|
||||
/**
|
||||
* @brief Get the trigger execution scheduler.
|
||||
* @return Returns a reference to the contained trigger execution scheduler.
|
||||
*/
|
||||
CScheduler& GetScheduler();
|
||||
|
||||
/**
|
||||
* @brief Remove a trigger object from the trigger map.
|
||||
* @param[in] pTrigger Pointer to the trigger object to remove.
|
||||
*/
|
||||
void RemoveTxTrigger(CTrigger* pTrigger);
|
||||
|
||||
private:
|
||||
mutable std::mutex m_mtxSignals; ///< Signal object map protection.
|
||||
std::map<sdv::u8string, CSignal> m_mapRxSignals; ///< Signal object map.
|
||||
std::map<sdv::u8string, CSignal> m_mapTxSignals; ///< Signal object map.
|
||||
sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object status.
|
||||
std::atomic_uint64_t m_uiTransactionCnt = 1ull; ///< Transaction counter.
|
||||
std::mutex m_mtxTransactions; ///< List with transactions access.
|
||||
std::list<CTransaction> m_lstTransactions; ///< List with transactions.
|
||||
uint64_t m_uiDirectTransactionID; ///< Current direct transaction ID.
|
||||
CScheduler m_scheduler; ///< Scheduler for trigger execution.
|
||||
mutable std::mutex m_mtxTriggers; ///< Trigger object map protection.
|
||||
std::map<CTrigger*, std::unique_ptr<CTrigger>> m_mapTriggers; ///< Trigger object map.
|
||||
};
|
||||
|
||||
DEFINE_SDV_OBJECT(CDispatchService)
|
||||
|
||||
#endif // !defined DISPATCH_SERVICE_H
|
||||
246
sdv_services/data_dispatch_service/signal.cpp
Normal file
246
sdv_services/data_dispatch_service/signal.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
#include "dispatchservice.h"
|
||||
#include "signal.h"
|
||||
#include "trigger.h"
|
||||
#include "transaction.h"
|
||||
|
||||
CProvider::CProvider(CSignal& rSignal) : m_rSignal(rSignal)
|
||||
{}
|
||||
|
||||
void CProvider::DestroyObject()
|
||||
{
|
||||
m_rSignal.RemoveProvider(this);
|
||||
}
|
||||
|
||||
void CProvider::Write(/*in*/ sdv::any_t anyVal, /*in*/ sdv::IInterfaceAccess* pTransaction)
|
||||
{
|
||||
// Let the transaction handle the write if there is a transaction.
|
||||
CTransaction* pTransactionObj = static_cast<CTransaction*>(pTransaction);
|
||||
if (pTransactionObj)
|
||||
{
|
||||
pTransactionObj->DeferWrite(m_rSignal, anyVal);
|
||||
return;
|
||||
}
|
||||
|
||||
// Write the value
|
||||
// Note, explicitly convert to uint64_t to resolve ambiguous function selection under Linux.
|
||||
std::set<CTrigger*> setTriggers;
|
||||
m_rSignal.WriteFromProvider(anyVal, static_cast<uint64_t>(0), setTriggers);
|
||||
|
||||
// Execute the triggers
|
||||
for (CTrigger* pTrigger : setTriggers)
|
||||
pTrigger->Execute();
|
||||
|
||||
}
|
||||
|
||||
CConsumer::CConsumer(CSignal& rSignal, sdv::IInterfaceAccess* pEvent /*= nullptr*/) :
|
||||
m_rSignal(rSignal)
|
||||
{
|
||||
if (pEvent) m_pEvent = pEvent->GetInterface<sdv::core::ISignalReceiveEvent>();
|
||||
}
|
||||
|
||||
void CConsumer::DestroyObject()
|
||||
{
|
||||
m_rSignal.RemoveConsumer(this);
|
||||
}
|
||||
|
||||
sdv::any_t CConsumer::Read(/*in*/ sdv::IInterfaceAccess* pTransaction) const
|
||||
{
|
||||
const CTransaction* pTransactionObj = static_cast<const CTransaction*>(pTransaction);
|
||||
|
||||
// Determine the transaction ID to use
|
||||
uint64_t uiTransactionID = 0;
|
||||
if (pTransactionObj) uiTransactionID = pTransactionObj->GetReadTransactionID();
|
||||
|
||||
// Request a read from the signal
|
||||
return m_rSignal.ReadFromConsumer(uiTransactionID);
|
||||
}
|
||||
|
||||
void CConsumer::Distribute(const sdv::any_t& ranyVal)
|
||||
{
|
||||
if (m_pEvent) m_pEvent->Receive(ranyVal);
|
||||
}
|
||||
|
||||
CSignal::CSignal(CDispatchService& rDispatchSvc, const sdv::u8string& rssName, sdv::core::ESignalDirection eDirection,
|
||||
sdv::any_t anyDefVal /*= sdv::any_t()*/) :
|
||||
m_rDispatchSvc(rDispatchSvc), m_ssName(rssName), m_eDirection(eDirection), m_anyDefVal(anyDefVal)
|
||||
{
|
||||
for (auto& rprVal : m_rgprVal)
|
||||
rprVal = {0, anyDefVal};
|
||||
}
|
||||
|
||||
CSignal::~CSignal()
|
||||
{}
|
||||
|
||||
sdv::u8string CSignal::GetName() const
|
||||
{
|
||||
return m_ssName;
|
||||
}
|
||||
|
||||
sdv::core::ESignalDirection CSignal::GetDirection() const
|
||||
{
|
||||
return m_eDirection;
|
||||
}
|
||||
|
||||
sdv::any_t CSignal::GetDefVal() const
|
||||
{
|
||||
return m_anyDefVal;
|
||||
}
|
||||
|
||||
CProvider* CSignal::CreateProvider()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mtxSignalObjects);
|
||||
auto ptrProvider = std::make_unique<CProvider>(*this);
|
||||
// Ignore cppcheck warning; normally the returned pointer should always have a value at this stage (otherwise an exception was
|
||||
// triggered).
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!ptrProvider)
|
||||
return nullptr;
|
||||
CProvider* pObject = ptrProvider.get();
|
||||
if (pObject)
|
||||
m_mapProviders.try_emplace(pObject, std::move(ptrProvider));
|
||||
else
|
||||
RemoveProvider(nullptr); // Cleanup
|
||||
return pObject;
|
||||
}
|
||||
|
||||
void CSignal::RemoveProvider(CProvider* pProvider)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mtxSignalObjects);
|
||||
m_mapProviders.erase(pProvider);
|
||||
bool bUnregister = m_mapProviders.empty() && m_mapConsumers.empty();
|
||||
lock.unlock();
|
||||
if (bUnregister)
|
||||
m_rDispatchSvc.UnregisterSignal(m_ssName, m_eDirection);
|
||||
}
|
||||
|
||||
CConsumer* CSignal::CreateConsumer(sdv::IInterfaceAccess* pEvent /*= nullptr*/)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mtxSignalObjects);
|
||||
auto ptrConsumer = std::make_unique<CConsumer>(*this, pEvent);
|
||||
// Ignore cppcheck warning; normally the returned pointer should always have a value at this stage (otherwise an exception was
|
||||
// triggered).
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!ptrConsumer)
|
||||
return nullptr;
|
||||
CConsumer* pObject = ptrConsumer.get();
|
||||
if (pObject)
|
||||
m_mapConsumers.try_emplace(pObject, std::move(ptrConsumer));
|
||||
else
|
||||
RemoveConsumer(nullptr); // Cleanup
|
||||
return pObject;
|
||||
}
|
||||
|
||||
void CSignal::RemoveConsumer(CConsumer* pConsumer)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mtxSignalObjects);
|
||||
m_mapConsumers.erase(pConsumer);
|
||||
if (m_mapConsumers.empty())
|
||||
{
|
||||
// Remove any triggers
|
||||
std::unique_lock<std::mutex> lockTrigger(m_mtxTriggers);
|
||||
while (!m_setTriggers.empty())
|
||||
{
|
||||
CTrigger* pTrigger = *m_setTriggers.begin();
|
||||
m_setTriggers.erase(m_setTriggers.begin());
|
||||
lockTrigger.unlock();
|
||||
|
||||
// Remove this signal from the trigger.
|
||||
pTrigger->RemoveSignal(m_ssName);
|
||||
|
||||
lockTrigger.lock();
|
||||
}
|
||||
}
|
||||
|
||||
bool bUnregister = m_mapProviders.empty() && m_mapConsumers.empty();
|
||||
lock.unlock();
|
||||
if (bUnregister)
|
||||
m_rDispatchSvc.UnregisterSignal(m_ssName, m_eDirection);
|
||||
}
|
||||
|
||||
void CSignal::WriteFromProvider(const sdv::any_t& ranyVal, uint64_t uiTransactionID, std::set<CTrigger*>& rsetTriggers)
|
||||
{
|
||||
if (m_rDispatchSvc.GetStatus() != sdv::EObjectStatus::running) return;
|
||||
|
||||
uint64_t uiTransactionIDTemp = uiTransactionID;
|
||||
if (!uiTransactionIDTemp) uiTransactionIDTemp = m_rDispatchSvc.GetDirectTransactionID();
|
||||
|
||||
// Store the value
|
||||
std::unique_lock<std::mutex> lock(m_mtxVal);
|
||||
if (m_nValIndex >= std::extent_v<decltype(m_rgprVal)>) m_nValIndex = 0; // TODO: This is a serious error.
|
||||
size_t nTargetIndex = m_nValIndex;
|
||||
|
||||
// Create an entry with the current transaction ID
|
||||
if (m_rgprVal[nTargetIndex].first < uiTransactionIDTemp)
|
||||
{
|
||||
nTargetIndex = (nTargetIndex + 1) % std::extent_v<decltype(m_rgprVal)>;
|
||||
m_rgprVal[nTargetIndex].first = uiTransactionIDTemp;
|
||||
m_nValIndex = nTargetIndex;
|
||||
}
|
||||
|
||||
// Update the value
|
||||
m_rgprVal[nTargetIndex].second = ranyVal;
|
||||
lock.unlock();
|
||||
|
||||
// Add all triggers to the set of triggers
|
||||
std::unique_lock<std::mutex> lockTriggers(m_mtxTriggers);
|
||||
for (CTrigger* pTrigger : m_setTriggers)
|
||||
rsetTriggers.insert(pTrigger);
|
||||
lockTriggers.unlock();
|
||||
|
||||
// Trigger the update event.
|
||||
DistributeToConsumers(ranyVal);
|
||||
}
|
||||
|
||||
sdv::any_t CSignal::ReadFromConsumer(uint64_t uiTransactionID) const
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mtxVal);
|
||||
|
||||
if (m_nValIndex >= std::extent_v<decltype(m_rgprVal)>) return {}; // TODO: This is a serious error.
|
||||
|
||||
// Determine the transaction ID to use
|
||||
uint64_t uiTransactionIDCopy = uiTransactionID;
|
||||
size_t nTargetIndex = m_nValIndex;
|
||||
if (!uiTransactionIDCopy) uiTransactionIDCopy = m_rgprVal[nTargetIndex].first;
|
||||
|
||||
// Find the signal with the same or lower transaction ID
|
||||
while (m_rgprVal[m_nValIndex].first > uiTransactionIDCopy) // Value too new
|
||||
{
|
||||
// Search for the index with the transaction ID.
|
||||
// Ignore cppcheck comparison operator (template is interpreted as > operator)
|
||||
// cppcheck-suppress compareBoolExpressionWithInt
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
nTargetIndex = (nTargetIndex + std::extent_v<decltype(m_rgprVal)> - 1) % std::extent_v<decltype(m_rgprVal)>;
|
||||
if (nTargetIndex == m_nValIndex) return m_anyDefVal; // Transaction too old... cannot update
|
||||
}
|
||||
|
||||
// Fitting transaction found. Get the value.
|
||||
sdv::any_t anyVal = m_rgprVal[nTargetIndex].second;
|
||||
|
||||
return anyVal;
|
||||
}
|
||||
|
||||
void CSignal::DistributeToConsumers(const sdv::any_t& ranyVal)
|
||||
{
|
||||
if (m_rDispatchSvc.GetStatus() != sdv::EObjectStatus::running) return;
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mtxSignalObjects);
|
||||
for (auto& rvtConsumer : m_mapConsumers)
|
||||
rvtConsumer.second->Distribute(ranyVal);
|
||||
}
|
||||
|
||||
void CSignal::AddTrigger(CTrigger* pTrigger)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mtxTriggers);
|
||||
m_setTriggers.emplace(pTrigger);
|
||||
}
|
||||
|
||||
void CSignal::RemoveTrigger(CTrigger* pTrigger)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mtxTriggers);
|
||||
m_setTriggers.erase(pTrigger);
|
||||
}
|
||||
|
||||
bool CSignal::EqualsDefaultValue() const
|
||||
{
|
||||
return ReadFromConsumer(0) == m_anyDefVal;
|
||||
}
|
||||
238
sdv_services/data_dispatch_service/signal.h
Normal file
238
sdv_services/data_dispatch_service/signal.h
Normal file
@@ -0,0 +1,238 @@
|
||||
#ifndef SIGNAL_H
|
||||
#define SIGNAL_H
|
||||
|
||||
#include <support/interface_ptr.h>
|
||||
#include <interfaces/dispatch.h>
|
||||
#include "trigger.h"
|
||||
|
||||
// Forward declaration
|
||||
class CDispatchService;
|
||||
class CSignal;
|
||||
|
||||
/**
|
||||
* @brief Class implementing the signal provider. Needed for provider interface implementation.
|
||||
*/
|
||||
class CProvider : public sdv::IInterfaceAccess, public sdv::IObjectDestroy, public sdv::core::ISignalWrite
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param[in] rSignal Reference to the signal instance.
|
||||
*/
|
||||
explicit CProvider(CSignal& rSignal);
|
||||
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::IObjectDestroy)
|
||||
SDV_INTERFACE_ENTRY(sdv::core::ISignalWrite)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
/**
|
||||
* @brief Destroy the object. Overload of sdv::IObjectDestroy::DestroyObject.
|
||||
*/
|
||||
virtual void DestroyObject() override;
|
||||
|
||||
/**
|
||||
* @brief Update the signal value. Overload of sdv::core::ISignalWrite::Write.
|
||||
* @param[in] anyVal The value to update the signal with.
|
||||
* @param[in] pTransaction The transaction interface. Could be NULL in case the update should occur immediately.
|
||||
*/
|
||||
virtual void Write(/*in*/ sdv::any_t anyVal, /*in*/ sdv::IInterfaceAccess* pTransaction) override;
|
||||
|
||||
/**
|
||||
* @brief Update the signal value with the transaction ID supplied. A new entry will be created if the transaction is
|
||||
* larger.
|
||||
* @param[in] ranyVal Reference to the value to update the signal with.
|
||||
* @param[in] uiTransactionID The transaction ID or 0 for current transaction ID.
|
||||
*/
|
||||
void Write(const sdv::any_t& ranyVal, uint64_t uiTransactionID);
|
||||
|
||||
private:
|
||||
sdv::CLifetimeCookie m_cookie = sdv::CreateLifetimeCookie(); ///< Lifetime cookie to manage the module lifetime.
|
||||
CSignal& m_rSignal; ///< Reference to the signal class.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class implementing the signal consumer. Needed for consumer interface implementation.
|
||||
*/
|
||||
class CConsumer : public sdv::IInterfaceAccess, public sdv::IObjectDestroy, public sdv::core::ISignalRead
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param[in] rSignal Reference to the signal instance.
|
||||
* @param[in] pEvent The event to be triggered on a signal change. Optional.
|
||||
*/
|
||||
CConsumer(CSignal& rSignal, sdv::IInterfaceAccess* pEvent = nullptr);
|
||||
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::IObjectDestroy)
|
||||
SDV_INTERFACE_ENTRY(sdv::core::ISignalRead)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
/**
|
||||
* @brief Destroy the object. Overload of sdv::IObjectDestroy::DestroyObject.
|
||||
*/
|
||||
virtual void DestroyObject() override;
|
||||
|
||||
/**
|
||||
* @brief Get the signal value. Overload of sdv::core::ISignalRead::Read.
|
||||
* @param[in] pTransaction The transaction interface. Could be NULL in case the most up-to-date value is requested.
|
||||
* @return Returns the value.
|
||||
*/
|
||||
virtual sdv::any_t Read(/*in*/ sdv::IInterfaceAccess* pTransaction) const override;
|
||||
|
||||
/**
|
||||
* @brief Update the signal value with the transaction ID supplied. A new entry will be created if the transaction is
|
||||
* larger.
|
||||
* @param[in] ranyVal Reference to the value to update the signal with.
|
||||
* @param[in] uiTransactionID The transaction ID or 0 for current transaction ID.
|
||||
*/
|
||||
void Write(const sdv::any_t& ranyVal, uint64_t uiTransactionID);
|
||||
|
||||
/**
|
||||
* @brief Distribute a value to all consumers.
|
||||
* @param[in] ranyVal Reference of the value to consumers.
|
||||
*/
|
||||
void Distribute(const sdv::any_t& ranyVal);
|
||||
|
||||
private:
|
||||
sdv::CLifetimeCookie m_cookie = sdv::CreateLifetimeCookie(); ///< Lifetime cookie to manage the module lifetime.
|
||||
CSignal& m_rSignal; ///< Reference to the signal class
|
||||
sdv::core::ISignalReceiveEvent* m_pEvent = nullptr; ///< Receive event interface if available.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Signal administration
|
||||
*/
|
||||
class CSignal
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Helper object to manage object lifetime.
|
||||
*/
|
||||
struct SSignalObjectHelper
|
||||
{
|
||||
/**
|
||||
* @brief Default destructor (needs to be virtual to allow deletion of derived class).
|
||||
*/
|
||||
virtual ~SSignalObjectHelper() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Signal class constructor.
|
||||
* @param[in] rDispatchSvc Reference to the dispatch service.
|
||||
* @param[in] rssName Reference to the name string.
|
||||
* @param[in] anyDefVal Any containing the default value (for send-signals).
|
||||
* @param[in] eDirection Definition whether the signal is a send or receive signal.
|
||||
*/
|
||||
CSignal(CDispatchService& rDispatchSvc, const sdv::u8string& rssName, sdv::core::ESignalDirection eDirection,
|
||||
sdv::any_t anyDefVal = sdv::any_t());
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~CSignal();
|
||||
|
||||
/**
|
||||
* @brief Get the name of the signal.
|
||||
* @return String with the signal name.
|
||||
*/
|
||||
sdv::u8string GetName() const;
|
||||
|
||||
/**
|
||||
* @brief Get the signal direction.
|
||||
* @return The signal direction.
|
||||
*/
|
||||
sdv::core::ESignalDirection GetDirection() const;
|
||||
|
||||
/**
|
||||
* @brief Get the signal default value.
|
||||
* @return Any structure with default value.
|
||||
*/
|
||||
sdv::any_t GetDefVal() const;
|
||||
|
||||
/**
|
||||
* @brief Create a provider object.
|
||||
* @return Returns a pointer to the provider object or NULL when the signal is not configured to be a provider.
|
||||
*/
|
||||
CProvider* CreateProvider();
|
||||
|
||||
/**
|
||||
* @brief Remove a provider object.
|
||||
* @remarks If there are no other consumer/provider objects any more, the signal is unregistered from the dispatch service.
|
||||
* @param[in] pProvider Pointer to the provider to remove.
|
||||
*/
|
||||
void RemoveProvider(CProvider* pProvider);
|
||||
|
||||
/**
|
||||
* @brief Create a consumer object.
|
||||
* @param[in] pEvent The event to be triggered on a signal change. Optional.
|
||||
* @return Returns a pointer to the consumer object or NULL when the signal is not configured to be a consumer.
|
||||
*/
|
||||
CConsumer* CreateConsumer(sdv::IInterfaceAccess* pEvent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Remove a consumer object.
|
||||
* @remarks If there are no other consumer/provider objects any more, the signal is unregistered from the dispatch service.
|
||||
* @param[in] pConsumer Pointer to the consumer to remove.
|
||||
*/
|
||||
void RemoveConsumer(CConsumer* pConsumer);
|
||||
|
||||
/**
|
||||
* @brief Update the signal value with the transaction ID supplied. A new entry will be created if the transaction is larger.
|
||||
* @param[in] ranyVal Reference to the value to update the signal with.
|
||||
* @param[in] uiTransactionID The transaction ID or 0 for current transaction ID.
|
||||
* @param[in] rsetTriggers Set of triggers to execute on a spontaneous write.
|
||||
*/
|
||||
void WriteFromProvider(const sdv::any_t& ranyVal, uint64_t uiTransactionID, std::set<CTrigger*>& rsetTriggers);
|
||||
|
||||
/**
|
||||
* @brief Get the signal value.
|
||||
* @param[in] uiTransactionID The transaction ID or 0 for current transaction ID.
|
||||
* @return Returns the value.
|
||||
*/
|
||||
sdv::any_t ReadFromConsumer(uint64_t uiTransactionID) const;
|
||||
|
||||
/**
|
||||
* @brief Distribute a value to all consumers.
|
||||
* @param[in] ranyVal Reference of the value to distribute.
|
||||
*/
|
||||
void DistributeToConsumers(const sdv::any_t& ranyVal);
|
||||
|
||||
/**
|
||||
* @brief Add a trigger to the signal. This trigger will be returned when creating a trigger list.
|
||||
* @param[in] pTrigger Pointer to the trigger object.
|
||||
*/
|
||||
void AddTrigger(CTrigger* pTrigger);
|
||||
|
||||
/**
|
||||
* @brief Remove the trigger from the signal.
|
||||
* @param[in] pTrigger Pointer to the trigger object.
|
||||
*/
|
||||
void RemoveTrigger(CTrigger* pTrigger);
|
||||
|
||||
/**
|
||||
* @brief Returns whether the signal equals the default value.
|
||||
* @return Return the result of the comparison.
|
||||
*/
|
||||
bool EqualsDefaultValue() const;
|
||||
|
||||
private:
|
||||
sdv::CLifetimeCookie m_cookie = sdv::CreateLifetimeCookie(); ///< Lifetime cookie to manage the module lifetime.
|
||||
CDispatchService& m_rDispatchSvc; ///< Reference to dispatch service.
|
||||
sdv::u8string m_ssName; ///< Signal name
|
||||
sdv::core::ESignalDirection m_eDirection = sdv::core::ESignalDirection::sigdir_tx; ///< Signal direction
|
||||
sdv::any_t m_anyDefVal; ///< Default value
|
||||
mutable std::mutex m_mtxVal; ///< Signal value protection
|
||||
mutable std::pair<uint64_t, sdv::any_t> m_rgprVal[16]; ///< The signal value
|
||||
mutable size_t m_nValIndex = 0; ///< Most up-to-date-index
|
||||
mutable std::mutex m_mtxSignalObjects; ///< Signal object map protection.
|
||||
std::map<CProvider*, std::unique_ptr<CProvider>> m_mapProviders; ///< Map with signal objects.
|
||||
std::map<CConsumer*, std::unique_ptr<CConsumer>> m_mapConsumers; ///< Map with signal objects.
|
||||
std::mutex m_mtxTriggers; ///< Protection for the trigger set.
|
||||
std::set<CTrigger*> m_setTriggers; ///< Trigger set for this signal.
|
||||
};
|
||||
|
||||
#endif // !defined SIGNAL_H
|
||||
90
sdv_services/data_dispatch_service/transaction.cpp
Normal file
90
sdv_services/data_dispatch_service/transaction.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#include "dispatchservice.h"
|
||||
#include "transaction.h"
|
||||
#include "trigger.h"
|
||||
#include "signal.h"
|
||||
|
||||
CTransaction::CTransaction(CDispatchService& rDispatchSvc) :
|
||||
m_rDispatchSvc(rDispatchSvc), m_uiReadTransactionID(rDispatchSvc.GetNextTransactionID())
|
||||
{}
|
||||
|
||||
void CTransaction::DestroyObject()
|
||||
{
|
||||
FinalizeWrite();
|
||||
m_rDispatchSvc.FinishTransaction(this);
|
||||
}
|
||||
|
||||
uint64_t CTransaction::GetReadTransactionID() const
|
||||
{
|
||||
// Set transaction to write if not done so.
|
||||
if (m_eTransactionType != ETransactionType::read_transaction)
|
||||
{
|
||||
if (m_eTransactionType == ETransactionType::undefined)
|
||||
{
|
||||
m_eTransactionType = ETransactionType::read_transaction;
|
||||
|
||||
// Create a new direct transaction ID to prevent the current values from overwriting
|
||||
m_rDispatchSvc.CreateDirectTransactionID();
|
||||
}
|
||||
else
|
||||
return 0ull;
|
||||
}
|
||||
|
||||
return m_uiReadTransactionID;
|
||||
}
|
||||
|
||||
void CTransaction::DeferWrite(CSignal& rSignal, sdv::any_t& ranyVal)
|
||||
{
|
||||
// Set transaction to write if not done so.
|
||||
if (m_eTransactionType != ETransactionType::write_transaction)
|
||||
{
|
||||
if (m_eTransactionType == ETransactionType::undefined)
|
||||
m_eTransactionType = ETransactionType::write_transaction;
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the value to the deferred signal map.
|
||||
std::unique_lock<std::mutex> lock(m_mtxDeferredWriteSignalMap);
|
||||
m_mapDeferredWriteSignalMap.insert_or_assign(&rSignal, ranyVal);
|
||||
}
|
||||
|
||||
void CTransaction::FinalizeWrite()
|
||||
{
|
||||
// Set transaction to write if not done so.
|
||||
if (m_eTransactionType != ETransactionType::write_transaction)
|
||||
{
|
||||
if (m_eTransactionType == ETransactionType::undefined)
|
||||
m_eTransactionType = ETransactionType::write_transaction;
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the next transaction ID
|
||||
uint64_t uiWriteTransaction = m_rDispatchSvc.GetNextTransactionID();
|
||||
|
||||
// Write the signals with the transaction ID.
|
||||
std::unique_lock<std::mutex> lock(m_mtxDeferredWriteSignalMap);
|
||||
std::set<CTrigger*> setTriggers;
|
||||
for (auto& rvtDeferredSignal : m_mapDeferredWriteSignalMap)
|
||||
{
|
||||
if (!rvtDeferredSignal.first) continue;
|
||||
rvtDeferredSignal.first->WriteFromProvider(rvtDeferredSignal.second, uiWriteTransaction, setTriggers);
|
||||
}
|
||||
m_mapDeferredWriteSignalMap.clear();
|
||||
lock.unlock();
|
||||
|
||||
// Execute the triggers
|
||||
for (CTrigger* pTrigger : setTriggers)
|
||||
pTrigger->Execute();
|
||||
}
|
||||
|
||||
void CTransaction::SetIterator(std::list<CTransaction>::iterator itTransaction)
|
||||
{
|
||||
m_itTransaction = itTransaction;
|
||||
}
|
||||
|
||||
std::list<CTransaction>::iterator CTransaction::GetIterator() const
|
||||
{
|
||||
return m_itTransaction;
|
||||
}
|
||||
|
||||
99
sdv_services/data_dispatch_service/transaction.h
Normal file
99
sdv_services/data_dispatch_service/transaction.h
Normal file
@@ -0,0 +1,99 @@
|
||||
#ifndef TRANSACTION_H
|
||||
#define TRANSACTION_H
|
||||
|
||||
#include <support/interface_ptr.h>
|
||||
|
||||
// Forward declaration
|
||||
class CDispatchService;
|
||||
class CSignal;
|
||||
|
||||
/**
|
||||
* @brief Transaction administration
|
||||
* @details During creation of the transaction object, the transaction is of undefined type. The transaction takes the read or
|
||||
* write type only after a call to the read or write function. After this, the transaction doesn't change type any more and will
|
||||
* block calls to the functions provided for the other type (e.g. a read transaction doesn't allow a write and a write transaction
|
||||
* doesn't allow a read).
|
||||
* A read transaction is using the transaction ID to identify the current time of the transaction. Any data available before can
|
||||
* be read. Any data coming after will not be transmitted. In case there is no data any more (the transaction is too old) the
|
||||
* default value will be used.
|
||||
* A write transaction will collect the signal values. The distribution is deferred until the transaction is finalized. During
|
||||
* finalization, a new transaction ID is requested and the values are distributed using this ID (this is necessary so a read
|
||||
* transaction can decide whether the values are included in a read operation).
|
||||
* The written values are stored in a ringbuffer in the signal class. The latest position in the ring buffer contains the last
|
||||
* distributed transaction.
|
||||
*/
|
||||
class CTransaction : public sdv::IInterfaceAccess, public sdv::IObjectDestroy
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param[in] rDispatchSvc Reference to the dispatch service.
|
||||
*/
|
||||
explicit CTransaction(CDispatchService& rDispatchSvc);
|
||||
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::IInterfaceAccess)
|
||||
SDV_INTERFACE_ENTRY(sdv::IObjectDestroy)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
/**
|
||||
* @brief Destroy the object. Overload of sdv::IObjectDestroy::DestroyObject.
|
||||
*/
|
||||
virtual void DestroyObject() override;
|
||||
|
||||
/**
|
||||
* @brief When called, enables the transaction as read-transaction. This would only happen when the transaction is still in
|
||||
* undefined state.
|
||||
* @return Returns the read-transaction ID or 0 when the transaction is not registered as read-transaction.
|
||||
*/
|
||||
uint64_t GetReadTransactionID() const;
|
||||
|
||||
/**
|
||||
* @brief When called, enables the transaction as write-transaction. This would only happen when the transaction is still in
|
||||
* undefined state. In that case, the value will be added to the deferred signal map. Any previous value will be overwritten.
|
||||
* @param[in] rSignal Reference to the signal class.
|
||||
* @param[in] ranyVal Reference to the value.
|
||||
*/
|
||||
void DeferWrite(CSignal& rSignal, sdv::any_t& ranyVal);
|
||||
|
||||
/**
|
||||
* @brief Finalizes the transaction. For read transactions, nothing happens. For write transactions, a transaction ID is stored
|
||||
* with the written signal value.
|
||||
*/
|
||||
void FinalizeWrite();
|
||||
|
||||
/**
|
||||
* @brief Set the iterator to this transaction (prevents having to search for the transaction in the transaction list).
|
||||
* @param[in] itTransaction The iterator to store.
|
||||
*/
|
||||
void SetIterator(std::list<CTransaction>::iterator itTransaction);
|
||||
|
||||
/**
|
||||
* @brief Get the stored iterator to the transaction.
|
||||
* @return The stored iterator.
|
||||
*/
|
||||
std::list<CTransaction>::iterator GetIterator() const;
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Transaction type
|
||||
*/
|
||||
enum class ETransactionType
|
||||
{
|
||||
undefined = 0, ///< Transaction type was not defined yet.
|
||||
read_transaction = 1, ///< Read transaction (for TX signals).
|
||||
write_transaction = 2, ///< Write transaction (for RX signals and publishers).
|
||||
};
|
||||
|
||||
sdv::CLifetimeCookie m_cookie = sdv::CreateLifetimeCookie(); ///< Lifetime cookie to manage the module lifetime.
|
||||
CDispatchService& m_rDispatchSvc; ///< Reference to dispatch service.
|
||||
mutable ETransactionType m_eTransactionType = ETransactionType::undefined; ///< Transaction type.
|
||||
std::mutex m_mtxDeferredWriteSignalMap; ///< Deferred write signal map access.
|
||||
std::map<CSignal*, sdv::any_t> m_mapDeferredWriteSignalMap; ///< Deferred write signal map.
|
||||
uint64_t m_uiReadTransactionID = 0ull; ///< Read transaction ID.
|
||||
std::list<CTransaction>::iterator m_itTransaction{}; ///< Iterator of the transaction in the transaction list.
|
||||
};
|
||||
|
||||
#endif // !defined TRANSACTION_H
|
||||
205
sdv_services/data_dispatch_service/trigger.cpp
Normal file
205
sdv_services/data_dispatch_service/trigger.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
#include "dispatchservice.h"
|
||||
#include "trigger.h"
|
||||
#include "signal.h"
|
||||
|
||||
CScheduler::~CScheduler()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
void CScheduler::Start()
|
||||
{
|
||||
// Start the timer at 1ms rate
|
||||
m_timer = sdv::core::CTaskTimer(1, [&]() { EvaluateAndExecute(); });
|
||||
}
|
||||
|
||||
void CScheduler::Stop()
|
||||
{
|
||||
// Stop the timer
|
||||
m_timer.Reset();
|
||||
|
||||
// Clear the schedule lists
|
||||
std::unique_lock<std::mutex> lock(m_mtxScheduler);
|
||||
m_mapTriggers.clear();
|
||||
m_mmapScheduleList.clear();
|
||||
}
|
||||
|
||||
void CScheduler::Schedule(CTrigger* pTrigger, EExecutionFlag eExecFlag, std::chrono::high_resolution_clock::time_point tpDue)
|
||||
{
|
||||
if (!pTrigger) return;
|
||||
if (!m_timer) return;
|
||||
|
||||
// Check whether a job is scheduled already for this trigger object
|
||||
std::unique_lock<std::mutex> lock(m_mtxScheduler);
|
||||
auto itObject = m_mapTriggers.find(pTrigger);
|
||||
if (itObject != m_mapTriggers.end())
|
||||
{
|
||||
// Trigger execution of the object already found. Update the periodic flag if not set necessary
|
||||
if (eExecFlag == EExecutionFlag::spontaneous)
|
||||
itObject->second = eExecFlag;
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert the object into the scheduled object map.
|
||||
itObject = m_mapTriggers.emplace(pTrigger, eExecFlag).first;
|
||||
if (itObject == m_mapTriggers.end()) return; // Should not happen
|
||||
|
||||
// Schedule the execution
|
||||
m_mmapScheduleList.emplace(tpDue, itObject);
|
||||
}
|
||||
|
||||
void CScheduler::EvaluateAndExecute()
|
||||
{
|
||||
// Run until there is nothing to execute for the moment
|
||||
while (true)
|
||||
{
|
||||
// Check the schedule list
|
||||
std::unique_lock<std::mutex> lock(m_mtxScheduler);
|
||||
auto itJob = m_mmapScheduleList.begin();
|
||||
if (itJob == m_mmapScheduleList.end()) return; // Nothing to do
|
||||
|
||||
// Get the current time and check whether to execute
|
||||
std::chrono::high_resolution_clock::time_point tpNow = std::chrono::high_resolution_clock::now();
|
||||
if (tpNow < itJob->first) return; // No time yet
|
||||
|
||||
// This job is being scheduled; copy the information and remove the entries from the scheduler
|
||||
CTrigger* pTrigger = itJob->second->first;
|
||||
EExecutionFlag eExecFlag = itJob->second->second;
|
||||
m_mapTriggers.erase(itJob->second);
|
||||
m_mmapScheduleList.erase(itJob);
|
||||
lock.unlock();
|
||||
|
||||
// Execute the trigger
|
||||
pTrigger->Execute(eExecFlag);
|
||||
}
|
||||
}
|
||||
|
||||
void CScheduler::RemoveFromSchedule(const CTrigger* pTrigger)
|
||||
{
|
||||
// Search for the job in the scheduled list
|
||||
std::unique_lock<std::mutex> lock(m_mtxScheduler);
|
||||
auto itJob = std::find_if(m_mmapScheduleList.begin(), m_mmapScheduleList.end(),
|
||||
[&](const auto& rvtJob) { return rvtJob.second->first == pTrigger; });
|
||||
if (itJob == m_mmapScheduleList.end()) return; // No scheduled jobs found
|
||||
|
||||
// Remove the job
|
||||
m_mapTriggers.erase(itJob->second);
|
||||
m_mmapScheduleList.erase(itJob);
|
||||
}
|
||||
|
||||
CTrigger::CTrigger(CDispatchService& rDispatchSvc, uint32_t uiCycleTime, uint32_t uiDelayTime, uint32_t uiBehaviorFlags,
|
||||
sdv::core::ITxTriggerCallback* pCallback) :
|
||||
m_rDispatchSvc(rDispatchSvc), m_tpLast{}, m_nPeriod(uiCycleTime), m_nDelay(uiDelayTime), m_uiBehaviorFlags(uiBehaviorFlags),
|
||||
m_pCallback(pCallback)
|
||||
{
|
||||
// Start the timer if this triggr should be perodic
|
||||
if (uiCycleTime)
|
||||
m_timer = sdv::core::CTaskTimer(uiCycleTime, [&]() { Execute(EExecutionFlag::periodic); });
|
||||
}
|
||||
|
||||
bool CTrigger::IsValid() const
|
||||
{
|
||||
if (!m_pCallback) return false;
|
||||
if (m_nPeriod && !m_timer) return false;
|
||||
if (!m_nPeriod && !(m_uiBehaviorFlags & static_cast<uint32_t>(sdv::core::ISignalTransmission::ETxTriggerBehavior::spontaneous)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CTrigger::DestroyObject()
|
||||
{
|
||||
// Stop the timer
|
||||
m_timer.Reset();
|
||||
|
||||
// Remove all signals
|
||||
std::unique_lock<std::mutex> lock(m_mtxSignals);
|
||||
while (!m_mapSignals.empty())
|
||||
{
|
||||
CSignal* pSignal = m_mapSignals.begin()->second;
|
||||
m_mapSignals.erase(m_mapSignals.begin());
|
||||
if (!pSignal) continue;
|
||||
lock.unlock();
|
||||
|
||||
pSignal->RemoveTrigger(this);
|
||||
|
||||
lock.lock();
|
||||
}
|
||||
lock.unlock();
|
||||
|
||||
// Remove the object (this will destruct this object).
|
||||
m_rDispatchSvc.RemoveTxTrigger(this);
|
||||
}
|
||||
|
||||
bool CTrigger::AddSignal(/*in*/ const sdv::u8string& ssSignalName)
|
||||
{
|
||||
CSignal* pSignal = m_rDispatchSvc.FindSignal(ssSignalName, sdv::core::ESignalDirection::sigdir_tx);
|
||||
if (pSignal && pSignal->GetDirection() == sdv::core::ESignalDirection::sigdir_tx)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mtxSignals);
|
||||
pSignal->AddTrigger(this);
|
||||
m_mapSignals.insert(std::make_pair(pSignal->GetName(), pSignal));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CTrigger::RemoveSignal(/*in*/ const sdv::u8string& ssSignalName)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mtxSignals);
|
||||
auto itSignal = m_mapSignals.find(ssSignalName);
|
||||
if (itSignal == m_mapSignals.end()) return;
|
||||
CSignal* pSignal = itSignal->second;
|
||||
m_mapSignals.erase(itSignal);
|
||||
lock.unlock();
|
||||
|
||||
if (pSignal) pSignal->RemoveTrigger(this);
|
||||
}
|
||||
|
||||
void CTrigger::Execute(EExecutionFlag eExecFlag /*= EExecutionFlag::spontaneous*/)
|
||||
{
|
||||
if (m_rDispatchSvc.GetStatus() != sdv::EObjectStatus::running) return;
|
||||
|
||||
// Check for allowed execution
|
||||
if (eExecFlag == EExecutionFlag::spontaneous &&
|
||||
!(m_uiBehaviorFlags & static_cast<uint32_t>(sdv::core::ISignalTransmission::ETxTriggerBehavior::spontaneous)))
|
||||
return;
|
||||
|
||||
// Is there a delay time, check for the delay
|
||||
std::chrono::high_resolution_clock::time_point tpNow = std::chrono::high_resolution_clock::now();
|
||||
if (m_nDelay)
|
||||
{
|
||||
// Calculate earliest execution time
|
||||
std::chrono::high_resolution_clock::time_point tpAllowedExecution = m_tpLast + std::chrono::milliseconds(m_nDelay);
|
||||
|
||||
// Is execution allowed already; if not, schedule execution for later.
|
||||
if (tpNow < tpAllowedExecution)
|
||||
{
|
||||
m_rDispatchSvc.GetScheduler().Schedule(this, eExecFlag, tpAllowedExecution);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for the active flag. If periodic send when active is enabled, check the data for its state and update the repition
|
||||
// counter.
|
||||
if (m_uiBehaviorFlags & static_cast<uint32_t>(sdv::core::ISignalTransmission::ETxTriggerBehavior::periodic_if_active))
|
||||
{
|
||||
// Check whether content is active
|
||||
std::unique_lock<std::mutex> lock(m_mtxSignals);
|
||||
bool bDefault = true;
|
||||
for (const auto& rvtSignal : m_mapSignals)
|
||||
bDefault &= rvtSignal.second->EqualsDefaultValue();
|
||||
|
||||
// Reset or increase repetition counter based on activity
|
||||
m_nInactiveRepetition = bDefault ? m_nInactiveRepetition + 1 : 0;
|
||||
|
||||
// Based on the inactive repitions, decide to execute.
|
||||
// FIXED VALUE: Inactive repitions is set to 1... could be defined dynamic in the future
|
||||
if (eExecFlag == EExecutionFlag::periodic && m_nInactiveRepetition > 1) return;
|
||||
}
|
||||
|
||||
// Execution is allowed, update execution timepoint
|
||||
m_tpLast = tpNow;
|
||||
|
||||
// Execute the trigger
|
||||
if (m_pCallback) m_pCallback->Execute();
|
||||
}
|
||||
155
sdv_services/data_dispatch_service/trigger.h
Normal file
155
sdv_services/data_dispatch_service/trigger.h
Normal file
@@ -0,0 +1,155 @@
|
||||
#ifndef TRIGGER_H
|
||||
#define TRIGGER_H
|
||||
|
||||
#include <support/interface_ptr.h>
|
||||
#include <support/timer.h>
|
||||
|
||||
// Forward declaration
|
||||
class CDispatchService;
|
||||
class CSignal;
|
||||
class CTrigger;
|
||||
|
||||
/**
|
||||
* @brief Flag indicating whether the execution is part of a periodic update or is spontaneous.
|
||||
*/
|
||||
enum class EExecutionFlag
|
||||
{
|
||||
periodic, ///< Execution is triggered periodically
|
||||
spontaneous ///< Spontaneous execution triggered
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Scheduler class being used to schedule a deferred trigger execution when the minimal time between execution is undercut.
|
||||
*/
|
||||
class CScheduler
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
CScheduler() = default;
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~CScheduler();
|
||||
|
||||
/**
|
||||
* @brief Start the scheduler. This will start the scheduler timer at a 1ms rate.
|
||||
*/
|
||||
void Start();
|
||||
|
||||
/**
|
||||
* @brief Stop the scheduler. This will stop the scheduler timer and clear all pending schedule jobs.
|
||||
*/
|
||||
void Stop();
|
||||
|
||||
/**
|
||||
* @brief Schedule a new trigger execution for the trigger object.
|
||||
* @details If a scheduled trigger execution already exists for the object, the trigger execution is not scheduled again. If
|
||||
* the already scheduled trigger execution was marked as periodic and the new trigger is a spontenous trigger, the scheduled
|
||||
* execution is updated to represent a spontaneous execution (periodic triggers are sometimes not fired when the data hasn't
|
||||
* changed).
|
||||
* @param[in] pTrigger Pointer to the trigger object.
|
||||
* @param[in] eExecFlag Set the trigger execution flag.
|
||||
* @param[in] tpDue The due time this trigger is to be executed.
|
||||
*/
|
||||
void Schedule(CTrigger* pTrigger, EExecutionFlag eExecFlag, std::chrono::high_resolution_clock::time_point tpDue);
|
||||
|
||||
/**
|
||||
* @brief In case the trigger object will be destroyed, remove all pending schedule jobs from the scheduler.
|
||||
* @param[in] pTrigger Pointer to the trigger object.
|
||||
*/
|
||||
void RemoveFromSchedule(const CTrigger* pTrigger);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Evaluate a potential job execution.
|
||||
*/
|
||||
void EvaluateAndExecute();
|
||||
|
||||
/// Map containing the scheduled objects and the execution flag for the object.
|
||||
using CObjectMap = std::map<CTrigger*, EExecutionFlag>;
|
||||
|
||||
/// Multi-map containing the scheduled target time and an iterator to the entry of the scheduler object map.
|
||||
using CSchedulerMMap = std::multimap<std::chrono::high_resolution_clock::time_point, CObjectMap::iterator>;
|
||||
|
||||
sdv::core::CTaskTimer m_timer; ///< 1ms timer
|
||||
std::mutex m_mtxScheduler; ///< Protection of the schedule list
|
||||
CObjectMap m_mapTriggers; ///< Map with the currently scheduled trigger objects (prevents new
|
||||
///< triggers to be scheduled for the same trigger object).
|
||||
CSchedulerMMap m_mmapScheduleList; ///< Schedule lst.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Trigger object, managing the triggering.
|
||||
*/
|
||||
class CTrigger : public sdv::IInterfaceAccess, public sdv::core::ITxTrigger, public sdv::IObjectDestroy
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param[in] rDispatchSvc Reference to the dispatch service.
|
||||
* @param[in] uiCycleTime When set to any value other than 0, provides a cyclic trigger (ms). Could be 0 if cyclic
|
||||
* triggering is not required.
|
||||
* @param[in] uiDelayTime When set to any value other than 0, ensures a minimum time between two triggers. Could be 0
|
||||
* if minimum time should not be enforced.
|
||||
* @param[in] uiBehaviorFlags Zero or more flags from sdv::core::ETxTriggerBehavior.
|
||||
* @param[in] pCallback Pointer to the callback interface.
|
||||
*/
|
||||
CTrigger(CDispatchService& rDispatchSvc, uint32_t uiCycleTime, uint32_t uiDelayTime, uint32_t uiBehaviorFlags,
|
||||
sdv::core::ITxTriggerCallback* pCallback);
|
||||
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::IInterfaceAccess)
|
||||
SDV_INTERFACE_ENTRY(sdv::core::ITxTrigger)
|
||||
SDV_INTERFACE_ENTRY(sdv::IObjectDestroy)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
/**
|
||||
* @brief Return whether the trigger object is valid.
|
||||
* @return Returns the validity of the trigger.
|
||||
*/
|
||||
bool IsValid() const;
|
||||
|
||||
/**
|
||||
* @brief Destroy the object. Overload of sdv::IObjectDestroy::DestroyObject.
|
||||
*/
|
||||
virtual void DestroyObject() override;
|
||||
|
||||
/**
|
||||
* @brief Add a signal to the trigger object. The signal must be registered as TX signal before.
|
||||
* Overload of sdv::core::ITxTrigger::AddSignal.
|
||||
* @param[in] ssSignalName Name of the signal.
|
||||
* @return Returns whether adding the signal was successful.
|
||||
*/
|
||||
virtual bool AddSignal(/*in*/ const sdv::u8string& ssSignalName) override;
|
||||
|
||||
/**
|
||||
* @brief Remove a signal from the trigger object.Overload of sdv::core::ITxTrigger::RemoveSignal.
|
||||
* @param[in] ssSignalName Name of the signal.
|
||||
*/
|
||||
virtual void RemoveSignal(/*in*/ const sdv::u8string& ssSignalName) override;
|
||||
|
||||
/**
|
||||
* @brief This function is triggered every ms.
|
||||
* @param[in] eExecFlag When set, the trigger was caused by the periodic timer.
|
||||
*/
|
||||
void Execute(EExecutionFlag eExecFlag = EExecutionFlag::spontaneous);
|
||||
|
||||
private:
|
||||
sdv::CLifetimeCookie m_cookie = sdv::CreateLifetimeCookie(); ///< Lifetime cookie to manage the module lifetime.
|
||||
CDispatchService& m_rDispatchSvc; ///< Reference to dispatch service.
|
||||
sdv::core::CTaskTimer m_timer; ///< Task timer event object.
|
||||
std::chrono::high_resolution_clock::time_point m_tpLast; ///< Timepoint of the last trigger
|
||||
size_t m_nPeriod = 0ull; ///< The period to trigger.
|
||||
size_t m_nDelay = 0ull; ///< The minimum delay between two triggers.
|
||||
uint32_t m_uiBehaviorFlags = 0ul; ///< Additional behavior
|
||||
size_t m_nInactiveRepetition = 0ull; ///< Count the amount of inactive executions.
|
||||
sdv::core::ITxTriggerCallback* m_pCallback = nullptr; ///< Callback pointer
|
||||
std::mutex m_mtxSignals; ///< Signal map protection.
|
||||
std::map<sdv::u8string, CSignal*> m_mapSignals; ///< Assigned signals.
|
||||
};
|
||||
|
||||
#endif // ! defined TRIGGER_H
|
||||
Reference in New Issue
Block a user