@@ -82,17 +82,23 @@ def __init__(self, pMeter: PowerMeter, file_path: str, interval=0.2) -> None:
8282 )
8383 self .thread .start ()
8484
85+ def store_current_reading (self , now : datetime | None = None ) -> None :
86+ """Store current power measurement."""
87+ if now is None :
88+ now = datetime .now ()
89+ d = {
90+ "time" : now ,
91+ "average_mW" : self .pMeter .get_average_current_mA (),
92+ "max_mW" : self .pMeter .get_max_current_mA (),
93+ "min_mW" : self .pMeter .get_min_current_mA (),
94+ }
95+ self .pMeter .reset_measurements ()
96+ self .writer .add_row (d )
97+
8598 def _logging_thread (self ) -> None :
8699 """Background thread for logging the current watts reading."""
87100 while self .is_logging :
88- d = {
89- "time" : datetime .now (),
90- "average_mW" : self .pMeter .get_average_current_mA (),
91- "max_mW" : self .pMeter .get_max_current_mA (),
92- "min_mW" : self .pMeter .get_min_current_mA (),
93- }
94- self .pMeter .reset_measurements ()
95- self .writer .add_row (d )
101+ self .store_current_reading ()
96102 time .sleep (self .interval )
97103
98104 def close (self ) -> None :
@@ -112,12 +118,19 @@ class StructuredLogger:
112118 """Sniffs device logs for structured log messages, extracts those into apache arrow format.
113119 Also writes the raw log messages to raw.txt"""
114120
115- def __init__ (self , client : MeshInterface , dir_path : str , include_raw = True ) -> None :
121+ def __init__ (
122+ self ,
123+ client : MeshInterface ,
124+ dir_path : str ,
125+ power_logger : PowerLogger | None = None ,
126+ include_raw = True ,
127+ ) -> None :
116128 """Initialize the StructuredLogger object.
117129
118130 client (MeshInterface): The MeshInterface object to monitor.
119131 """
120132 self .client = client
133+ self .power_logger = power_logger
121134
122135 # Setup the arrow writer (and its schema)
123136 self .writer = FeatherWriter (f"{ dir_path } /slog" )
@@ -129,6 +142,9 @@ def __init__(self, client: MeshInterface, dir_path: str, include_raw=True) -> No
129142 if self .include_raw :
130143 all_fields .append (("raw" , pa .string ()))
131144
145+ # Use timestamp as the first column
146+ all_fields .insert (0 , ("time" , pa .timestamp ("us" )))
147+
132148 # pass in our name->type tuples a pa.fields
133149 self .writer .set_schema (
134150 pa .schema (map (lambda x : pa .field (x [0 ], x [1 ]), all_fields ))
@@ -198,11 +214,16 @@ def _onLogMessage(self, line: str) -> None:
198214
199215 # Store our structured log record
200216 if di or self .include_raw :
201- di ["time" ] = datetime .now ()
217+ now = datetime .now ()
218+ di ["time" ] = now
202219 if self .include_raw :
203220 di ["raw" ] = line
204221 self .writer .add_row (di )
205222
223+ # If we have a sibling power logger, make sure we have a power measurement with the EXACT same timestamp
224+ if self .power_logger :
225+ self .power_logger .store_current_reading (now )
226+
206227 if self .raw_file :
207228 self .raw_file .write (line + "\n " ) # Write the raw log
208229
@@ -239,15 +260,16 @@ def __init__(
239260
240261 logging .info (f"Writing slogs to { dir_name } " )
241262
242- self .slog_logger : Optional [StructuredLogger ] = StructuredLogger (
243- client , self .dir_name
244- )
245263 self .power_logger : Optional [PowerLogger ] = (
246264 None
247265 if not power_meter
248266 else PowerLogger (power_meter , f"{ self .dir_name } /power" )
249267 )
250268
269+ self .slog_logger : Optional [StructuredLogger ] = StructuredLogger (
270+ client , self .dir_name , power_logger = self .power_logger
271+ )
272+
251273 # Store a lambda so we can find it again to unregister
252274 self .atexit_handler = lambda : self .close () # pylint: disable=unnecessary-lambda
253275
0 commit comments