frappy_psi.lakeshore: improved calcurve handling

instead of keeping track a lookup for curve keys and
of empty curves, loop over curve_cache. this is easier code
and not a real performace problem
This commit is contained in:
2026-06-09 14:21:21 +02:00
parent bdfa1d1a38
commit cf6592da53
+52 -39
View File
@@ -129,13 +129,11 @@ class Device(HasLscIO, Module):
log_formats = True, False # tuple (<log Ohm allowed>, <log K allowed>)
max_curve_length = 200
cmds_per_line = 5 # number of commands / replies allowed on one line
_empty_curves = None
_request_lock = None
FAST_POLL = 0.01
def initModule(self):
self._empty_curves = {} # dict <curve no> of None (used as ordered set)
self._curve_map = {} # dict CurveHeader.key of curve_no
self._to_delete = [] # curves to delete
self._requests = {} # dict <calcurve> of CurveRequest
self._sensors = {} # dict <channel> of sensors
self._disable_channels = set(self.channels)
@@ -188,12 +186,6 @@ class Device(HasLscIO, Module):
for curve_no in range(*self.user_curves):
crvhdr = CurveHeader(*headers[curve_no].split(','))
self.curve_cache[curve_no] = crvhdr
if crvhdr.key:
if crvhdr in self._curve_map:
# this is a duplicate, add to empty curves
self._empty_curves[curve_no] = None
else:
self._curve_map[crvhdr.key] = curve_no
try:
check_item = None
with self._request_lock:
@@ -219,6 +211,10 @@ class Device(HasLscIO, Module):
sensor = self._sensors.get(ch)
if sensor is None or sensor.disabled:
self.disable_channel(ch)
if self._to_delete:
no = self._to_delete.pop(0)
if self.curve_cache[no].key is None:
self.command(f'CRVDEL {no}')
except Exception as e:
request.loading = False
request.status = ERROR, repr(e)
@@ -237,25 +233,23 @@ class Device(HasLscIO, Module):
def get_empty(self):
"""get curve no of a curve to be reused"""
if self._empty_curves:
# we have unused curve slots
curve_no = next(iter(self._empty_curves))
self._empty_curves.pop(curve_no)
for curve_no in range(*self.user_curves):
if not self.curve_cache[curve_no].key:
return curve_no
used_no = set(s.curve_no for s in self._sensors.values())
for req in self._requests.values():
used_no.add(req.invalidate)
n0, n = self.user_curves
n -= n0
# instead of taking the lowest available number, start at a
# random index, else most recent curves would be overridden
offset = random.randrange(n)
for i in range(n):
curve_no = n0 + (i + offset) % n
if curve_no not in used_no:
break
else:
used_no = set(s.curve_no for s in self._sensors.values())
for req in self._requests.values():
used_no.add(req.invalidate)
n0, n = self.user_curves
n -= n0
# avoid to take the lower numbers first
# as then the most recent curves would be overridden
offset = random.randrange(n)
for i in range(n):
curve_no = n0 + (i + offset) % n
if curve_no not in used_no:
break
else:
raise ValueError('no empty curves available')
raise ValueError('no empty curves available')
return curve_no
def get_calib_state(self, sensor):
@@ -269,7 +263,7 @@ class Device(HasLscIO, Module):
return ImpossibleError('error while loading calibration curve')
return None
def verify_ends(self, request):
def verify_ends(self, request, no):
"""preliminary check: check if the ends of the curve are matching the stored ones"""
npnt = len(request.points)
numbers = [1, npnt]
@@ -277,8 +271,12 @@ class Device(HasLscIO, Module):
if npnt < self.max_curve_length:
numbers.append(npnt + 1)
points.append((0, 0))
for pairs in zip(points, self.get_crvpts(request.curve_no, *numbers)):
stored_points = self.get_crvpts(no, *numbers)
for pairs in zip(points, stored_points):
if not self.is_equal(*pairs):
self.log.info('not equal %r', pairs)
self.log.info('requested points %r', points)
self.log.info('stored points %r', stored_points)
return False
return True
@@ -288,11 +286,25 @@ class Device(HasLscIO, Module):
:param request: the curve request
:return: next action
"""
no = self._curve_map.get(request.crvhdr.key)
if no:
key = request.crvhdr.key
duplicates = []
for no, crvhdr in self.curve_cache.items():
if key == crvhdr.key:
if self.verify_ends(request, no):
ends_ok = True
for delno in duplicates:
self.curve_cache[delno] = CurveHeader()
self._to_delete.append(delno)
break
else:
duplicates.append(no)
else:
no = None
ends_ok = False
if no is not None:
request.crvhdr = self.curve_cache[no]
request.set_curve_no(no)
if self.verify_ends(request):
if ends_ok:
self.log.info('calcurve #%d %s found, start to check consistency', no, request.crvhdr)
# guess the curve is o.k. -> install
request.install_sensors(request.sensors.values())
@@ -305,8 +317,8 @@ class Device(HasLscIO, Module):
request.invalidate = no
request.set_curve_no(self.get_empty())
request.install_sensors(request.sensors.values())
self.log.info('%s found, but content has changed, create #%d',
request.crvhdr.sn, request.curve_no)
self.log.info('%s found as #%d, but content has changed, create #%d',
request.crvhdr.sn, no, request.curve_no)
return self.start_load
request.set_curve_no(self.get_empty())
for sensor in request.sensors.values():
@@ -390,7 +402,6 @@ class Device(HasLscIO, Module):
:return: next action
"""
self.curve_cache[request.curve_no] = CurveHeader(*request.crvhdr)
self._curve_map[request.crvhdr.key] = request.curve_no
self.put_header(request.curve_no, *request.crvhdr)
request.install_sensors(request.sensors.values())
no = request.invalidate
@@ -399,7 +410,7 @@ class Device(HasLscIO, Module):
self.log.info('calcurve #%d %s %s, clear previous #%d',
request.curve_no, request.crvhdr, comment, no)
self.curve_cache[no] = CurveHeader()
self._empty_curves[no] = None
self._to_delete.append(no)
else:
self.log.info('calcurve #%d %s %s',
request.curve_no, request.crvhdr, comment)
@@ -440,7 +451,7 @@ class Device(HasLscIO, Module):
"""get all headers
for performance reasons, by default 5 headers are read in one line
this is speeing up quite a lot on serial connections
this speeds up quite a lot on serial connections
"""
n0, n1 = self.user_curves
result = {}
@@ -465,7 +476,7 @@ class CurveRequest:
self.sensors = {sensor.channel: sensor}
calcurve = CalCurve(sensor.calcurve)
equipment_id = device.propertyValues.get('original_id') or device.secNode.equipment_id
name = f"{equipment_id.split('.')[0]}.{sensor.name}"
self.name = f"{equipment_id.split('.')[0]}.{sensor.name}"
sn = calcurve.calibname
limit = calcurve.calibrange[1]
unit = calcurve.options.get('unit', 'Ohm')
@@ -495,9 +506,11 @@ class CurveRequest:
else:
fmt = 2 if unit == 'V' else 1
coef = 1 + (self.points[0][1] < self.points[-1][1]) # 1: ntc, 2: ptc
self.crvhdr = CurveHeader(name, sn, fmt, limit, coef)
self.crvhdr = CurveHeader(self.name, sn, fmt, limit, coef)
def set_curve_no(self, curve_no):
if self.name != self.crvhdr.name:
self.crvhdr = CurveHeader(self.name, *self.crvhdr[1:])
self.curve_no = curve_no
for s in self.sensors.values():
s.curve_no = curve_no