Skip to content

Commit b2e53dc

Browse files
committed
Handle GenTL open errors & improve device matching
Wrap harvester acquisition and camera initialization in a try/except to ensure resources are cleaned up on failure and to raise a clearer RuntimeError that includes loaded/failed CTIs and the original exception. Improve device selection logic: when a target_device_id or explicit serial is provided but not found, raise a descriptive error listing available serials; otherwise keep index-based selection and validate index range.
1 parent 4032402 commit b2e53dc

1 file changed

Lines changed: 61 additions & 41 deletions

File tree

dlclivegui/cameras/backends/gentl_backend.py

Lines changed: 61 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -385,42 +385,54 @@ def open(self) -> None:
385385

386386
with type(self)._OPEN_LOCK:
387387
loaded, failed = self._resolve_and_persist_ctis()
388-
infos = self._acquire_shared_harvester(loaded)
389-
if not infos:
390-
self._reset_harvester()
391-
raise RuntimeError(
392-
"No GenTL cameras detected via Harvesters after loading producers.\n\n"
393-
f"Loaded CTIs: {loaded}\n"
394-
f"Failed CTIs: {failed}\n"
395-
"Fix: ensure your camera vendor's GenTL producer is installed and working."
396-
)
397-
398-
selected_index, selected_serial, selected_info = self._select_device(infos)
399-
self.settings.index = int(selected_index)
400-
401-
with self._shared_entry.lock:
402-
self._acquirer = self._create_image_acquirer(selected_serial, int(selected_index))
403-
node_map = self._acquirer.remote_device.node_map
404-
self._device_label = self._resolve_device_label(node_map)
405-
406-
self._configure_pixel_format(node_map)
407-
self._configure_trigger(node_map)
408-
self._configure_resolution(node_map)
409-
self._configure_exposure(node_map)
410-
self._configure_gain(node_map)
411-
self._configure_frame_rate(node_map)
412-
self._read_telemetry(node_map)
413-
self._persist_device_metadata(selected_info, selected_serial)
414-
415-
if self._fast_start:
416-
LOG.info("GenTL open() in fast_start probe mode: acquisition not started.")
417-
return
418-
419-
self._acquirer.start()
388+
try:
389+
infos = self._acquire_shared_harvester(loaded)
390+
if not infos:
391+
self._reset_harvester()
392+
raise RuntimeError(
393+
"No GenTL cameras detected via Harvesters after loading producers.\n\n"
394+
f"Loaded CTIs: {loaded}\n"
395+
f"Failed CTIs: {failed}\n"
396+
"Fix: ensure your camera vendor's GenTL producer is installed and working."
397+
)
420398

421-
LOG.debug(
422-
"Opened GenTL camera index=%s serial=%s label=%s", selected_index, selected_serial, self._device_label
423-
)
399+
selected_index, selected_serial, selected_info = self._select_device(infos)
400+
self.settings.index = int(selected_index)
401+
402+
with self._shared_entry.lock:
403+
self._acquirer = self._create_image_acquirer(selected_serial, int(selected_index))
404+
node_map = self._acquirer.remote_device.node_map
405+
self._device_label = self._resolve_device_label(node_map)
406+
407+
self._configure_pixel_format(node_map)
408+
self._configure_trigger(node_map)
409+
self._configure_resolution(node_map)
410+
self._configure_exposure(node_map)
411+
self._configure_gain(node_map)
412+
self._configure_frame_rate(node_map)
413+
self._read_telemetry(node_map)
414+
self._persist_device_metadata(selected_info, selected_serial)
415+
416+
if self._fast_start:
417+
LOG.info("GenTL open() in fast_start probe mode: acquisition not started.")
418+
return
419+
420+
self._acquirer.start()
421+
422+
LOG.debug(
423+
"Opened GenTL camera index=%s serial=%s label=%s",
424+
selected_index,
425+
selected_serial,
426+
self._device_label,
427+
)
428+
except Exception as exc:
429+
try:
430+
self.close()
431+
except Exception:
432+
pass
433+
raise RuntimeError(
434+
f"Failed to open GenTL camera.\n\nLoaded CTIs: {loaded}\nFailed CTIs: {failed}\nReason: {exc}"
435+
) from exc
424436

425437
def read(self) -> tuple[np.ndarray, float]:
426438
if self._acquirer is None:
@@ -681,12 +693,20 @@ def _select_device(self, infos: list) -> tuple[int, str | None, object]:
681693
selected_serial: str | None = None
682694

683695
if target_device_id:
684-
selected_index, selected_serial = self._match_device(infos, str(target_device_id).strip())
685-
686-
if selected_index is None and self._serial_number:
687-
selected_index, selected_serial = self._match_device(infos, str(self._serial_number).strip())
688-
689-
if selected_index is None:
696+
target = str(target_device_id).strip()
697+
selected_index, selected_serial = self._match_device(infos, target)
698+
if selected_index is None:
699+
available = [str(self._info_get(i, "serial_number", "") or "").strip() for i in infos]
700+
raise RuntimeError(f"GenTL device '{target}' not found. Available serials: {available}")
701+
702+
elif self._serial_number:
703+
serial = str(self._serial_number).strip()
704+
selected_index, selected_serial = self._match_device(infos, serial)
705+
if selected_index is None:
706+
available = [str(self._info_get(i, "serial_number", "") or "").strip() for i in infos]
707+
raise RuntimeError(f"GenTL camera with serial '{serial}' not found. Available serials: {available}")
708+
709+
else:
690710
if requested_index < 0 or requested_index >= len(infos):
691711
raise RuntimeError(f"Camera index {requested_index} out of range for {len(infos)} GenTL device(s)")
692712
selected_index = requested_index

0 commit comments

Comments
 (0)