1
- import { useEffect , useState , useCallback } from "react" ;
1
+ import { useEffect , useState , useCallback , useMemo } from "react" ;
2
2
import {
3
3
Box ,
4
4
SplitPanel ,
@@ -22,7 +22,6 @@ interface DeviceMetrics {
22
22
temperature : number ;
23
23
cpuFreq : number ;
24
24
cpuFreqMax : number ;
25
- cpuFreqPct : number ;
26
25
latencyMean : number ;
27
26
latencyP95 : number ;
28
27
fpsMean : number ;
@@ -42,20 +41,44 @@ interface DeviceStatusResponse {
42
41
}
43
42
44
43
const DeviceStatusPanel = ( { isInferenceRunning, setNotifications } : DeviceStatusProps ) => {
45
- // Define thresholds for different metrics
46
- const [ thresholds ] = useState ( {
47
- cpu : {
48
- usage : { warning : 90 , error : 99 } ,
49
- temperature : { warning : 75 , error : 90 } ,
50
- frequency : { warning : 75 , error : 50 } , // Note: For CPU frequency, higher is better
51
- } ,
52
- memory : { warning : 85 , error : 95 } ,
53
- disk : { warning : 90 , error : 95 } ,
54
- performance : {
55
- latency_p95 : { warning : 1.25 , error : 1.5 } ,
56
- fps_mean : { warning : 1.05 , error : 1.1 } ,
57
- } ,
58
- } ) ;
44
+ // Define comparison types
45
+ type ComparisonOperator = "gt" | "lt" | "gte" | "lte" | "eq" ;
46
+
47
+ // Create a function that returns a comparator based on the operator string
48
+ const getComparator = useCallback ( ( op : ComparisonOperator ) => {
49
+ switch ( op ) {
50
+ case "gt" :
51
+ return ( a : number , b : number ) => a > b ;
52
+ case "lt" :
53
+ return ( a : number , b : number ) => a < b ;
54
+ case "gte" :
55
+ return ( a : number , b : number ) => a >= b ;
56
+ case "lte" :
57
+ return ( a : number , b : number ) => a <= b ;
58
+ case "eq" :
59
+ return ( a : number , b : number ) => a === b ;
60
+ default :
61
+ return ( a : number , b : number ) => a > b ; // Default to greater than
62
+ }
63
+ } , [ ] ) ;
64
+
65
+ // Define thresholds for different metrics, wrapped in useMemo to maintain reference equality
66
+ const thresholds = useMemo (
67
+ ( ) => ( {
68
+ cpu : {
69
+ usage : { warning : 90 , error : 99 , compare : "gt" as ComparisonOperator } ,
70
+ temperature : { warning : 75 , error : 90 , compare : "gt" as ComparisonOperator } ,
71
+ frequency : { warning : 85 , error : 75 , compare : "lt" as ComparisonOperator } , // Note: For CPU frequency, higher is better
72
+ } ,
73
+ memory : { warning : 85 , error : 90 , compare : "gt" as ComparisonOperator } ,
74
+ disk : { warning : 90 , error : 95 , compare : "gt" as ComparisonOperator } ,
75
+ performance : {
76
+ latency_p95 : { warning : 1.35 , error : 1.75 , compare : "gt" as ComparisonOperator } ,
77
+ fps_mean : { warning : 1.05 , error : 1.1 , compare : "gt" as ComparisonOperator } ,
78
+ } ,
79
+ } ) ,
80
+ [ ]
81
+ ) ; // Empty dependency array means this will only be calculated once
59
82
60
83
const [ metrics , setMetrics ] = useState < DeviceMetrics > ( {
61
84
cpuUsage : 0 ,
@@ -64,7 +87,6 @@ const DeviceStatusPanel = ({ isInferenceRunning, setNotifications }: DeviceStatu
64
87
temperature : 0 ,
65
88
cpuFreq : 0 ,
66
89
cpuFreqMax : 0 ,
67
- cpuFreqPct : 0 ,
68
90
latencyMean : 0.0 ,
69
91
latencyP95 : 0.0 ,
70
92
fpsMean : 0.0 ,
@@ -116,48 +138,121 @@ const DeviceStatusPanel = ({ isInferenceRunning, setNotifications }: DeviceStatu
116
138
}
117
139
} , [ isInferenceRunning ] ) ;
118
140
119
- // Separate effect for handling warning and error messages based on metrics
120
- useEffect ( ( ) => {
121
- // Check CPU usage
122
- const cpuUsageId = "device-status-cpu-usage" ;
123
- if ( metrics . cpuUsage >= thresholds . cpu . usage . error ) {
124
- addFlashMessage ( cpuUsageId , "CPU Usage is extremely high" , "error" ) ;
125
- } else if ( metrics . cpuUsage >= thresholds . cpu . usage . warning ) {
126
- addFlashMessage ( cpuUsageId , "CPU Usage is getting high" , "warning" ) ;
127
- } else {
128
- removeFlashMessage ( cpuUsageId ) ;
129
- }
141
+ // Updated status check function using the dynamic comparator
142
+ const checkStatus = useCallback (
143
+ (
144
+ value : number ,
145
+ thresholdConfig : { warning : number ; error : number ; compare : ComparisonOperator }
146
+ ) : "success" | "warning" | "error" => {
147
+ const { warning, error, compare } = thresholdConfig ;
148
+ const comparator = getComparator ( compare ) ;
149
+
150
+ if ( ! comparator ( value , warning ) ) return "success" ;
151
+ if ( ! comparator ( value , error ) ) return "warning" ;
152
+ return "error" ;
153
+ } ,
154
+ [ getComparator ]
155
+ ) ;
130
156
131
- // Check CPU temperature
132
- const cpuTempId = "device-status-cpu-temp" ;
133
- if ( metrics . temperature >= thresholds . cpu . temperature . error ) {
134
- addFlashMessage ( cpuTempId , "CPU Temperature is extremely high" , "error" ) ;
135
- } else if ( metrics . temperature >= thresholds . cpu . temperature . warning ) {
136
- addFlashMessage ( cpuTempId , "CPU Temperature is getting high" , "warning" ) ;
137
- } else {
138
- removeFlashMessage ( cpuTempId ) ;
139
- }
157
+ const checkStatusWithInference = useCallback (
158
+ (
159
+ value : number ,
160
+ thresholdConfig : { warning : number ; error : number ; compare : ComparisonOperator } ,
161
+ isInferenceRunning : boolean ,
162
+ updatesSinceInferenceStartedDelay : number ,
163
+ noInferenceStatus : "info" | "stopped" | "pending" = "stopped"
164
+ ) : "info" | "success" | "warning" | "error" | "stopped" | "pending" => {
165
+ if ( ! isInferenceRunning ) return noInferenceStatus ;
166
+ if ( updatesSinceInferenceStarted <= updatesSinceInferenceStartedDelay ) return "pending" ;
167
+ return checkStatus ( value , thresholdConfig ) ;
168
+ } ,
169
+ [ checkStatus , updatesSinceInferenceStarted ]
170
+ ) ;
140
171
141
- // Check memory usage
142
- const memoryUsageId = "device-status-memory-usage" ;
143
- if ( metrics . memoryUsage >= thresholds . memory . error ) {
144
- addFlashMessage ( memoryUsageId , "Memory Usage is extremely high" , "error" ) ;
145
- } else if ( metrics . memoryUsage >= thresholds . memory . warning ) {
146
- addFlashMessage ( memoryUsageId , "Memory Usage is getting high" , "warning" ) ;
147
- } else {
148
- removeFlashMessage ( memoryUsageId ) ;
149
- }
172
+ const allAlerts = useMemo (
173
+ ( ) => ( {
174
+ "device-status-cpu-usage" : {
175
+ metricValue : metrics . cpuUsage ,
176
+ status : checkStatus ( metrics . cpuUsage , thresholds . cpu . usage ) ,
177
+ warningMessage : "CPU Usage is high" ,
178
+ errorMessage : "CPU Usage is extremely high" ,
179
+ } ,
180
+ "device-status-cpu-temp" : {
181
+ metricValue : metrics . temperature ,
182
+ status : checkStatus ( metrics . temperature , thresholds . cpu . temperature ) ,
183
+ warningMessage : "CPU Temperature is high" ,
184
+ errorMessage : "CPU Temperature is extremely high" ,
185
+ } ,
186
+ "device-status-memory-usage" : {
187
+ metricValue : metrics . memoryUsage ,
188
+ status : checkStatus ( metrics . memoryUsage , thresholds . memory ) ,
189
+ warningMessage : "Memory Usage is high" ,
190
+ errorMessage : "Memory Usage is extremely high" ,
191
+ } ,
192
+ "device-status-disk-usage" : {
193
+ metricValue : metrics . diskUsage ,
194
+ status : checkStatus ( metrics . diskUsage , thresholds . disk ) ,
195
+ warningMessage : "Disk Usage is high" ,
196
+ errorMessage : "Disk Usage is extremely high" ,
197
+ } ,
198
+ "device-status-cpu-freq" : {
199
+ metricValue : ( metrics . cpuFreq / metrics . cpuFreqMax ) * 100.0 ,
200
+ status : checkStatusWithInference (
201
+ ( metrics . cpuFreq / metrics . cpuFreqMax ) * 100.0 ,
202
+ thresholds . cpu . frequency ,
203
+ isInferenceRunning ,
204
+ 2 ,
205
+ "info"
206
+ ) ,
207
+ warningMessage : "CPU Frequency is low" ,
208
+ errorMessage : "CPU Frequency is critically low" ,
209
+ updateDelay : 2 ,
210
+ noInferenceStatus : "info" as "info" | "stopped" | "pending" ,
211
+ } ,
212
+ "device-status-latency-p95" : {
213
+ metricValue : metrics . latencyP95 / metrics . latencyMean ,
214
+ status : checkStatusWithInference (
215
+ metrics . latencyP95 / metrics . latencyMean ,
216
+ thresholds . performance . latency_p95 ,
217
+ isInferenceRunning ,
218
+ 2 ,
219
+ "stopped"
220
+ ) ,
221
+ warningMessage : "95% Latency is high" ,
222
+ errorMessage : "95% Latency is critically high" ,
223
+ updateDelay : 2 ,
224
+ noInferenceStatus : "stopped" as "info" | "stopped" | "pending" ,
225
+ } ,
226
+ "device-status-fps-mean" : {
227
+ metricValue : 30.0 / metrics . fpsMean ,
228
+ status : checkStatusWithInference (
229
+ 30.0 / metrics . fpsMean ,
230
+ thresholds . performance . fps_mean ,
231
+ isInferenceRunning ,
232
+ 2 ,
233
+ "stopped"
234
+ ) ,
235
+ warningMessage : "Frame Rate is low" ,
236
+ errorMessage : "Frame Rate is critically low" ,
237
+ updateDelay : 2 ,
238
+ noInferenceStatus : "stopped" as "info" | "stopped" | "pending" ,
239
+ } ,
240
+ } ) ,
241
+ [ metrics , thresholds , checkStatus , checkStatusWithInference , isInferenceRunning ]
242
+ ) ;
150
243
151
- // Check disk usage
152
- const diskUsageId = "device-status-disk-usage" ;
153
- if ( metrics . diskUsage >= thresholds . disk . error ) {
154
- addFlashMessage ( diskUsageId , "Disk Usage is extremely high" , "error" ) ;
155
- } else if ( metrics . diskUsage >= thresholds . disk . warning ) {
156
- addFlashMessage ( diskUsageId , "Disk Usage is getting high" , "warning" ) ;
157
- } else {
158
- removeFlashMessage ( diskUsageId ) ;
159
- }
160
- } , [ metrics , addFlashMessage , removeFlashMessage , thresholds ] ) ;
244
+ // Separate effects for handling warning and error messages based on metrics
245
+ useEffect ( ( ) => {
246
+ Object . entries ( allAlerts ) . forEach ( ( [ alertId , data ] ) => {
247
+ if ( data . status === "error" ) {
248
+ addFlashMessage ( alertId , data . errorMessage , "error" ) ;
249
+ } else if ( data . status === "warning" ) {
250
+ addFlashMessage ( alertId , data . warningMessage , "warning" ) ;
251
+ } else {
252
+ removeFlashMessage ( alertId ) ;
253
+ }
254
+ } ) ;
255
+ } , [ addFlashMessage , removeFlashMessage , allAlerts ] ) ;
161
256
162
257
// Count updates since inference started
163
258
useEffect ( ( ) => {
@@ -180,7 +275,6 @@ const DeviceStatusPanel = ({ isInferenceRunning, setNotifications }: DeviceStatu
180
275
temperature : parseFloat ( response . cpu_temp . toFixed ( 1 ) ) ,
181
276
cpuFreq : parseFloat ( response . cpu_freq . toFixed ( 0 ) ) ,
182
277
cpuFreqMax : parseFloat ( response . cpu_freq_max . toFixed ( 0 ) ) ,
183
- cpuFreqPct : ( response . cpu_freq / response . cpu_freq_max ) * 100 ,
184
278
latencyMean : parseFloat ( response . latency_mean . toFixed ( 1 ) ) ,
185
279
latencyP95 : parseFloat ( response . latency_p95 . toFixed ( 1 ) ) ,
186
280
fpsMean : parseFloat ( response . fps_mean . toFixed ( 1 ) ) ,
@@ -197,42 +291,6 @@ const DeviceStatusPanel = ({ isInferenceRunning, setNotifications }: DeviceStatu
197
291
return ( ) => clearInterval ( intervalId ) ;
198
292
} , [ ] ) ; // Remove updatesSinceInferenceStarted from dependency array
199
293
200
- const checkStatus = (
201
- value : number ,
202
- thresholdPair : [ number , number ]
203
- ) : "success" | "warning" | "error" => {
204
- const [ warning , error ] = thresholdPair ;
205
- if ( value < warning ) return "success" ;
206
- if ( value < error ) return "warning" ;
207
- return "error" ;
208
- } ;
209
-
210
- const getCPUStatusType = (
211
- value : number ,
212
- thresholdPair : [ number , number ] ,
213
- isInferenceRunning : boolean
214
- ) : "info" | "success" | "warning" | "error" => {
215
- if ( ! isInferenceRunning ) return "info" ;
216
- const [ warning , error ] = thresholdPair ;
217
- if ( value > warning ) return "success" ;
218
- if ( value > error ) return "warning" ;
219
- return "error" ;
220
- } ;
221
-
222
- const checkPerformanceStatus = (
223
- value : number ,
224
- referenceValue : number ,
225
- thresholdPair : [ number , number ] ,
226
- isInferenceRunning : boolean
227
- ) : "success" | "warning" | "error" | "stopped" | "pending" => {
228
- if ( ! isInferenceRunning ) return "stopped" ;
229
- if ( updatesSinceInferenceStarted <= 3 ) return "pending" ;
230
- const [ warning , error ] = thresholdPair ;
231
- if ( value / referenceValue < warning ) return "success" ;
232
- if ( value / referenceValue < error ) return "warning" ;
233
- return "error" ;
234
- } ;
235
-
236
294
return (
237
295
< SplitPanel header = { "Car Health" } hidePreferencesButton = { true } closeBehavior = "collapse" >
238
296
< SpaceBetween size = "xs" direction = "vertical" >
@@ -248,33 +306,17 @@ const DeviceStatusPanel = ({ isInferenceRunning, setNotifications }: DeviceStatu
248
306
< Box variant = "h4" > CPU</ Box >
249
307
< div style = { { display : "grid" , gridTemplateColumns : "100px auto" , rowGap : "6px" } } >
250
308
< Box > Usage:</ Box >
251
- < StatusIndicator
252
- type = { checkStatus ( metrics . cpuUsage , [
253
- thresholds . cpu . usage . warning ,
254
- thresholds . cpu . usage . error ,
255
- ] ) }
256
- >
309
+ < StatusIndicator type = { allAlerts [ "device-status-cpu-usage" ] . status } >
257
310
{ metrics . cpuUsage } %
258
311
</ StatusIndicator >
259
312
260
313
< Box > Temperature:</ Box >
261
- < StatusIndicator
262
- type = { checkStatus ( metrics . temperature , [
263
- thresholds . cpu . temperature . warning ,
264
- thresholds . cpu . temperature . error ,
265
- ] ) }
266
- >
314
+ < StatusIndicator type = { allAlerts [ "device-status-cpu-temp" ] . status } >
267
315
{ metrics . temperature } °C
268
316
</ StatusIndicator >
269
317
270
318
< Box > Frequency:</ Box >
271
- < StatusIndicator
272
- type = { getCPUStatusType (
273
- metrics . cpuFreqPct ,
274
- [ thresholds . cpu . frequency . warning , thresholds . cpu . frequency . error ] ,
275
- isInferenceRunning && updatesSinceInferenceStarted > 2
276
- ) }
277
- >
319
+ < StatusIndicator type = { allAlerts [ "device-status-cpu-freq" ] . status } >
278
320
{ metrics . cpuFreq } MHz / { metrics . cpuFreqMax } MHz
279
321
</ StatusIndicator >
280
322
</ div >
@@ -285,22 +327,12 @@ const DeviceStatusPanel = ({ isInferenceRunning, setNotifications }: DeviceStatu
285
327
< Box variant = "h4" > Memory Usage</ Box >
286
328
< div style = { { display : "grid" , gridTemplateColumns : "100px auto" , rowGap : "6px" } } >
287
329
< Box > RAM:</ Box >
288
- < StatusIndicator
289
- type = { checkStatus ( metrics . memoryUsage , [
290
- thresholds . memory . warning ,
291
- thresholds . memory . error ,
292
- ] ) }
293
- >
330
+ < StatusIndicator type = { allAlerts [ "device-status-memory-usage" ] . status } >
294
331
{ metrics . memoryUsage } %
295
332
</ StatusIndicator >
296
333
297
334
< Box > Disk:</ Box >
298
- < StatusIndicator
299
- type = { checkStatus ( metrics . diskUsage , [
300
- thresholds . disk . warning ,
301
- thresholds . disk . error ,
302
- ] ) }
303
- >
335
+ < StatusIndicator type = { allAlerts [ "device-status-disk-usage" ] . status } >
304
336
{ metrics . diskUsage } %
305
337
</ StatusIndicator >
306
338
</ div >
@@ -311,38 +343,19 @@ const DeviceStatusPanel = ({ isInferenceRunning, setNotifications }: DeviceStatu
311
343
< Box variant = "h4" > Performance</ Box >
312
344
< div style = { { display : "grid" , gridTemplateColumns : "100px auto" , rowGap : "6px" } } >
313
345
< Box > Mean Latency:</ Box >
314
- < StatusIndicator
315
- type = { isInferenceRunning ? "info" : "stopped" }
316
- >
346
+ < StatusIndicator type = { isInferenceRunning ? "info" : "stopped" } >
317
347
{ metrics . latencyMean . toFixed ( 1 ) } ms
318
- </ StatusIndicator >
348
+ </ StatusIndicator >
319
349
< Box > 95% Latency:</ Box >
320
- < StatusIndicator
321
- type = { checkPerformanceStatus (
322
- metrics . latencyP95 ,
323
- metrics . latencyMean ,
324
- [ thresholds . performance . latency_p95 . warning , thresholds . performance . latency_p95 . error ] ,
325
- isInferenceRunning
326
- ) }
327
- >
350
+ < StatusIndicator type = { allAlerts [ "device-status-latency-p95" ] . status } >
328
351
{ metrics . latencyP95 . toFixed ( 1 ) } ms
329
352
</ StatusIndicator >
330
353
< Box > Frame Rate:</ Box >
331
- < StatusIndicator
332
- type = { checkPerformanceStatus (
333
- 30.0 ,
334
- metrics . fpsMean ,
335
- [ thresholds . performance . fps_mean . warning , thresholds . performance . fps_mean . error ] ,
336
- isInferenceRunning
337
- ) }
338
- >
354
+ < StatusIndicator type = { allAlerts [ "device-status-fps-mean" ] . status } >
339
355
{ metrics . fpsMean . toFixed ( 1 ) } fps
340
356
</ StatusIndicator >
341
357
</ div >
342
358
</ SpaceBetween >
343
-
344
- { /* Empty grid cell for layout balance */ }
345
- < div > </ div >
346
359
</ Grid >
347
360
</ SpaceBetween >
348
361
</ SplitPanel >
0 commit comments