@@ -11,15 +11,13 @@ use ratatui::{prelude::*, widgets::*};
1111
1212use crate :: config:: { Config , ViewType } ;
1313use crate :: metrics:: { Metrics , Sampler , zero_div} ;
14- use crate :: {
15- metrics:: { MemMetrics , TempMetrics } ,
16- sources:: SocInfo ,
17- } ;
14+ use crate :: { metrics:: MemMetrics , sources:: SocInfo } ;
1815
1916type WithError < T > = Result < T , Box < dyn std:: error:: Error > > ;
2017
2118const GB : u64 = 1024 * 1024 * 1024 ;
2219const MAX_SPARKLINE : usize = 128 ;
20+ const MAX_TEMPS : usize = 8 ;
2321
2422// MARK: Term utils
2523
@@ -44,14 +42,6 @@ fn leave_term() {
4442
4543// MARK: Storage
4644
47- fn items_add < T > ( vec : & mut Vec < T > , val : T ) -> & Vec < T > {
48- vec. insert ( 0 , val) ;
49- if vec. len ( ) > MAX_SPARKLINE {
50- vec. pop ( ) ;
51- }
52- vec
53- }
54-
5545#[ derive( Debug , Default ) ]
5646struct FreqStore {
5747 items : Vec < u64 > , // from 0 to 100
@@ -61,7 +51,9 @@ struct FreqStore {
6151
6252impl FreqStore {
6353 fn push ( & mut self , value : u64 , usage : f64 ) {
64- items_add ( & mut self . items , ( usage * 100.0 ) as u64 ) ;
54+ self . items . insert ( 0 , ( usage * 100.0 ) as u64 ) ;
55+ self . items . truncate ( MAX_SPARKLINE ) ;
56+
6557 self . top_value = value;
6658 self . usage = usage;
6759 }
@@ -77,8 +69,11 @@ struct PowerStore {
7769
7870impl PowerStore {
7971 fn push ( & mut self , value : f64 ) {
80- let was_top = if self . items . len ( ) > 0 { self . items [ 0 ] as f64 / 1000.0 } else { 0.0 } ;
81- items_add ( & mut self . items , ( value * 1000.0 ) as u64 ) ;
72+ let was_top = if !self . items . is_empty ( ) { self . items [ 0 ] as f64 / 1000.0 } else { 0.0 } ;
73+
74+ self . items . insert ( 0 , ( value * 1000.0 ) as u64 ) ;
75+ self . items . truncate ( MAX_SPARKLINE ) ;
76+
8277 self . top_value = avg2 ( was_top, value) ;
8378 self . avg_value = self . items . iter ( ) . sum :: < u64 > ( ) as f64 / self . items . len ( ) as f64 / 1000.0 ;
8479 self . max_value = self . items . iter ( ) . max ( ) . map_or ( 0 , |v| * v) as f64 / 1000.0 ;
@@ -97,7 +92,9 @@ struct MemoryStore {
9792
9893impl MemoryStore {
9994 fn push ( & mut self , value : MemMetrics ) {
100- items_add ( & mut self . items , value. ram_usage ) ;
95+ self . items . insert ( 0 , value. ram_usage ) ;
96+ self . items . truncate ( MAX_SPARKLINE ) ;
97+
10198 self . ram_usage = value. ram_usage ;
10299 self . ram_total = value. ram_total ;
103100 self . swap_usage = value. swap_usage ;
@@ -106,6 +103,46 @@ impl MemoryStore {
106103 }
107104}
108105
106+ #[ derive( Debug , Default ) ]
107+ struct TempStore {
108+ items : Vec < f32 > ,
109+ }
110+
111+ impl TempStore {
112+ fn last ( & self ) -> f32 {
113+ * self . items . first ( ) . unwrap_or ( & 0.0 )
114+ }
115+
116+ fn push ( & mut self , value : f32 ) {
117+ // https://www.tunabellysoftware.com/blog/files/tg-pro-apple-silicon-m3-series-support.html
118+ // https://github.com/vladkens/macmon/issues/12
119+ let value = if value == 0.0 { self . trend_ema ( 0.8 ) } else { value } ;
120+ if value == 0.0 {
121+ return ; // skip if not sensor available
122+ }
123+
124+ self . items . insert ( 0 , value) ;
125+ self . items . truncate ( MAX_TEMPS ) ;
126+ }
127+
128+ // https://en.wikipedia.org/wiki/Exponential_smoothing
129+ fn trend_ema ( & self , alpha : f32 ) -> f32 {
130+ if self . items . len ( ) < 2 {
131+ return 0.0 ;
132+ }
133+
134+ // starts from most recent value, so need to be reversed
135+ let mut iter = self . items . iter ( ) . rev ( ) ;
136+ let mut ema = * iter. next ( ) . unwrap_or ( & 0.0 ) ;
137+
138+ for & item in iter {
139+ ema = alpha * item + ( 1.0 - alpha) * ema;
140+ }
141+
142+ ema
143+ }
144+ }
145+
109146// MARK: Components
110147
111148fn h_stack ( area : Rect ) -> ( Rect , Rect ) {
@@ -178,7 +215,7 @@ fn run_sampler_thread(tx: mpsc::Sender<Event>, msec: Arc<RwLock<u32>>) {
178215 } ) ;
179216}
180217
181- // get avaerage of two values, used to smooth out metrics
218+ // get average of two values, used to smooth out metrics
182219// see: https://github.com/vladkens/macmon/issues/10
183220fn avg2 < T : num_traits:: Float > ( a : T , b : T ) -> T {
184221 return if a == T :: zero ( ) { b } else { ( a + b) / T :: from ( 2.0 ) . unwrap ( ) } ;
@@ -192,14 +229,16 @@ pub struct App {
192229
193230 soc : SocInfo ,
194231 mem : MemoryStore ,
195- temp : TempMetrics ,
196232
197233 cpu_power : PowerStore ,
198234 gpu_power : PowerStore ,
199235 ane_power : PowerStore ,
200236 all_power : PowerStore ,
201237 sys_power : PowerStore ,
202238
239+ cpu_temp : TempStore ,
240+ gpu_temp : TempStore ,
241+
203242 ecpu_freq : FreqStore ,
204243 pcpu_freq : FreqStore ,
205244 igpu_freq : FreqStore ,
@@ -222,8 +261,8 @@ impl App {
222261 self . pcpu_freq . push ( data. pcpu_usage . 0 as u64 , data. pcpu_usage . 1 as f64 ) ;
223262 self . igpu_freq . push ( data. gpu_usage . 0 as u64 , data. gpu_usage . 1 as f64 ) ;
224263
225- self . temp . cpu_temp_avg = avg2 ( self . temp . cpu_temp_avg , data. temp . cpu_temp_avg ) ;
226- self . temp . gpu_temp_avg = avg2 ( self . temp . gpu_temp_avg , data. temp . gpu_temp_avg ) ;
264+ self . cpu_temp . push ( data. temp . cpu_temp_avg ) ;
265+ self . gpu_temp . push ( data. temp . gpu_temp_avg ) ;
227266
228267 self . mem . push ( data. memory ) ;
229268 }
@@ -388,8 +427,8 @@ impl App {
388427 . constraints ( [ Constraint :: Fill ( 1 ) , Constraint :: Fill ( 1 ) , Constraint :: Fill ( 1 ) ] . as_ref ( ) )
389428 . split ( iarea) ;
390429
391- f. render_widget ( self . get_power_block ( "CPU" , & self . cpu_power , self . temp . cpu_temp_avg ) , ha[ 0 ] ) ;
392- f. render_widget ( self . get_power_block ( "GPU" , & self . gpu_power , self . temp . gpu_temp_avg ) , ha[ 1 ] ) ;
430+ f. render_widget ( self . get_power_block ( "CPU" , & self . cpu_power , self . cpu_temp . last ( ) ) , ha[ 0 ] ) ;
431+ f. render_widget ( self . get_power_block ( "GPU" , & self . gpu_power , self . gpu_temp . last ( ) ) , ha[ 1 ] ) ;
393432 f. render_widget ( self . get_power_block ( "ANE" , & self . ane_power , 0.0 ) , ha[ 2 ] ) ;
394433 }
395434
0 commit comments