-
Notifications
You must be signed in to change notification settings - Fork 5
Fix SSM module typos, enum mismatches, and stream robustness #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
2780927
cdba7b1
509ce8a
5659038
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,8 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Implements functionality unique to the Lake Shore M81.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from datetime import datetime | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import logging | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import struct | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import time | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from base64 import b64decode | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from threading import Lock | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from warnings import warn | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -12,12 +14,25 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from lakeshore.ssm_settings_profiles import SettingsProfiles | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from lakeshore.requires_firmware_version import requires_firmware_version | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger = logging.getLogger(__name__) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from wakepy import keep | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except NotImplementedError: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pass # Proceed without wakepy on linux without systemd | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except KeyError: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pass # Proceed without wakepy on linux without dbus | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except (NotImplementedError, KeyError, ImportError): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Provide a no-op context manager fallback when wakepy is unavailable | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from contextlib import contextmanager | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class _NoOpMode: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Mimics wakepy's Mode object returned by keep.running().""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| success = False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class _NoOpKeep: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @staticmethod | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @contextmanager | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def running(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| yield _NoOpMode() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| keep = _NoOpKeep() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class SSMSystemOperationRegister(RegisterBase): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -128,6 +143,8 @@ def __init__(self, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.settings_profiles = SettingsProfiles(self) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Lock ordering: stream_lock must be acquired before dut_lock (inherited from | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # GenericInstrument) to avoid deadlock. Never acquire dut_lock then stream_lock. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.stream_lock = Lock() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Sweeping limits | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -218,6 +235,7 @@ def _locate_module_by_name(module_name, set_of_modules): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'SRANge': float, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'SVLimit': lambda s: bool(int(s)), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'SILimit': lambda s: bool(int(s)), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'SSWeeping': lambda s: bool(int(s)), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'MDC': float, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'MRMs': float, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'MPPeak': float, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -270,6 +288,13 @@ def get_multiple_min_max_values(self, *data_sources): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def stream_data(self, rate, num_points, *data_sources): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Generator object to stream data from the instrument. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| A TRACe:STOp command is sent when the generator exits for any reason | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (normal completion, caller break, exception, or garbage collection) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| to ensure instrument-side cleanup. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Note: the overflow check only runs on normal completion. If the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| generator is abandoned early, overflow status is not checked. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Args: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rate (int): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Desired transfer rate in points/sec. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -292,26 +317,45 @@ def stream_data(self, rate, num_points, *data_sources): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bytes_per_row = int(self.query('TRACe:FORMat:ENCOding:B64:BCOunt?')) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| binary_format = '<' + self.query('TRACe:FORMat:ENCOding:B64:BFORmat?').strip('\"') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if num_points is not None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.command(f'TRACe:STARt {num_points}') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.command('TRACe:STARt') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| num_collected = 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while num_points is None or num_collected < num_points: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| b64_string = '' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while not b64_string: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| b64_string = self.query('TRACe:DATA:ALL?', check_errors=False) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| new_bytes = b64decode(b64_string) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rows = [new_bytes[i:i + bytes_per_row] for i in range(0, len(new_bytes), bytes_per_row)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for row in rows: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data = struct.unpack(binary_format, row) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| num_collected += 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| yield data | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if num_points is not None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.command(f'TRACe:STARt {num_points}') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.command('TRACe:STARt') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| num_collected = 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while num_points is None or num_collected < num_points: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| b64_string = '' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while not b64_string: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| b64_string = self.query('TRACe:DATA:ALL?', check_errors=False) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not b64_string: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| time.sleep(0.01) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| new_bytes = b64decode(b64_string) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rows = [new_bytes[i:i + bytes_per_row] for i in range(0, len(new_bytes), bytes_per_row)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for row in rows: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if len(row) < bytes_per_row: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Incomplete trailing row from non-evenly-divisible buffer | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger.debug('Skipping incomplete row (%d/%d bytes)', len(row), bytes_per_row) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+334
to
+341
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if num_points is not None and num_collected >= num_points: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Batch contained more rows than needed to reach num_points | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+327
to
+344
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while num_points is None or num_collected < num_points: | |
| b64_string = '' | |
| while not b64_string: | |
| b64_string = self.query('TRACe:DATA:ALL?', check_errors=False) | |
| if not b64_string: | |
| time.sleep(0.01) | |
| new_bytes = b64decode(b64_string) | |
| rows = [new_bytes[i:i + bytes_per_row] for i in range(0, len(new_bytes), bytes_per_row)] | |
| for row in rows: | |
| if len(row) < bytes_per_row: | |
| # Incomplete trailing row from non-evenly-divisible buffer | |
| logger.debug('Skipping incomplete row (%d/%d bytes)', len(row), bytes_per_row) | |
| break | |
| if num_points is not None and num_collected >= num_points: | |
| # Batch contained more rows than needed to reach num_points | |
| break | |
| pending_bytes = b'' | |
| done = False | |
| while not done and (num_points is None or num_collected < num_points): | |
| b64_string = '' | |
| while not b64_string: | |
| b64_string = self.query('TRACe:DATA:ALL?', check_errors=False) | |
| if not b64_string: | |
| time.sleep(0.01) | |
| # Combine any leftover bytes from the previous poll with the newly decoded data. | |
| new_bytes = pending_bytes + b64decode(b64_string) | |
| total_len = len(new_bytes) | |
| if total_len < bytes_per_row: | |
| # Not enough data to form a full row yet; keep buffering. | |
| pending_bytes = new_bytes | |
| continue | |
| full_rows_len = (total_len // bytes_per_row) * bytes_per_row | |
| full_rows_bytes = new_bytes[:full_rows_len] | |
| pending_bytes = new_bytes[full_rows_len:] | |
| for i in range(0, full_rows_len, bytes_per_row): | |
| if num_points is not None and num_collected >= num_points: | |
| # Batch contained more rows than needed to reach num_points | |
| done = True | |
| break | |
| row = full_rows_bytes[i:i + bytes_per_row] |
Copilot
AI
Feb 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new error message doesn’t include the raw response payload, which is often the key detail when diagnosing instrument/firmware parsing issues. Consider including the original response (or the pre-split string) in the exception message, e.g. ... got {len(response)}: {response!r} (and similarly for the self-calibration date).
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -30,6 +30,7 @@ class DataSourceMnemonic(str, Enum): | |||
| MEASURE_SETTLING = 'MSETtling' | ||||
| MEASURE_UNLOCK = 'MUNLock' | ||||
| MEASURE_REFERENCE_FREQUENCY = 'MRFRequency' | ||||
| SOURCE_IS_SETTLING = 'SRSettling' | ||||
|
||||
| SOURCE_IS_SETTLING = 'SRSettling' |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -126,6 +126,77 @@ def test_stream_data(self): | |
| self.assertIn('TRACe:FORMat:ENCOding:B64:BFORmat?', self.fake_connection.get_outgoing_message()) | ||
| self.assertIn('TRACe:STARt 3', self.fake_connection.get_outgoing_message()) | ||
|
|
||
| def test_stream_data_sends_stop_on_completion(self): | ||
| """Test that TRACe:STOp is sent when stream_data completes normally""" | ||
|
|
||
| list_data = [(False, 45.6521), (True, 1.258), (False, 65.8974)] | ||
|
|
||
| my_data = [] | ||
| for data in list_data: | ||
| for value in data: | ||
| my_data.append(value) | ||
|
|
||
| list_format = '<?d?d?d' | ||
| pack_data = pack(list_format, *my_data) | ||
| encoded_data = str(b64encode(pack_data))[2:-1] | ||
|
Comment on lines
+132
to
+141
|
||
|
|
||
| self.fake_connection.setup_response('No error') # trace reset | ||
| self.fake_connection.setup_response('No error') # configure elements | ||
| self.fake_connection.setup_response('No error') # format encoding | ||
| self.fake_connection.setup_response('No error') # rate | ||
| self.fake_connection.setup_response('9;No error') # bytes per row | ||
| self.fake_connection.setup_response('"?d";No error') # binary format | ||
| self.fake_connection.setup_response('No error') # trace start | ||
| self.fake_connection.setup_response(encoded_data) # trace data | ||
| self.fake_connection.setup_response('0;No error') # overflow | ||
|
|
||
| # Consume all data | ||
| list(self.dut.stream_data(10, 3, ("MX", 1), ("MY", 2), ("MR", 3))) | ||
|
|
||
| # Drain all outgoing messages and check TRACe:STOp was sent | ||
| outgoing_messages = [] | ||
| while self.fake_connection.outgoing: | ||
| outgoing_messages.append(self.fake_connection.get_outgoing_message()) | ||
| self.assertTrue( | ||
| any('TRACe:STOp' in msg for msg in outgoing_messages), | ||
| f'TRACe:STOp not found in outgoing messages: {outgoing_messages}') | ||
|
|
||
| def test_stream_data_sends_stop_on_early_break(self): | ||
| """Test that TRACe:STOp is sent when caller breaks out of stream_data early""" | ||
|
|
||
| list_data = [(False, 45.6521), (True, 1.258), (False, 65.8974)] | ||
|
|
||
| my_data = [] | ||
| for data in list_data: | ||
| for value in data: | ||
| my_data.append(value) | ||
|
|
||
| list_format = '<?d?d?d' | ||
| pack_data = pack(list_format, *my_data) | ||
| encoded_data = str(b64encode(pack_data))[2:-1] | ||
|
|
||
| self.fake_connection.setup_response('No error') # trace reset | ||
| self.fake_connection.setup_response('No error') # configure elements | ||
| self.fake_connection.setup_response('No error') # format encoding | ||
| self.fake_connection.setup_response('No error') # rate | ||
| self.fake_connection.setup_response('9;No error') # bytes per row | ||
| self.fake_connection.setup_response('"?d";No error') # binary format | ||
| self.fake_connection.setup_response('No error') # trace start | ||
| self.fake_connection.setup_response(encoded_data) # trace data | ||
|
|
||
| # Only consume 1 of 3 points then abandon the generator | ||
| gen = self.dut.stream_data(10, 3, ("MX", 1), ("MY", 2), ("MR", 3)) | ||
| next(gen) | ||
| gen.close() | ||
|
|
||
| # Drain all outgoing messages and check TRACe:STOp was sent | ||
| outgoing_messages = [] | ||
| while self.fake_connection.outgoing: | ||
| outgoing_messages.append(self.fake_connection.get_outgoing_message()) | ||
| self.assertTrue( | ||
| any('TRACe:STOp' in msg for msg in outgoing_messages), | ||
| f'TRACe:STOp not found in outgoing messages: {outgoing_messages}') | ||
|
|
||
| def test_get_data(self): | ||
| """Test get data""" | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docstring claim about cleanup on 'garbage collection' is stronger than Python generally guarantees (timing of generator finalization can be implementation-dependent). Consider softening this to 'when the generator is closed/finalized (e.g., via exhaustion,
close(), or GC finalization) we attempt best-effort cleanup' to avoid implying a deterministic guarantee.