mirror of
https://github.com/eclipse-openvehicle-api/openvehicle-api.git
synced 2026-02-05 15:18:45 +00:00
206 lines
6.9 KiB
C++
206 lines
6.9 KiB
C++
|
|
#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();
|
||
|
|
}
|