Repository navigation
Expand file tree
/
Copy pathalert_manager.cpp
More file actions
105 lines (94 loc) · 3.85 KB
/
Copy pathalert_manager.cpp
File metadata and controls
105 lines (94 loc) · 3.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// AlertManager 実装(ステップ5)。
// 通知閾値のエッジ検出 + 非ブロッキング振動パターン再生。
// patternByName() のパターン定義は正式版。
#include "alert_manager.hpp"
#include <M5Unified.h>
void AlertManager::begin(AppConfig& config) {
this->_config = &config;
}
void AlertManager::update(uint32_t remainingMs, bool timerRunning) {
// 閾値チェックは走行中のみ(停止中の設定変更やリセットの残り時間ジャンプで
// 発火させない)。振動再生は発火後に停止しても最後まで再生を続ける
if (timerRunning) {
this->checkThresholds(remainingMs);
}
this->updateVibration();
}
void AlertManager::onTimerReset() {
this->_hasPrev = false;
}
void AlertManager::playPattern(const VibrationPattern& pattern) {
if (pattern.count == 0) {
return;
}
// 再生中でも新しいパターンで上書きする(同時通過時は後着=より緊急側が勝つ)
this->_current = pattern;
this->_remainingCycles = pattern.count;
this->_playing = true;
this->_vibrating = true;
M5.Power.setVibration(pattern.strength);
this->_phaseChangeAtMs = millis() + pattern.onMs;
}
VibrationPattern AlertManager::shortPattern(uint8_t count) {
return VibrationPattern{200, 150, 150, count};
}
VibrationPattern AlertManager::timeUpPattern(uint8_t count) {
return VibrationPattern{255, 400, 250, count};
}
VibrationPattern AlertManager::tickPattern() {
// ダイヤルのディテント用。通知と混同しないよう、ごく短く弱くする
return VibrationPattern{160, 25, 0, 1};
}
void AlertManager::checkThresholds(uint32_t remainingMs) {
// 「前フレーム残り時間 > 閾値 >=
// 今フレーム残り時間」のエッジで一度だけ発火。
// リセットで残り時間が増える方向のジャンプは条件を満たさないため安全。
// 振動回数は決め打ち: リスト先頭(一番早い通知)から 1回, 2回, ...
const auto& alerts = this->_config->alerts();
for (size_t i = 0; i < alerts.size(); ++i) {
const auto& alert = alerts[i];
if (!alert.enabled) {
continue;
}
const uint32_t thresholdMs = alert.beforeSec * 1000UL;
if (this->_hasPrev && this->_prevRemainingMs > thresholdMs &&
remainingMs <= thresholdMs) {
M5_LOGI("AlertManager: fire %u sec before (short x%u)",
alert.beforeSec, (unsigned)(i + 1));
this->playPattern(shortPattern((uint8_t)(i + 1)));
}
}
// 時間切れ(残り0到達)は通知設定の ON/OFF によらず、
// 長振動 x(通知数+1) で通知する
if (this->_hasPrev && this->_prevRemainingMs > 0 && remainingMs == 0) {
const uint8_t count = (uint8_t)(alerts.size() + 1);
M5_LOGI("AlertManager: time up (long x%u)", count);
this->playPattern(timeUpPattern(count));
}
this->_prevRemainingMs = remainingMs;
this->_hasPrev = true;
}
void AlertManager::updateVibration() {
if (!this->_playing) {
return;
}
if ((int32_t)(millis() - this->_phaseChangeAtMs) < 0) {
return; // 現在のフェーズ継続中
}
if (this->_vibrating) {
// ON フェーズ終了 → 振動停止。残りサイクルがあれば休止フェーズへ
M5.Power.setVibration(0);
this->_vibrating = false;
--this->_remainingCycles;
if (this->_remainingCycles == 0) {
this->_playing = false;
} else {
this->_phaseChangeAtMs = millis() + this->_current.offMs;
}
} else {
// OFF フェーズ終了 → 次のサイクルの振動を開始
M5.Power.setVibration(this->_current.strength);
this->_vibrating = true;
this->_phaseChangeAtMs = millis() + this->_current.onMs;
}
}