Skip to content

Commit b464e90

Browse files
committed
make ppk2 power meter threadsafe
1 parent 3c76e19 commit b464e90

1 file changed

Lines changed: 29 additions & 19 deletions

File tree

meshtastic/powermon/ppk2.py

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,10 @@ def __init__(self, portName: Optional[str] = None):
4646
# Normally we just sleep with a timeout on this condition (polling the power measurement data repeatedly)
4747
# but any time our measurements have been fully consumed (via reset_measurements) we notify() this condition
4848
# to trigger a new reading ASAP.
49-
self.want_measurement = threading.Condition()
49+
self._want_measurement = threading.Condition()
50+
51+
# To guard against a brief window while updating measured values
52+
self._result_lock = threading.Condition()
5053

5154
self.r = r = ppk2_api.PPK2_API(
5255
portName
@@ -62,8 +65,10 @@ def __init__(self, portName: Optional[str] = None):
6265
def measurement_loop(self):
6366
"""Endless measurement loop will run in a thread."""
6467
while self.measuring:
65-
with self.want_measurement:
66-
self.want_measurement.wait(0.0001 if self.num_data_reads == 0 else 0.001)
68+
with self._want_measurement:
69+
self._want_measurement.wait(
70+
0.0001 if self.num_data_reads == 0 else 0.001
71+
)
6772
# normally we poll using this timeout, but sometimes
6873
# reset_measurement() will notify us to read immediately
6974

@@ -75,17 +80,20 @@ def measurement_loop(self):
7580

7681
# update invariants
7782
if len(samples) > 0:
78-
if (
79-
self.current_num_samples == 0
80-
): # First set of new reads, reset min/max
83+
if self.current_num_samples == 0:
84+
# First set of new reads, reset min/max
8185
self.current_max = 0
82-
self.current_min = samples[
83-
0
84-
] # we need at least one sample to get an initial min
86+
self.current_min = samples[0]
87+
# we need at least one sample to get an initial min
88+
89+
# The following operations could be expensive, so do outside of the lock
90+
# FIXME - change all these lists into numpy arrays to use lots less CPU
8591
self.current_max = max(self.current_max, max(samples))
8692
self.current_min = min(self.current_min, min(samples))
87-
self.current_sum += sum(samples)
88-
self.current_num_samples += len(samples)
93+
latest_sum = sum(samples)
94+
with self._result_lock:
95+
self.current_sum += latest_sum
96+
self.current_num_samples += len(samples)
8997
# logging.debug(f"PPK2 data_len={len(read_data)}, sample_len={len(samples)}")
9098

9199
self.num_data_reads += 1
@@ -102,13 +110,14 @@ def get_max_current_mA(self):
102110

103111
def get_average_current_mA(self):
104112
"""Return the average current in mA."""
105-
if self.current_num_samples != 0:
106-
# If we have new samples, calculate a new average
107-
self.current_average = self.current_sum / self.current_num_samples
113+
with self._result_lock:
114+
if self.current_num_samples != 0:
115+
# If we have new samples, calculate a new average
116+
self.current_average = self.current_sum / self.current_num_samples
108117

109-
# Even if we don't have new samples, return the last calculated average
110-
# measurements are in microamperes, divide by 1000
111-
return self.current_average / 1000
118+
# Even if we don't have new samples, return the last calculated average
119+
# measurements are in microamperes, divide by 1000
120+
return self.current_average / 1000
112121

113122
def reset_measurements(self):
114123
"""Reset current measurements."""
@@ -122,8 +131,9 @@ def reset_measurements(self):
122131
self.num_data_reads = 0
123132
self.total_data_len = 0
124133
self.max_data_len = 0
125-
with self.want_measurement:
126-
self.want_measurement.notify() # notify the measurement loop to read immediately
134+
135+
with self._want_measurement:
136+
self._want_measurement.notify() # notify the measurement loop to read immediately
127137

128138
def close(self) -> None:
129139
"""Close the power meter."""

0 commit comments

Comments
 (0)