Skip to content

Commit 628a4cb

Browse files
committed
Always use IDENTICAL timestamps so the power and slog reports can match
1 parent d0db5ca commit 628a4cb

1 file changed

Lines changed: 35 additions & 13 deletions

File tree

meshtastic/slog/slog.py

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)