diff --git a/frappy_psi/lakeshore.py b/frappy_psi/lakeshore.py index 0719713a..bb19e74d 100644 --- a/frappy_psi/lakeshore.py +++ b/frappy_psi/lakeshore.py @@ -129,13 +129,11 @@ class Device(HasLscIO, Module): log_formats = True, False # tuple (, ) 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 of None (used as ordered set) - self._curve_map = {} # dict CurveHeader.key of curve_no + self._to_delete = [] # curves to delete self._requests = {} # dict of CurveRequest self._sensors = {} # dict 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