@@ -19,10 +19,11 @@ static AlertCounts g_currentCounts = {0, 0};
1919static AlertCounts g_previousCounts = {0 , 0 }; // Track previous counts for transitions
2020static uint32_t g_epoch = 0 ;
2121
22- // Rotation state for display
23- static std::vector<SensorID> g_activeList;
24- static bool g_showingCrit = false ;
25- static size_t g_rotateIdx = 0 ;
22+ // Rotation state for display - separate lists for warnings and criticals
23+ static std::vector<SensorID> g_critList;
24+ static std::vector<SensorID> g_warnList;
25+ static size_t g_critRotateIdx = 0 ;
26+ static size_t g_warnRotateIdx = 0 ;
2627static unsigned long g_lastRotateMs = 0 ;
2728
2829// Forward declarations
@@ -56,6 +57,36 @@ void sendAlertEvent(SensorID id, AlertLevel level) {
5657 xQueueSend (alertEventQueue, &ev, 0 ); // best-effort, drop if full
5758}
5859
60+ // Helper to build and send UI update with current state
61+ static void sendUIUpdate () {
62+ AlertUIUpdate update;
63+ update.counts = g_currentCounts;
64+ update.criticalAlertsActive = (g_currentCounts.criticalCount > 0 );
65+ update.updateEpoch = g_epoch;
66+
67+ // Critical display (shows on top, in altitude area)
68+ if (!g_critList.empty ()) {
69+ update.showCritical = true ;
70+ update.criticalId = g_critList[g_critRotateIdx];
71+ update.criticalLevel = g_currentLevels[g_critList[g_critRotateIdx]];
72+ } else {
73+ update.showCritical = false ;
74+ }
75+
76+ // Warning display (shows below critical)
77+ if (!g_warnList.empty ()) {
78+ update.showWarning = true ;
79+ update.warningId = g_warnList[g_warnRotateIdx];
80+ update.warningLevel = g_currentLevels[g_warnList[g_warnRotateIdx]];
81+ } else {
82+ update.showWarning = false ;
83+ }
84+
85+ if (alertUIQueue) {
86+ xQueueOverwrite (alertUIQueue, &update);
87+ }
88+ }
89+
5990// ------------ Internal implementation -------------
6091static void alertAggregationTask (void * parameter) {
6192 AlertEvent ev;
@@ -72,36 +103,28 @@ static void alertAggregationTask(void* parameter) {
72103 recalcCountsAndPublish ();
73104 }
74105
75- // Handle rotation every 2s if active list not empty
76- if (!g_activeList.empty ()) {
106+ // Handle rotation every 2s if either list not empty
107+ bool hasAlerts = !g_critList.empty () || !g_warnList.empty ();
108+ if (hasAlerts) {
77109 unsigned long now = millis ();
78110 if (now - g_lastRotateMs >= 2000 ) {
79111 g_lastRotateMs = now;
80- g_rotateIdx = (g_rotateIdx + 1 ) % g_activeList.size ();
81-
82- // Send unified update with current counts and display info
83- AlertUIUpdate update;
84- update.counts = g_currentCounts;
85- update.showDisplay = true ;
86- update.displayId = g_activeList[g_rotateIdx];
87- update.displayLevel = g_currentLevels[g_activeList[g_rotateIdx]];
88- update.displayCritical = g_showingCrit;
89- update.criticalAlertsActive = (g_currentCounts.criticalCount > 0 );
90- update.updateEpoch = g_epoch;
91- if (alertUIQueue) {
92- xQueueOverwrite (alertUIQueue, &update);
112+
113+ // Rotate both lists independently
114+ if (!g_critList.empty ()) {
115+ g_critRotateIdx = (g_critRotateIdx + 1 ) % g_critList.size ();
93116 }
117+ if (!g_warnList.empty ()) {
118+ g_warnRotateIdx = (g_warnRotateIdx + 1 ) % g_warnList.size ();
119+ }
120+
121+ sendUIUpdate ();
94122 }
95123 } else {
96- // If list empty ensure label hidden once
124+ // If both lists empty, ensure labels hidden once
97125 static bool hideSent = false ;
98126 if (!hideSent) {
99- AlertUIUpdate update;
100- update.counts = g_currentCounts;
101- update.showDisplay = false ;
102- update.criticalAlertsActive = (g_currentCounts.criticalCount > 0 );
103- update.updateEpoch = g_epoch;
104- if (alertUIQueue) xQueueOverwrite (alertUIQueue, &update);
127+ sendUIUpdate ();
105128 hideSent = true ;
106129 }
107130 }
@@ -110,18 +133,18 @@ static void alertAggregationTask(void* parameter) {
110133
111134static void recalcCountsAndPublish () {
112135 AlertCounts counts{0 , 0 };
113- std::vector<SensorID> critList ;
114- std::vector<SensorID> warnList ;
136+ std::vector<SensorID> newCritList ;
137+ std::vector<SensorID> newWarnList ;
115138 for (const auto & kv : g_currentLevels) {
116139 switch (kv.second ) {
117140 case AlertLevel::WARN_LOW:
118141 case AlertLevel::WARN_HIGH:
119- warnList .push_back (kv.first );
142+ newWarnList .push_back (kv.first );
120143 counts.warningCount ++;
121144 break ;
122145 case AlertLevel::CRIT_LOW:
123146 case AlertLevel::CRIT_HIGH:
124- critList .push_back (kv.first );
147+ newCritList .push_back (kv.first );
125148 counts.criticalCount ++;
126149 break ;
127150 default :
@@ -138,48 +161,33 @@ static void recalcCountsAndPublish() {
138161 counts.criticalCount != g_currentCounts.criticalCount );
139162 g_currentCounts = counts;
140163
141- // Update active list for display rotation
142- bool newShowingCrit = !critList. empty ( );
143- const std::vector<SensorID>& newList = newShowingCrit ? critList : warnList ;
164+ // Check if lists changed
165+ bool critListChanged = (newCritList != g_critList );
166+ bool warnListChanged = (newWarnList != g_warnList) ;
144167
145- bool listChanged = (newShowingCrit != g_showingCrit) || (newList != g_activeList);
146-
147- // Send unified update when counts change or display list changes
148- if (countsChanged || listChanged) {
149- if (listChanged) {
150- g_activeList = newList;
151- g_showingCrit = newShowingCrit;
152- g_rotateIdx = 0 ;
153- g_lastRotateMs = millis ();
168+ // Send unified update when counts change or display lists change
169+ if (countsChanged || critListChanged || warnListChanged) {
170+ if (critListChanged) {
171+ g_critList = newCritList;
172+ g_critRotateIdx = 0 ;
154173 }
155-
156- AlertUIUpdate update;
157- update.counts = g_currentCounts;
158- update.criticalAlertsActive = (g_currentCounts.criticalCount > 0 );
159- update.updateEpoch = g_epoch;
160-
161- if (g_activeList.empty ()) {
162- update.showDisplay = false ;
163- } else {
164- update.showDisplay = true ;
165- update.displayId = g_activeList[g_rotateIdx];
166- update.displayLevel = g_currentLevels[g_activeList[g_rotateIdx]];
167- update.displayCritical = g_showingCrit;
174+ if (warnListChanged) {
175+ g_warnList = newWarnList;
176+ g_warnRotateIdx = 0 ;
168177 }
178+ g_lastRotateMs = millis ();
169179
170180 USBSerial.printf (" [Alert] Sending UI update: crit=%d warn=%d critActive=%d\n " ,
171- update. counts .criticalCount , update. counts .warningCount , update. criticalAlertsActive );
181+ counts.criticalCount , counts.warningCount , (counts. criticalCount > 0 ) );
172182
173- if (alertUIQueue) {
174- xQueueOverwrite (alertUIQueue, &update);
175- }
183+ sendUIUpdate ();
176184 }
177185
178186 // Build snapshot for carousel (critical preferred)
179187 AlertSnapshot snap{};
180188 snap.epoch = ++g_epoch;
181- snap.criticalMode = (!critList .empty ()) ? 1 : 0 ;
182- const std::vector<SensorID>& srcList = (!critList .empty ()) ? critList : warnList ;
189+ snap.criticalMode = (!newCritList .empty ()) ? 1 : 0 ;
190+ const std::vector<SensorID>& srcList = (!newCritList .empty ()) ? newCritList : newWarnList ;
183191 snap.count = (uint8_t )std::min<size_t >(srcList.size (), MAX_ALERT_ITEMS);
184192 for (uint8_t i = 0 ; i < snap.count ; ++i) {
185193 snap.ids [i] = srcList[i];
0 commit comments