diff --git a/deapi/client.py b/deapi/client.py index 265d040..4b93ac6 100644 --- a/deapi/client.py +++ b/deapi/client.py @@ -208,8 +208,8 @@ def connect(self, host: str = "127.0.0.1", port: int = 13240, read_only=False): version = [int(part) for part in server_version[:4]] temp = version[2] + version[1] * 1000 + version[0] * 1000000 - if temp >= 2008000 and version[3] >= 11901: - ## version 2.8.0 build 11901+ — virtual image buffer support (SDK 5.3.0) + if temp >= 2008000 and version[3] >= 12073: + ## version 2.8.0 build 12073+ — virtual image buffer support (SDK 5.3.0) self.commandVersion = 16 elif (temp >= 2007005 and version[3] < 11274) or temp >= 2008000: ## version after 2.8.0 (older builds) @@ -1717,6 +1717,8 @@ def get_result( "autoStretchGamma", ] ) + if self.commandVersion >= 16: + attributes_order.append("current_scan_pattern_idx") # special casting rules field_casts = { @@ -2063,6 +2065,8 @@ def get_virtual_image_buffer( - Other values indicate timeout, failure, or finished state. frame_index : int The acquisition frame index associated with this virtual image. + pattern_index : int + The scan pattern index associated with this virtual image. image : numpy.ndarray or None 2-D array of shape ``(height, width)`` on success, ``None`` otherwise. """ @@ -2076,14 +2080,16 @@ def get_virtual_image_buffer( status = MovieBufferStatus.UNKNOWN frame_index = 0 + pattern_index = 0 image = None if response: values = self.__getParameters(response.acknowledge[0]) - if isinstance(values, list) and len(values) >= 3: + if isinstance(values, list) and len(values) >= 4: status_int = values[0] total_bytes = values[1] frame_index = values[2] + pattern_index = values[3] try: status = MovieBufferStatus(status_int) except ValueError: @@ -2100,7 +2106,7 @@ def get_virtual_image_buffer( else: status = MovieBufferStatus.FAILED - return status, frame_index, image + return status, frame_index, pattern_index, image def save_image(self, image, fileName, textSize=0): t0 = self.GetTime() diff --git a/deapi/data_types.py b/deapi/data_types.py index bd363d2..46cdf2f 100644 --- a/deapi/data_types.py +++ b/deapi/data_types.py @@ -281,6 +281,8 @@ class Attributes: output_binning_method : int, optional Method used for binning the output image. Defaults to BinningMethod.AVERAGE, other options are BinningMethod.NONE, BinningMethod.SUM, and BinningMethod.FOURIERCROP. + current_scan_pattern_idx : int, optional + Index of the current scan pattern. """ def __init__( @@ -332,6 +334,7 @@ def __init__( output_binning_x: int = 1, output_binning_y: int = 1, output_binning_method: int = 1, # BinningMethod.AVERAGE + current_scan_pattern_idx: int = 0, ): self.centerX = center_x @@ -381,6 +384,7 @@ def __init__( self.output_binning_x = output_binning_x self.output_binning_y = output_binning_y self.output_binning_method = output_binning_method + self.current_scan_pattern_idx = current_scan_pattern_idx def __repr__(self): return ( diff --git a/deapi/simulated_server/fake_server.py b/deapi/simulated_server/fake_server.py index 829141e..693e75f 100644 --- a/deapi/simulated_server/fake_server.py +++ b/deapi/simulated_server/fake_server.py @@ -757,9 +757,10 @@ def _fake_get_result(self, command): float(np.min(image)), # autoStretchMin 26 float(np.max(image)), # autoStretchMax 27 float(1.0), # autoStretchGamma 28 - float(np.min(image)), # histogram min 29 - float(np.max(image)), # histogram max 30 - float(np.max(image)), # histogram upper local max 31 + int(0), # current_scan_pattern_idx 29 + float(np.min(image)), # histogram min 30 + float(np.max(image)), # histogram max 31 + float(np.max(image)), # histogram upper local max 32 ] # Then histogram... diff --git a/deapi/tests/test_scanning/test_virtual_image_buffers.py b/deapi/tests/test_scanning/test_virtual_image_buffers.py index 2e650ce..55b03dc 100644 --- a/deapi/tests/test_scanning/test_virtual_image_buffers.py +++ b/deapi/tests/test_scanning/test_virtual_image_buffers.py @@ -83,45 +83,45 @@ def test_streaming_all_virtual_buffers(self, client): queue_virtual_buffers=True, ) - received_frames: list[tuple[int, int, np.ndarray]] = [] + received_frames: list[tuple[int, int, int, np.ndarray]] = [] # UNKNOWN = 0, FAILED = 1, TIMEOUT = 3, FINISHED = 4, OK = 5 # OK --> Still more frames to access - finshed = False + finished = False - while not finshed: + while not finished: for buf_id in range(NUM_VIRTUAL_BUFFERS): - status, frame_index, image = client.get_virtual_image_buffer( + status, frame_index, pattern_index, image = client.get_virtual_image_buffer( buf_id, virtual_image_info=info ) print( - f"buf_id={buf_id} frame={frame_index} status={status} image shape: {image.shape if image is not None else None}" + f"buf_id={buf_id} frame={frame_index} pattern={pattern_index} status={status} image shape: {image.shape if image is not None else None}" ) if status == MovieBufferStatus.OK: assert ( image is not None - ), f"buf_id={buf_id} frame={frame_index}: status OK but image is None" + ), f"buf_id={buf_id} frame={frame_index} pattern={pattern_index}: status OK but image is None" assert image.shape == ( info.height, info.width, - ), f"buf_id={buf_id} frame={frame_index}: unexpected shape {image.shape}" - received_frames.append((buf_id, frame_index, image)) + ), f"buf_id={buf_id} frame={frame_index} pattern={pattern_index}: unexpected shape {image.shape}" + received_frames.append((buf_id, frame_index, pattern_index, image)) elif status == MovieBufferStatus.FINISHED: - finshed = True + finished = True # This channel is done — mark finished. elif status == MovieBufferStatus.TIMEOUT: print( - f"buf_id={buf_id} frame={frame_index}: timeout waiting for frame" + f"buf_id={buf_id} frame={frame_index} pattern={pattern_index}: timeout waiting for frame" ) # This can happen if we check a channel before its first frame is ready. # Just ignore and check again in the next loop iteration. elif status == MovieBufferStatus.FAILED: print( - f"buf_id={buf_id} frame={frame_index}: failed to retrieve frame-- Likely this" + f"buf_id={buf_id} frame={frame_index} pattern={pattern_index}: failed to retrieve frame-- Likely this" f"virtual image is not initialized." ) @@ -156,19 +156,19 @@ def test_streaming_all_virtual_buffers_one_off(self, client): queue_virtual_buffers=True, ) - received_frames: list[tuple[int, int, np.ndarray]] = [] + received_frames: list[tuple[int, int, int, np.ndarray]] = [] # UNKNOWN = 0, FAILED = 1, TIMEOUT = 3, FINISHED = 4, OK = 5 # OK --> Still more frames to access - finshed = False + finished = False - while not finshed: + while not finished: for buf_id in range(NUM_VIRTUAL_BUFFERS): - status, frame_index, image = client.get_virtual_image_buffer( + status, frame_index, pattern_index, image = client.get_virtual_image_buffer( buf_id, virtual_image_info=info ) print( - f"buf_id={buf_id} frame={frame_index} status={status} image shape: {image.shape if image is not None else None}" + f"buf_id={buf_id} frame={frame_index} pattern={pattern_index} status={status} image shape: {image.shape if image is not None else None}" ) if buf_id == 4: @@ -176,27 +176,27 @@ def test_streaming_all_virtual_buffers_one_off(self, client): if status == MovieBufferStatus.OK: assert ( image is not None - ), f"buf_id={buf_id} frame={frame_index}: status OK but image is None" + ), f"buf_id={buf_id} frame={frame_index} pattern={pattern_index}: status OK but image is None" assert image.shape == ( info.height, info.width, - ), f"buf_id={buf_id} frame={frame_index}: unexpected shape {image.shape}" - received_frames.append((buf_id, frame_index, image)) + ), f"buf_id={buf_id} frame={frame_index} pattern={pattern_index}: unexpected shape {image.shape}" + received_frames.append((buf_id, frame_index, pattern_index, image)) elif status == MovieBufferStatus.FINISHED: - finshed = True + finished = True # This channel is done — mark finished. elif status == MovieBufferStatus.TIMEOUT: print( - f"buf_id={buf_id} frame={frame_index}: timeout waiting for frame" + f"buf_id={buf_id} frame={frame_index} pattern={pattern_index}: timeout waiting for frame" ) # This can happen if we check a channel before its first frame is ready. # Just ignore and check again in the next loop iteration. elif status == MovieBufferStatus.FAILED: print( - f"buf_id={buf_id} frame={frame_index}: failed to retrieve frame-- Likely this" + f"buf_id={buf_id} frame={frame_index} pattern={pattern_index}: failed to retrieve frame-- Likely this" f"virtual image is not initialized." ) @@ -242,7 +242,7 @@ def test_streaming_multiple_xy_arrays(self, client): queue_virtual_buffers=True, ) - received_frames: list[tuple[int, int, np.ndarray]] = [] + received_frames: list[tuple[int, int, int, np.ndarray]] = [] # UNKNOWN = 0, FAILED = 1, TIMEOUT = 3, FINISHED = 4, OK = 5 # OK --> Still more frames to access @@ -253,12 +253,12 @@ def test_streaming_multiple_xy_arrays(self, client): for buf_id in range(NUM_VIRTUAL_BUFFERS): got_frame = False while not got_frame: - status, frame_index, image = client.get_virtual_image_buffer( + status, frame_index, pattern_index, image = client.get_virtual_image_buffer( buf_id, virtual_image_info=info, timeout_msec=1000 # 1 sec ) if status == MovieBufferStatus.OK: received_frames.append( - (buf_id, frame_index, image) + (buf_id, frame_index, pattern_index, image) ) # Do whatever with the frame. got_frame = True @@ -271,7 +271,7 @@ def test_streaming_multiple_xy_arrays(self, client): got_frame = False elif status == MovieBufferStatus.FAILED: print( - f"buf_id={buf_id} frame={frame_index}: failed to retrieve frame-- Likely this" + f"buf_id={buf_id} frame={frame_index} pattern={pattern_index}: failed to retrieve frame-- Likely this" f" virtual image is not initialized." ) got_frame = True diff --git a/deapi/version.py b/deapi/version.py index f1f82a0..82aad06 100644 --- a/deapi/version.py +++ b/deapi/version.py @@ -6,7 +6,7 @@ # Used by FakeServer so its reported "Server Software Version" always matches # the commandVersion used for dispatch, making tests version-agnostic. _command_version_to_server_version = { - 16: "2.8.0.11901", + 16: "2.8.0.12073", 15: "2.7.5.1000", 13: "2.7.4.10590", 12: "2.7.4.1000", @@ -18,5 +18,5 @@ # The server version string that corresponds to the current commandVersion. fake_server_software_version = _command_version_to_server_version.get( - commandVersion, "2.8.0.11901" + commandVersion, "2.8.0.12073" ) diff --git a/doc/reference/scan_design.rst b/doc/reference/scan_design.rst index b868f55..bdb1797 100644 --- a/doc/reference/scan_design.rst +++ b/doc/reference/scan_design.rst @@ -473,8 +473,8 @@ frame is captured. ) # ── Collect streamed virtual image frames ───────────────────────────────── - # Each entry is (buf_id, frame_index, image_array). - received_frames: list[tuple[int, int, np.ndarray]] = [] + # Each entry is (buf_id, frame_index, pattern_index, image_array). + received_frames: list[tuple[int, int, int, np.ndarray]] = [] # MovieBufferStatus meanings: # UNKNOWN = 0 — status not yet determined @@ -489,7 +489,7 @@ frame is captured. for buf_id in range(NUM_VIRTUAL_BUFFERS): got_frame = False while not got_frame: - status, frame_index, image = client.get_virtual_image_buffer( + status, frame_index, pattern_index, image = client.get_virtual_image_buffer( buf_id, virtual_image_info=info, timeout_msec=1000, # Wait up to 1 s for a frame @@ -497,7 +497,7 @@ frame is captured. if status == MovieBufferStatus.OK: # Frame retrieved — store it for downstream processing - received_frames.append((buf_id, frame_index, image)) + received_frames.append((buf_id, frame_index, pattern_index, image)) got_frame = True elif status == MovieBufferStatus.FINISHED: @@ -511,7 +511,7 @@ frame is captured. elif status == MovieBufferStatus.FAILED: print( - f"buf_id={buf_id} frame={frame_index}: failed to retrieve frame — " + f"buf_id={buf_id} frame={frame_index} pattern={pattern_index}: failed to retrieve frame — " f"likely this virtual image channel is not initialized." ) got_frame = True # Skip this frame and continue @@ -595,12 +595,12 @@ frame is captured. bool gotFrame = false; while (!gotFrame) { - var (status, frameIndex, image) = + var (status, frameIndex, patternIndex, image) = client.GetVirtualImageBuffer(bufId, info, timeoutMsec: 1000); if (status == MovieBufferStatus.OK) { - receivedFrames.Add((bufId, frameIndex, image)); + receivedFrames.Add((bufId, frameIndex, patternIndex, image)); gotFrame = true; } else if (status == MovieBufferStatus.Finished) @@ -616,7 +616,7 @@ frame is captured. else // FAILED or UNKNOWN { Console.WriteLine( - $"bufId={bufId} frame={frameIndex}: failed to retrieve frame — " + + $"bufId={bufId} frame={frameIndex} pattern={patternIndex}: failed to retrieve frame — " + $"likely this virtual image channel is not initialized."); gotFrame = true; } @@ -684,7 +684,7 @@ frame is captured. // ── Collect streamed virtual image frames ───────────────────────────── // Each entry stores the detector channel, frame index, and pixel data. - using Frame = std::tuple>; + using Frame = std::tuple>; std::vector receivedFrames; // deapi::MovieBufferStatus values: @@ -699,11 +699,11 @@ frame is captured. for (int bufId = 0; bufId < NUM_VIRTUAL_BUFFERS; ++bufId) { bool gotFrame = false; while (!gotFrame) { - auto [status, frameIndex, image] = + auto [status, frameIndex, patternIndex, image] = client.getVirtualImageBuffer(bufId, info, /*timeoutMsec=*/1000); if (status == deapi::MovieBufferStatus::OK) { - receivedFrames.emplace_back(bufId, frameIndex, image); + receivedFrames.emplace_back(bufId, frameIndex, patternIndex, image); gotFrame = true; } else if (status == deapi::MovieBufferStatus::FINISHED) { finished = true; @@ -714,6 +714,7 @@ frame is captured. } else { // FAILED or UNKNOWN std::cerr << "bufId=" << bufId << " frame=" << frameIndex + << " pattern=" << patternIndex << ": failed to retrieve frame — " "likely this virtual image channel is not initialized.\n"; gotFrame = true;