From e8ffe6b782c3d815d88459d3fb79b95d655bd0c9 Mon Sep 17 00:00:00 2001 From: Jason Millard Date: Mon, 15 Sep 2025 21:50:13 -0400 Subject: [PATCH] misc: rework alarmhandler to use ported C# action class --- include/DOF/DOF.h | 2 +- src/Log.cpp | 32 +++++++-- src/cab/out/pac/PacDriveSingleton.cpp | 38 +++++++++- .../MatrixBitmapAnimationEffectBase.h | 5 +- src/fx/matrixfx/MatrixFlickerEffectBase.h | 13 ++-- src/fx/matrixfx/MatrixPlasmaEffectBase.h | 3 +- src/fx/matrixfx/MatrixShiftEffectBase.h | 9 ++- src/fx/timmedfx/BlinkEffect.cpp | 2 +- src/fx/timmedfx/BlinkEffect.h | 3 +- src/fx/timmedfx/DelayEffect.cpp | 4 +- src/fx/timmedfx/DelayEffect.h | 5 +- src/fx/timmedfx/DurationEffect.cpp | 5 +- src/fx/timmedfx/DurationEffect.h | 2 + src/fx/timmedfx/ExtendDurationEffect.cpp | 38 +++++----- src/fx/timmedfx/ExtendDurationEffect.h | 14 ++-- src/fx/timmedfx/FadeEffect.cpp | 10 +-- src/fx/timmedfx/FadeEffect.h | 2 + src/fx/timmedfx/MaxDurationEffect.cpp | 53 +++++++------- src/fx/timmedfx/MaxDurationEffect.h | 8 ++- src/fx/timmedfx/MinDurationEffect.cpp | 33 ++++----- src/fx/timmedfx/MinDurationEffect.h | 6 +- src/ledcontrol/setup/Configurator.cpp | 4 +- src/pinballsupport/Action.h | 71 +++++++++++++++++++ src/pinballsupport/AlarmHandler.cpp | 45 ++++++++++-- src/pinballsupport/AlarmHandler.h | 37 ++++++++-- src/test.cpp | 1 + 26 files changed, 316 insertions(+), 129 deletions(-) create mode 100644 src/pinballsupport/Action.h diff --git a/include/DOF/DOF.h b/include/DOF/DOF.h index b21aa35..7676a25 100644 --- a/include/DOF/DOF.h +++ b/include/DOF/DOF.h @@ -2,7 +2,7 @@ #define LIBDOF_VERSION_MAJOR 0 // X Digits #define LIBDOF_VERSION_MINOR 4 // Max 2 Digits -#define LIBDOF_VERSION_PATCH 3 // Max 2 Digits +#define LIBDOF_VERSION_PATCH 4 // Max 2 Digits #define _LIBDOF_STR(x) #x #define LIBDOF_STR(x) _LIBDOF_STR(x) diff --git a/src/Log.cpp b/src/Log.cpp index 693c0c9..e64df6e 100644 --- a/src/Log.cpp +++ b/src/Log.cpp @@ -78,7 +78,13 @@ void Log::Init(bool enableLogging) auto now = std::chrono::system_clock::now(); auto time_t = std::chrono::system_clock::to_time_t(now); std::stringstream ss; - ss << std::put_time(std::localtime(&time_t), "%Y.%m.%d %H:%M"); + struct tm localTime1; +#ifdef _WIN32 + localtime_s(&localTime1, &time_t); + ss << std::put_time(&localTime1, "%Y.%m.%d %H:%M"); +#else + ss << std::put_time(localtime_r(&time_t, &localTime1), "%Y.%m.%d %H:%M"); +#endif m_logger << StringExtensions::Build("libdof (DirectOutput Framework) - Version {0}, built {1}", LIBDOF_VERSION, ss.str()) << std::endl; m_logger << "DOF created by SwissLizard / MIT License" << std::endl; @@ -89,7 +95,13 @@ void Log::Init(bool enableLogging) auto timeT = std::chrono::system_clock::to_time_t(nowMs); auto ms = std::chrono::duration_cast(nowMs.time_since_epoch()) % 1000; std::stringstream timestamp; - timestamp << std::put_time(std::localtime(&timeT), "%Y.%m.%d %H:%M:%S"); + struct tm localTime2; +#ifdef _WIN32 + localtime_s(&localTime2, &timeT); + timestamp << std::put_time(&localTime2, "%Y.%m.%d %H:%M:%S"); +#else + timestamp << std::put_time(localtime_r(&timeT, &localTime2), "%Y.%m.%d %H:%M:%S"); +#endif timestamp << "." << std::setfill('0') << std::setw(3) << ms.count(); m_logger << timestamp.str() << "\t" << StringExtensions::Build("DirectOutput logger initialized{0}", instrumentationsEnabledNote) << std::endl; @@ -173,7 +185,13 @@ void Log::WriteToFile(const std::string& message) auto timeT = std::chrono::system_clock::to_time_t(nowMs); auto ms = std::chrono::duration_cast(nowMs.time_since_epoch()) % 1000; std::stringstream timestamp; - timestamp << std::put_time(std::localtime(&timeT), "%Y.%m.%d %H:%M:%S"); + struct tm localTime3; +#ifdef _WIN32 + localtime_s(&localTime3, &timeT); + timestamp << std::put_time(&localTime3, "%Y.%m.%d %H:%M:%S"); +#else + timestamp << std::put_time(localtime_r(&timeT, &localTime3), "%Y.%m.%d %H:%M:%S"); +#endif timestamp << "." << std::setfill('0') << std::setw(3) << ms.count(); WriteRaw(timestamp.str() + "\t"); } @@ -190,7 +208,13 @@ void Log::WriteToFile(const std::string& message) auto timeT = std::chrono::system_clock::to_time_t(nowMs); auto ms = std::chrono::duration_cast(nowMs.time_since_epoch()) % 1000; std::stringstream timestamp; - timestamp << std::put_time(std::localtime(&timeT), "%Y.%m.%d %H:%M:%S"); + struct tm localTime4; +#ifdef _WIN32 + localtime_s(&localTime4, &timeT); + timestamp << std::put_time(&localTime4, "%Y.%m.%d %H:%M:%S"); +#else + timestamp << std::put_time(localtime_r(&timeT, &localTime4), "%Y.%m.%d %H:%M:%S"); +#endif timestamp << "." << std::setfill('0') << std::setw(3) << ms.count(); WriteRaw(timestamp.str() + "\t" + line); } diff --git a/src/cab/out/pac/PacDriveSingleton.cpp b/src/cab/out/pac/PacDriveSingleton.cpp index 082ddd3..341a9f1 100644 --- a/src/cab/out/pac/PacDriveSingleton.cpp +++ b/src/cab/out/pac/PacDriveSingleton.cpp @@ -82,7 +82,41 @@ void PacDriveSingleton::EnumerateDevices() if (currentDevice->serial_number) { std::wstring wserial(currentDevice->serial_number); - info.serial = std::string(wserial.begin(), wserial.end()); +#ifdef _WIN32 + int size = WideCharToMultiByte(CP_UTF8, 0, wserial.c_str(), -1, nullptr, 0, nullptr, nullptr); + if (size > 0) + { + info.serial.resize(size - 1); + WideCharToMultiByte(CP_UTF8, 0, wserial.c_str(), -1, &info.serial[0], size, nullptr, nullptr); + } +#else + info.serial.clear(); + for (wchar_t wc : wserial) + { + if (wc <= 0x7F) + { + info.serial.push_back(static_cast(wc)); + } + else if (wc <= 0x7FF) + { + info.serial.push_back(static_cast(0xC0 | ((wc >> 6) & 0x1F))); + info.serial.push_back(static_cast(0x80 | (wc & 0x3F))); + } + else if (wc <= 0xFFFF) + { + info.serial.push_back(static_cast(0xE0 | ((wc >> 12) & 0x0F))); + info.serial.push_back(static_cast(0x80 | ((wc >> 6) & 0x3F))); + info.serial.push_back(static_cast(0x80 | (wc & 0x3F))); + } + else + { + info.serial.push_back(static_cast(0xF0 | ((wc >> 18) & 0x07))); + info.serial.push_back(static_cast(0x80 | ((wc >> 12) & 0x3F))); + info.serial.push_back(static_cast(0x80 | ((wc >> 6) & 0x3F))); + info.serial.push_back(static_cast(0x80 | (wc & 0x3F))); + } + } +#endif } else { @@ -580,4 +614,4 @@ void PacDriveSingleton::CloseUsbDevice(int index) } } -} \ No newline at end of file +} diff --git a/src/fx/matrixfx/MatrixBitmapAnimationEffectBase.h b/src/fx/matrixfx/MatrixBitmapAnimationEffectBase.h index 0d8ce1b..86b85e4 100644 --- a/src/fx/matrixfx/MatrixBitmapAnimationEffectBase.h +++ b/src/fx/matrixfx/MatrixBitmapAnimationEffectBase.h @@ -6,6 +6,7 @@ #include "../../general/StringExtensions.h" #include "../../Log.h" #include "../../pinballsupport/AlarmHandler.h" +#include "../../pinballsupport/Action.h" #include "../../general/bitmap/PixelData.h" #include #include @@ -95,7 +96,7 @@ template void MatrixBitmapAnimationEffectBasem_table->GetPinball()->GetAlarms()->RegisterIntervalAlarm(m_animationFrameDurationMs, this, [this]() { this->Animate(); }); + this->m_table->GetPinball()->GetAlarms()->RegisterIntervalAlarm(m_animationFrameDurationMs, Action(this, &MatrixBitmapAnimationEffectBase::Animate)); Animate(); } @@ -112,7 +113,7 @@ template void MatrixBitmapAnimationEffectBasem_table->GetPinball()->GetAlarms()->UnregisterIntervalAlarm(this); + this->m_table->GetPinball()->GetAlarms()->UnregisterIntervalAlarm(Action(this, &MatrixBitmapAnimationEffectBase::Animate)); } catch (...) { diff --git a/src/fx/matrixfx/MatrixFlickerEffectBase.h b/src/fx/matrixfx/MatrixFlickerEffectBase.h index 0670755..cffd067 100644 --- a/src/fx/matrixfx/MatrixFlickerEffectBase.h +++ b/src/fx/matrixfx/MatrixFlickerEffectBase.h @@ -7,6 +7,7 @@ #include "../../table/Table.h" #include "../../Pinball.h" #include "../../pinballsupport/AlarmHandler.h" +#include "../../pinballsupport/Action.h" #include #include #include @@ -58,7 +59,7 @@ template class MatrixFlickerEffectBase : public Mat int m_currentValue; TableElementData* m_flickerTableElementData; std::mt19937 m_randomGenerator; - std::function m_intervalAlarmCallback; + Action m_intervalAlarmCallback; struct FlickerObject { @@ -85,7 +86,7 @@ MatrixFlickerEffectBase::MatrixFlickerEffectBase() , m_currentValue(0) , m_flickerTableElementData(nullptr) , m_randomGenerator(std::random_device {}()) - , m_intervalAlarmCallback(nullptr) + , m_intervalAlarmCallback() { } @@ -137,9 +138,9 @@ template void MatrixFlickerEffectBaseDoFlicker(); }; + m_intervalAlarmCallback = Action(this, &MatrixFlickerEffectBase::DoFlicker); } - this->m_table->GetPinball()->GetAlarms()->RegisterIntervalAlarm(30, this, m_intervalAlarmCallback); + this->m_table->GetPinball()->GetAlarms()->RegisterIntervalAlarm(30, m_intervalAlarmCallback); m_active = true; } @@ -240,8 +241,8 @@ template void MatrixFlickerEffectBasem_table->GetPinball()->GetAlarms()->UnregisterIntervalAlarm(this); - m_intervalAlarmCallback = nullptr; + this->m_table->GetPinball()->GetAlarms()->UnregisterIntervalAlarm(m_intervalAlarmCallback); + m_intervalAlarmCallback = Action(); } m_active = false; } diff --git a/src/fx/matrixfx/MatrixPlasmaEffectBase.h b/src/fx/matrixfx/MatrixPlasmaEffectBase.h index 6be974e..eec0062 100644 --- a/src/fx/matrixfx/MatrixPlasmaEffectBase.h +++ b/src/fx/matrixfx/MatrixPlasmaEffectBase.h @@ -7,6 +7,7 @@ #include "../../table/Table.h" #include "../../Pinball.h" #include "../../pinballsupport/AlarmHandler.h" +#include "../../pinballsupport/Action.h" #include #include @@ -120,7 +121,7 @@ template void MatrixPlasmaEffectBasem_table->GetPinball()->GetAlarms()->RegisterAlarm(stepDelayMs, [this]() { this->PlasmaStep(); }, false); + this->m_table->GetPinball()->GetAlarms()->RegisterAlarm(stepDelayMs, Action(this, &MatrixPlasmaEffectBase::PlasmaStep), false); } template void MatrixPlasmaEffectBase::UpdateMatrix() diff --git a/src/fx/matrixfx/MatrixShiftEffectBase.h b/src/fx/matrixfx/MatrixShiftEffectBase.h index ff205bd..7d2822e 100644 --- a/src/fx/matrixfx/MatrixShiftEffectBase.h +++ b/src/fx/matrixfx/MatrixShiftEffectBase.h @@ -8,6 +8,7 @@ #include "../../table/Table.h" #include "../../Pinball.h" #include "../../pinballsupport/AlarmHandler.h" +#include "../../pinballsupport/Action.h" #include #include @@ -51,6 +52,7 @@ template class MatrixShiftEffectBase : public Matri int m_lastDiscardedValue; int m_currentStep; bool m_active; + Action m_stepCallback; }; @@ -63,6 +65,7 @@ MatrixShiftEffectBase::MatrixShiftEffectBase() , m_lastDiscardedValue(0) , m_currentStep(0) , m_active(false) + , m_stepCallback(this, &MatrixShiftEffectBase::DoStep) { } @@ -91,7 +94,7 @@ template void MatrixShiftEffectBasem_table->GetPinball()->GetAlarms()->RegisterIntervalAlarm(RefreshIntervalMs, this, [this]() { this->DoStep(); }); + this->m_table->GetPinball()->GetAlarms()->RegisterIntervalAlarm(RefreshIntervalMs, m_stepCallback); m_active = true; } @@ -215,7 +218,7 @@ template void MatrixShiftEffectBasem_table->GetPinball()->GetAlarms()->UnregisterIntervalAlarm(this); + this->m_table->GetPinball()->GetAlarms()->UnregisterIntervalAlarm(m_stepCallback); m_lastDiscardedValue = 0; m_currentStep = 0; m_active = false; @@ -245,7 +248,7 @@ template void MatrixShiftEffectBasem_table->GetPinball()->GetAlarms()->UnregisterIntervalAlarm(this); + this->m_table->GetPinball()->GetAlarms()->UnregisterIntervalAlarm(m_stepCallback); } catch (...) { diff --git a/src/fx/timmedfx/BlinkEffect.cpp b/src/fx/timmedfx/BlinkEffect.cpp index 6a22d58..387d9e9 100644 --- a/src/fx/timmedfx/BlinkEffect.cpp +++ b/src/fx/timmedfx/BlinkEffect.cpp @@ -22,8 +22,8 @@ BlinkEffect::BlinkEffect() , m_blinkState(false) , m_blinkOrgTableElementDataValue(1) , m_blinkTableElementData() + , m_alarmCallback(this, &BlinkEffect::DoBlink) { - m_alarmCallback = [this]() { this->DoBlink(); }; } BlinkEffect::~BlinkEffect() { } diff --git a/src/fx/timmedfx/BlinkEffect.h b/src/fx/timmedfx/BlinkEffect.h index bacfdcf..e8f0f7f 100644 --- a/src/fx/timmedfx/BlinkEffect.h +++ b/src/fx/timmedfx/BlinkEffect.h @@ -3,6 +3,7 @@ #include "../EffectEffectBase.h" #include "BlinkEffectUntriggerBehaviourEnum.h" #include "../../table/TableElementData.h" +#include "../../pinballsupport/Action.h" #include namespace DOF @@ -47,7 +48,7 @@ class BlinkEffect : public EffectEffectBase int m_blinkOrgTableElementDataValue; TableElementData m_blinkTableElementData; - std::function m_alarmCallback; + Action m_alarmCallback; }; } \ No newline at end of file diff --git a/src/fx/timmedfx/DelayEffect.cpp b/src/fx/timmedfx/DelayEffect.cpp index bf5fed9..bae2a40 100644 --- a/src/fx/timmedfx/DelayEffect.cpp +++ b/src/fx/timmedfx/DelayEffect.cpp @@ -12,6 +12,7 @@ namespace DOF DelayEffect::DelayEffect() : m_delayMs(0) , m_delayTableElementData() + , m_afterDelayCallback(this, &DelayEffect::AfterDelay) { } @@ -24,7 +25,6 @@ void DelayEffect::Trigger(TableElementData* tableElementData) if (m_delayMs > 0) { m_delayTableElementData = *tableElementData; - m_afterDelayCallback = [this]() { this->AfterDelay(&m_delayTableElementData); }; m_table->GetPinball()->GetAlarms()->RegisterAlarm(m_delayMs, m_afterDelayCallback, true); } else @@ -34,7 +34,7 @@ void DelayEffect::Trigger(TableElementData* tableElementData) } } -void DelayEffect::AfterDelay(TableElementData* data) { TriggerTargetEffect(data); } +void DelayEffect::AfterDelay() { TriggerTargetEffect(&m_delayTableElementData); } void DelayEffect::Init(Table* table) { EffectEffectBase::Init(table); } diff --git a/src/fx/timmedfx/DelayEffect.h b/src/fx/timmedfx/DelayEffect.h index 0cbadab..a4303d0 100644 --- a/src/fx/timmedfx/DelayEffect.h +++ b/src/fx/timmedfx/DelayEffect.h @@ -2,6 +2,7 @@ #include "../EffectEffectBase.h" #include "../../table/TableElementData.h" +#include "../../pinballsupport/Action.h" #include namespace DOF @@ -22,10 +23,10 @@ class DelayEffect : public EffectEffectBase virtual std::string GetXmlElementName() const override { return "DelayEffect"; } private: - void AfterDelay(TableElementData* data); + void AfterDelay(); int m_delayMs; - std::function m_afterDelayCallback; + Action m_afterDelayCallback; TableElementData m_delayTableElementData; }; diff --git a/src/fx/timmedfx/DurationEffect.cpp b/src/fx/timmedfx/DurationEffect.cpp index c51f799..42c427c 100644 --- a/src/fx/timmedfx/DurationEffect.cpp +++ b/src/fx/timmedfx/DurationEffect.cpp @@ -12,6 +12,7 @@ DurationEffect::DurationEffect() , m_durationMs(500) , m_active(false) , m_durationTableElementData() + , m_durationEndCallback(this, &DurationEffect::DurationEnd) { } @@ -23,13 +24,13 @@ void DurationEffect::Trigger(TableElementData* tableElementData) { TriggerTargetEffect(tableElementData); m_durationTableElementData = *tableElementData; - m_table->GetPinball()->GetAlarms()->RegisterAlarm(m_durationMs, [this]() { this->DurationEnd(); }); + m_table->GetPinball()->GetAlarms()->RegisterAlarm(m_durationMs, m_durationEndCallback); m_active = true; } else if (m_retriggerBehaviour == RetriggerBehaviourEnum::Restart) { m_durationTableElementData = *tableElementData; - m_table->GetPinball()->GetAlarms()->RegisterAlarm(m_durationMs, [this]() { this->DurationEnd(); }); + m_table->GetPinball()->GetAlarms()->RegisterAlarm(m_durationMs, m_durationEndCallback); } } } diff --git a/src/fx/timmedfx/DurationEffect.h b/src/fx/timmedfx/DurationEffect.h index c447794..6dad18b 100644 --- a/src/fx/timmedfx/DurationEffect.h +++ b/src/fx/timmedfx/DurationEffect.h @@ -3,6 +3,7 @@ #include "../EffectEffectBase.h" #include "../RetriggerBehaviourEnum.h" #include "../../table/TableElementData.h" +#include "../../pinballsupport/Action.h" namespace DOF { @@ -33,6 +34,7 @@ class DurationEffect : public EffectEffectBase int m_durationMs; bool m_active; TableElementData m_durationTableElementData; + Action m_durationEndCallback; }; } \ No newline at end of file diff --git a/src/fx/timmedfx/ExtendDurationEffect.cpp b/src/fx/timmedfx/ExtendDurationEffect.cpp index b21bdc9..cf32e4c 100644 --- a/src/fx/timmedfx/ExtendDurationEffect.cpp +++ b/src/fx/timmedfx/ExtendDurationEffect.cpp @@ -3,57 +3,55 @@ #include "../../table/Table.h" #include "../../Pinball.h" #include "../../pinballsupport/AlarmHandler.h" +#include "../../general/MathExtensions.h" +#include namespace DOF { ExtendDurationEffect::ExtendDurationEffect() - : m_retriggerBehaviour(RetriggerBehaviourEnum::Restart) - , m_durationMs(500) - , m_active(false) - , m_durationTimerTableElementData() + : m_durationMs(500) + , m_delayedData() + , m_extendedDurationEndCallback(this, &ExtendDurationEffect::ExtendedDurationEnd) { } +void ExtendDurationEffect::SetDurationMs(int value) { m_durationMs = MathExtensions::Limit(value, 0, INT_MAX); } + void ExtendDurationEffect::Trigger(TableElementData* tableElementData) { if (m_targetEffect != nullptr) { - TriggerTargetEffect(tableElementData); - if (tableElementData->m_value != 0) { - if (!m_active) + TriggerTargetEffect(tableElementData); + } + else + { + if (m_durationMs > 0) { - m_durationTimerTableElementData = *tableElementData; - m_table->GetPinball()->GetAlarms()->RegisterAlarm(m_durationMs, [this]() { this->ExtendDurationEnd(&m_durationTimerTableElementData); }, true); - m_active = true; + m_delayedData = *tableElementData; + m_table->GetPinball()->GetAlarms()->RegisterAlarm(m_durationMs, m_extendedDurationEndCallback, true); } - else if (m_retriggerBehaviour == RetriggerBehaviourEnum::Restart) + else { - m_durationTimerTableElementData = *tableElementData; - m_table->GetPinball()->GetAlarms()->RegisterAlarm(m_durationMs, [this]() { this->ExtendDurationEnd(&m_durationTimerTableElementData); }, true); + TriggerTargetEffect(tableElementData); } } } } -void ExtendDurationEffect::ExtendDurationEnd(TableElementData* tableElementData) -{ - tableElementData->m_value = 0; - TriggerTargetEffect(tableElementData); - m_active = false; -} +void ExtendDurationEffect::ExtendedDurationEnd() { TriggerTargetEffect(&m_delayedData); } void ExtendDurationEffect::Finish() { try { + m_table->GetPinball()->GetAlarms()->UnregisterAlarm(m_extendedDurationEndCallback); } catch (...) { } - m_active = false; EffectEffectBase::Finish(); } diff --git a/src/fx/timmedfx/ExtendDurationEffect.h b/src/fx/timmedfx/ExtendDurationEffect.h index 2061a33..502841c 100644 --- a/src/fx/timmedfx/ExtendDurationEffect.h +++ b/src/fx/timmedfx/ExtendDurationEffect.h @@ -1,8 +1,8 @@ #pragma once #include "../EffectEffectBase.h" -#include "../RetriggerBehaviourEnum.h" #include "../../table/TableElementData.h" +#include "../../pinballsupport/Action.h" namespace DOF { @@ -13,22 +13,18 @@ class ExtendDurationEffect : public EffectEffectBase ExtendDurationEffect(); virtual ~ExtendDurationEffect() = default; - RetriggerBehaviourEnum GetRetriggerBehaviour() const { return m_retriggerBehaviour; } - void SetRetriggerBehaviour(RetriggerBehaviourEnum value) { m_retriggerBehaviour = value; } int GetDurationMs() const { return m_durationMs; } - void SetDurationMs(int value) { m_durationMs = value; } - bool GetActive() const { return m_active; } + void SetDurationMs(int value); virtual void Trigger(TableElementData* tableElementData) override; virtual void Finish() override; virtual std::string GetXmlElementName() const override { return "ExtendDurationEffect"; } private: - void ExtendDurationEnd(TableElementData* tableElementData); + void ExtendedDurationEnd(); - RetriggerBehaviourEnum m_retriggerBehaviour; int m_durationMs; - bool m_active; - TableElementData m_durationTimerTableElementData; + TableElementData m_delayedData; + Action m_extendedDurationEndCallback; }; } \ No newline at end of file diff --git a/src/fx/timmedfx/FadeEffect.cpp b/src/fx/timmedfx/FadeEffect.cpp index d012d93..6c8f066 100644 --- a/src/fx/timmedfx/FadeEffect.cpp +++ b/src/fx/timmedfx/FadeEffect.cpp @@ -3,6 +3,7 @@ #include "../../table/Table.h" #include "../../Pinball.h" #include "../../pinballsupport/AlarmHandler.h" +#include "../../pinballsupport/Action.h" #include "../../general/MathExtensions.h" #include "../../general/StringExtensions.h" #include "../../Log.h" @@ -20,6 +21,7 @@ FadeEffect::FadeEffect() , m_stepValue(0) , m_lastTargetTriggerValue(-1) , m_tableElementData() + , m_fadingCallback(this, &FadeEffect::FadingStep) { } @@ -48,7 +50,7 @@ void FadeEffect::Trigger(TableElementData* tableElementData) } else { - m_table->GetPinball()->GetAlarms()->UnregisterIntervalAlarm(this); + m_table->GetPinball()->GetAlarms()->UnregisterIntervalAlarm(m_fadingCallback); m_currentValue = m_targetValue; m_lastTargetTriggerValue = -1; TriggerTargetEffect(&m_tableElementData); @@ -62,11 +64,11 @@ void FadeEffect::FadingStep() if ((m_currentValue < m_targetValue && m_stepValue > 0) || (m_currentValue > m_targetValue && m_stepValue < 0)) { - m_table->GetPinball()->GetAlarms()->RegisterIntervalAlarm(FadingRefreshIntervalMs, this, [this]() { this->FadingStep(); }); + m_table->GetPinball()->GetAlarms()->RegisterIntervalAlarm(FadingRefreshIntervalMs, m_fadingCallback); } else { - m_table->GetPinball()->GetAlarms()->UnregisterIntervalAlarm(this); + m_table->GetPinball()->GetAlarms()->UnregisterIntervalAlarm(m_fadingCallback); m_currentValue = m_targetValue; } @@ -82,7 +84,7 @@ void FadeEffect::Finish() { try { - m_table->GetPinball()->GetAlarms()->UnregisterIntervalAlarm(this); + m_table->GetPinball()->GetAlarms()->UnregisterIntervalAlarm(m_fadingCallback); } catch (...) { diff --git a/src/fx/timmedfx/FadeEffect.h b/src/fx/timmedfx/FadeEffect.h index ac89300..3f09b85 100644 --- a/src/fx/timmedfx/FadeEffect.h +++ b/src/fx/timmedfx/FadeEffect.h @@ -3,6 +3,7 @@ #include "../EffectEffectBase.h" #include "FadeEffectDurationModeEnum.h" #include "../../table/TableElementData.h" +#include "../../pinballsupport/Action.h" namespace DOF { @@ -38,6 +39,7 @@ class FadeEffect : public EffectEffectBase float m_stepValue; int m_lastTargetTriggerValue; TableElementData m_tableElementData; + Action m_fadingCallback; }; } \ No newline at end of file diff --git a/src/fx/timmedfx/MaxDurationEffect.cpp b/src/fx/timmedfx/MaxDurationEffect.cpp index 1db6bec..54ec2c9 100644 --- a/src/fx/timmedfx/MaxDurationEffect.cpp +++ b/src/fx/timmedfx/MaxDurationEffect.cpp @@ -3,53 +3,50 @@ #include "../../table/Table.h" #include "../../Pinball.h" #include "../../pinballsupport/AlarmHandler.h" +#include "../../general/MathExtensions.h" +#include namespace DOF { MaxDurationEffect::MaxDurationEffect() : m_retriggerBehaviour(RetriggerBehaviourEnum::Restart) - , m_maxDurationMs(5000) + , m_maxDurationMs(500) , m_active(false) - , m_durationTimerTableElementData() + , m_untriggerData() + , m_durationEndCallback(this, &MaxDurationEffect::DurationEnd) { } +void MaxDurationEffect::SetMaxDurationMs(int value) { m_maxDurationMs = MathExtensions::Limit(value, 1, INT_MAX); } + void MaxDurationEffect::Trigger(TableElementData* tableElementData) { - if (m_targetEffect != nullptr) + if (tableElementData->m_value != 0) { - if (tableElementData->m_value != 0) + if (!m_active || m_retriggerBehaviour == RetriggerBehaviourEnum::Restart) { - if (!m_active) - { - TriggerTargetEffect(tableElementData); - m_durationTimerTableElementData = *tableElementData; - m_table->GetPinball()->GetAlarms()->RegisterAlarm(m_maxDurationMs, [this]() { this->MaxDurationReached(&m_durationTimerTableElementData); }, true); - m_active = true; - } - else if (m_retriggerBehaviour == RetriggerBehaviourEnum::Restart) - { - TriggerTargetEffect(tableElementData); - m_durationTimerTableElementData = *tableElementData; - m_table->GetPinball()->GetAlarms()->RegisterAlarm(m_maxDurationMs, [this]() { this->MaxDurationReached(&m_durationTimerTableElementData); }, true); - } + TriggerTargetEffect(tableElementData); + m_untriggerData = *tableElementData; + m_untriggerData.m_value = 0; + m_table->GetPinball()->GetAlarms()->RegisterAlarm(m_maxDurationMs, m_durationEndCallback); + m_active = true; } - else + } + else + { + if (m_active && m_untriggerData.m_tableElementType == tableElementData->m_tableElementType && m_untriggerData.m_number == tableElementData->m_number) { - if (m_active) - { - TriggerTargetEffect(tableElementData); - m_active = false; - } + TriggerTargetEffect(tableElementData); + m_table->GetPinball()->GetAlarms()->UnregisterAlarm(m_durationEndCallback); + m_active = false; } } } -void MaxDurationEffect::MaxDurationReached(TableElementData* tableElementData) +void MaxDurationEffect::DurationEnd() { - tableElementData->m_value = 0; - TriggerTargetEffect(tableElementData); + TriggerTargetEffect(&m_untriggerData); m_active = false; } @@ -57,6 +54,10 @@ void MaxDurationEffect::Finish() { try { + if (m_table && m_table->GetPinball() && m_table->GetPinball()->GetAlarms()) + { + m_table->GetPinball()->GetAlarms()->UnregisterAlarm(m_durationEndCallback); + } } catch (...) { diff --git a/src/fx/timmedfx/MaxDurationEffect.h b/src/fx/timmedfx/MaxDurationEffect.h index efbd2cd..9f0bbdf 100644 --- a/src/fx/timmedfx/MaxDurationEffect.h +++ b/src/fx/timmedfx/MaxDurationEffect.h @@ -3,6 +3,7 @@ #include "../EffectEffectBase.h" #include "../RetriggerBehaviourEnum.h" #include "../../table/TableElementData.h" +#include "../../pinballsupport/Action.h" namespace DOF { @@ -16,19 +17,20 @@ class MaxDurationEffect : public EffectEffectBase RetriggerBehaviourEnum GetRetriggerBehaviour() const { return m_retriggerBehaviour; } void SetRetriggerBehaviour(RetriggerBehaviourEnum value) { m_retriggerBehaviour = value; } int GetMaxDurationMs() const { return m_maxDurationMs; } - void SetMaxDurationMs(int value) { m_maxDurationMs = value; } + void SetMaxDurationMs(int value); bool GetActive() const { return m_active; } virtual void Trigger(TableElementData* tableElementData) override; virtual void Finish() override; virtual std::string GetXmlElementName() const override { return "MaxDurationEffect"; } private: - void MaxDurationReached(TableElementData* tableElementData); + void DurationEnd(); RetriggerBehaviourEnum m_retriggerBehaviour; int m_maxDurationMs; bool m_active; - TableElementData m_durationTimerTableElementData; + TableElementData m_untriggerData; + Action m_durationEndCallback; }; } \ No newline at end of file diff --git a/src/fx/timmedfx/MinDurationEffect.cpp b/src/fx/timmedfx/MinDurationEffect.cpp index 0c30bcb..911d75a 100644 --- a/src/fx/timmedfx/MinDurationEffect.cpp +++ b/src/fx/timmedfx/MinDurationEffect.cpp @@ -11,8 +11,9 @@ MinDurationEffect::MinDurationEffect() : m_retriggerBehaviour(RetriggerBehaviourEnum::Restart) , m_minDurationMs(500) , m_active(false) - , m_untriggered(false) - , m_durationTimerTableElementData() + , m_untriggerData() + , m_durationStart() + , m_minDurationEndCallback(this, &MinDurationEffect::MinDurationEnd) { } @@ -25,61 +26,51 @@ void MinDurationEffect::Trigger(TableElementData* tableElementData) if (!m_active || m_retriggerBehaviour == RetriggerBehaviourEnum::Restart) { m_durationStart = std::chrono::steady_clock::now(); - m_durationTimerTableElementData = *tableElementData; + m_untriggerData = *tableElementData; TriggerTargetEffect(tableElementData); m_active = true; } } else { - if (m_active && m_durationTimerTableElementData.m_tableElementType == tableElementData->m_tableElementType && m_durationTimerTableElementData.m_number == tableElementData->m_number) + if (m_active && m_untriggerData.m_tableElementType == tableElementData->m_tableElementType && m_untriggerData.m_number == tableElementData->m_number) { - auto now = std::chrono::steady_clock::now(); - auto elapsed = std::chrono::duration_cast(now - m_durationStart).count(); - + auto elapsed = std::chrono::duration_cast(std::chrono::steady_clock::now() - m_durationStart).count(); if (elapsed >= m_minDurationMs) { MinDurationEnd(); } else { - int remainingMs = m_minDurationMs - static_cast(elapsed); - m_table->GetPinball()->GetAlarms()->RegisterAlarm(remainingMs, [this]() { this->MinDurationEnd(); }, true); + int remainingMs = m_minDurationMs - (int)elapsed; + m_table->GetPinball()->GetAlarms()->RegisterAlarm(remainingMs, m_minDurationEndCallback); } } } } } - void MinDurationEffect::MinDurationEnd() { if (m_active) { - TableElementData endData = m_durationTimerTableElementData; - endData.m_value = 0; - TriggerTargetEffect(&endData); + TableElementData d = m_untriggerData; + d.m_value = 0; + TriggerTargetEffect(&d); } - m_active = false; - m_untriggered = false; } void MinDurationEffect::Finish() { try { - if (m_table && m_table->GetPinball() && m_table->GetPinball()->GetAlarms()) - { - // Note: UnregisterAlarm with lambdas doesn't work reliably, but this matches C# behavior - // The alarm will naturally expire or be cleaned up by AlarmHandler - } + m_table->GetPinball()->GetAlarms()->UnregisterAlarm(m_minDurationEndCallback); } catch (...) { } m_active = false; - m_untriggered = false; EffectEffectBase::Finish(); } diff --git a/src/fx/timmedfx/MinDurationEffect.h b/src/fx/timmedfx/MinDurationEffect.h index ac97a13..8699180 100644 --- a/src/fx/timmedfx/MinDurationEffect.h +++ b/src/fx/timmedfx/MinDurationEffect.h @@ -3,6 +3,7 @@ #include "../EffectEffectBase.h" #include "../RetriggerBehaviourEnum.h" #include "../../table/TableElementData.h" +#include "../../pinballsupport/Action.h" #include namespace DOF @@ -24,15 +25,14 @@ class MinDurationEffect : public EffectEffectBase virtual std::string GetXmlElementName() const override { return "MinDurationEffect"; } private: - void MinDurationReached(TableElementData* tableElementData); void MinDurationEnd(); RetriggerBehaviourEnum m_retriggerBehaviour; int m_minDurationMs; bool m_active; - bool m_untriggered; - TableElementData m_durationTimerTableElementData; + TableElementData m_untriggerData; std::chrono::steady_clock::time_point m_durationStart; + Action m_minDurationEndCallback; }; } \ No newline at end of file diff --git a/src/ledcontrol/setup/Configurator.cpp b/src/ledcontrol/setup/Configurator.cpp index 8600dc4..61e8dcc 100644 --- a/src/ledcontrol/setup/Configurator.cpp +++ b/src/ledcontrol/setup/Configurator.cpp @@ -907,9 +907,7 @@ void Configurator::SetupTable( switch (tcs->GetOutputControl()) { - case OutputControlEnum::FixedOn: - table->GetAssignedStaticEffects()->Add(finalEffect->GetName()); - break; + case OutputControlEnum::FixedOn: table->GetAssignedStaticEffects()->Add(finalEffect->GetName()); break; case OutputControlEnum::Controlled: if (!StringExtensions::IsNullOrWhiteSpace(tcs->GetTableElement())) { diff --git a/src/pinballsupport/Action.h b/src/pinballsupport/Action.h new file mode 100644 index 0000000..0f3323f --- /dev/null +++ b/src/pinballsupport/Action.h @@ -0,0 +1,71 @@ +#pragma once + +#include + +namespace DOF +{ + +class Action +{ +public: + Action() + : m_target(nullptr) + , m_method(nullptr) + , m_hasParameter(false) + , m_parameter(nullptr) + { + } + + template + Action(T* instance, void (T::*method)()) + : m_target(static_cast(instance)) + , m_method(reinterpret_cast(method)) + , m_hasParameter(false) + , m_parameter(nullptr) + , m_function([instance, method]() { (instance->*method)(); }) + { + } + + template + Action(T* instance, void (T::*method)(P*), P* parameter) + : m_target(static_cast(instance)) + , m_method(reinterpret_cast(method)) + , m_hasParameter(true) + , m_parameter(static_cast(parameter)) + , m_parameterizedFunction([instance, method](void* param) { (instance->*method)(static_cast(param)); }) + { + } + + Action(const Action& other) = default; + Action& operator=(const Action& other) = default; + + void operator()() const + { + if (!m_hasParameter && m_function) + m_function(); + } + + void operator()(void* parameter) const + { + if (m_hasParameter && m_parameterizedFunction) + m_parameterizedFunction(parameter); + } + + bool operator==(const Action& other) const { return m_target == other.m_target && m_method == other.m_method && m_hasParameter == other.m_hasParameter; } + + bool operator!=(const Action& other) const { return !(*this == other); } + + explicit operator bool() const { return m_target && ((!m_hasParameter && m_function) || (m_hasParameter && m_parameterizedFunction)); } + + bool HasParameter() const { return m_hasParameter; } + +private: + void* m_target; + void* m_method; + bool m_hasParameter; + void* m_parameter; + std::function m_function; + std::function m_parameterizedFunction; +}; + +} \ No newline at end of file diff --git a/src/pinballsupport/AlarmHandler.cpp b/src/pinballsupport/AlarmHandler.cpp index 5bfecc8..aab99ea 100644 --- a/src/pinballsupport/AlarmHandler.cpp +++ b/src/pinballsupport/AlarmHandler.cpp @@ -37,7 +37,7 @@ void AlarmHandler::UnregisterAlarm(AlarmCallback alarmHandler) auto it = m_alarmList.begin(); while (it != m_alarmList.end()) { - if (it->alarmHandler.target() == alarmHandler.target()) + if (it->alarmHandler == alarmHandler) { it = m_alarmList.erase(it); } @@ -95,7 +95,7 @@ bool AlarmHandler::ProcessAlarms(TimePoint alarmTime) { try { - alarm.alarmHandler(); + alarm.Execute(); } catch (...) { @@ -105,22 +105,22 @@ bool AlarmHandler::ProcessAlarms(TimePoint alarmTime) return alarmsExecuted; } -void AlarmHandler::RegisterIntervalAlarm(int intervalMs, void* owner, AlarmCallback intervalAlarmHandler) +void AlarmHandler::RegisterIntervalAlarm(int intervalMs, AlarmCallback intervalAlarmHandler) { std::lock_guard lock(m_intervalAlarmMutex); - UnregisterIntervalAlarm(owner); - m_intervalAlarmList.emplace_back(intervalMs, intervalAlarmHandler, owner); + UnregisterIntervalAlarm(intervalAlarmHandler); + m_intervalAlarmList.emplace_back(intervalMs, intervalAlarmHandler); } -void AlarmHandler::UnregisterIntervalAlarm(void* owner) +void AlarmHandler::UnregisterIntervalAlarm(AlarmCallback intervalAlarmHandler) { std::lock_guard lock(m_intervalAlarmMutex); auto it = m_intervalAlarmList.begin(); while (it != m_intervalAlarmList.end()) { - if (it->owner == owner) + if (it->intervalAlarmHandler == intervalAlarmHandler) { it = m_intervalAlarmList.erase(it); } @@ -200,4 +200,35 @@ bool AlarmHandler::ProcessIntervalAlarms(TimePoint alarmTime) return alarmsExecuted; } +void AlarmHandler::RegisterAlarm(int durationMs, AlarmCallback alarmHandler, void* parameter, bool dontUnregister) +{ + std::lock_guard lock(m_alarmMutex); + + if (!dontUnregister) + { + UnregisterAlarm(alarmHandler, parameter); + } + + TimePoint alarmTime = std::chrono::steady_clock::now() + std::chrono::milliseconds(durationMs); + m_alarmList.emplace_back(alarmTime, alarmHandler, parameter, dontUnregister); +} + +void AlarmHandler::UnregisterAlarm(AlarmCallback alarmHandler, void* parameter) +{ + std::lock_guard lock(m_alarmMutex); + + auto it = m_alarmList.begin(); + while (it != m_alarmList.end()) + { + if (it->alarmHandler == alarmHandler && it->hasParameter && it->parameter == parameter) + { + it = m_alarmList.erase(it); + } + else + { + ++it; + } + } +} + } \ No newline at end of file diff --git a/src/pinballsupport/AlarmHandler.h b/src/pinballsupport/AlarmHandler.h index 0b467fd..235c4ec 100644 --- a/src/pinballsupport/AlarmHandler.h +++ b/src/pinballsupport/AlarmHandler.h @@ -1,10 +1,12 @@ #pragma once #include "DOF/DOF.h" +#include "Action.h" #include #include #include #include +#include namespace DOF { @@ -15,7 +17,7 @@ class Pinball; class AlarmHandler { public: - using AlarmCallback = std::function; + using AlarmCallback = Action; using TimePoint = std::chrono::steady_clock::time_point; AlarmHandler(); @@ -23,10 +25,14 @@ class AlarmHandler void Init(Pinball* pPinball); void Finish(); + void RegisterAlarm(int durationMs, AlarmCallback alarmHandler, bool dontUnregister = false); - void RegisterIntervalAlarm(int intervalMs, void* owner, AlarmCallback intervalAlarmHandler); void UnregisterAlarm(AlarmCallback alarmHandler); - void UnregisterIntervalAlarm(void* owner); + void RegisterAlarm(int durationMs, AlarmCallback alarmHandler, void* parameter, bool dontUnregister = false); + void UnregisterAlarm(AlarmCallback alarmHandler, void* parameter); + + void RegisterIntervalAlarm(int intervalMs, AlarmCallback intervalAlarmHandler); + void UnregisterIntervalAlarm(AlarmCallback intervalAlarmHandler); TimePoint GetNextAlarmTime(); bool ExecuteAlarms(TimePoint alarmTime); @@ -35,14 +41,35 @@ class AlarmHandler { TimePoint alarmTime; AlarmCallback alarmHandler; + void* parameter; + bool hasParameter; bool dontUnregister; AlarmSetting(TimePoint time, AlarmCallback handler, bool dontUnreg = false) : alarmTime(time) , alarmHandler(handler) + , parameter(nullptr) + , hasParameter(false) , dontUnregister(dontUnreg) { } + + AlarmSetting(TimePoint time, AlarmCallback handler, void* param, bool dontUnreg = false) + : alarmTime(time) + , alarmHandler(handler) + , parameter(param) + , hasParameter(true) + , dontUnregister(dontUnreg) + { + } + + void Execute() const + { + if (hasParameter) + alarmHandler(parameter); + else + alarmHandler(); + } }; struct IntervalAlarmSetting @@ -50,13 +77,11 @@ class AlarmHandler int intervalMs; TimePoint nextAlarm; AlarmCallback intervalAlarmHandler; - void* owner; - IntervalAlarmSetting(int interval, AlarmCallback handler, void* ownerPtr = nullptr) + IntervalAlarmSetting(int interval, AlarmCallback handler) : intervalMs(interval) , intervalAlarmHandler(handler) , nextAlarm(std::chrono::steady_clock::now() + std::chrono::milliseconds(interval)) - , owner(ownerPtr) { } }; diff --git a/src/test.cpp b/src/test.cpp index 6e3e366..f004f13 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -123,6 +123,7 @@ void RunIJTests(DOF::DOF* pDof) std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT_START_DELAY)); + TriggerOutputOnOff(pDof, 'L', 88, 5000); TriggerOutputOnOff(pDof, 'S', 9); TriggerOutputOnOff(pDof, 'S', 12); TriggerOutputOnOff(pDof, 'S', 51);