@@ -558,6 +558,15 @@ def open(self) -> None:
558558
559559 self ._acquirer .start ()
560560
561+ try :
562+ self ._read_telemetry (node_map )
563+ self ._debug_frame_rate_nodes (node_map , context = "after starting acquisition" )
564+ except Exception :
565+ LOG .warning (
566+ "Failed to read telemetry after starting acquisition; some 'actual' values may be missing." ,
567+ exc_info = True ,
568+ )
569+
561570 LOG .debug (
562571 "Opened GenTL camera index=%s serial=%s label=%s" ,
563572 selected_index ,
@@ -1126,6 +1135,48 @@ def _node_symbolics(node) -> list[str]:
11261135 except Exception :
11271136 return []
11281137
1138+ @staticmethod
1139+ def _node_value (node_map , name : str , default = None ):
1140+ """Best-effort read of a GenICam node value."""
1141+ try :
1142+ node = getattr (node_map , name )
1143+ except Exception :
1144+ return default
1145+
1146+ try :
1147+ return node .value
1148+ except Exception :
1149+ return default
1150+
1151+ @classmethod
1152+ def _node_float (cls , node_map , * names : str , allow_zero : bool = False ) -> float | None :
1153+ """Return the first positive float value from a list of GenICam node names."""
1154+ for name in names :
1155+ value = cls ._node_value (node_map , name , None )
1156+ try :
1157+ fvalue = float (value )
1158+ except Exception :
1159+ continue
1160+
1161+ if fvalue > 0 or (allow_zero and fvalue == 0 ):
1162+ return fvalue
1163+
1164+ return None
1165+
1166+ @classmethod
1167+ def _node_str (cls , node_map , * names : str ) -> str | None :
1168+ """Return the first non-empty string value from a list of GenICam node names."""
1169+ for name in names :
1170+ value = cls ._node_value (node_map , name , None )
1171+ if value is None :
1172+ continue
1173+
1174+ text = str (value ).strip ()
1175+ if text :
1176+ return text
1177+
1178+ return None
1179+
11291180 def _set_enum_node (self , node_map , name : str , value : str , * , strict : bool = False ) -> bool :
11301181 node = self ._node (node_map , name )
11311182 if node is None :
@@ -1605,21 +1656,48 @@ def _configure_frame_rate(self, node_map) -> None:
16051656 return
16061657
16071658 target = float (self .settings .fps )
1659+ LOG .info ("Configuring GenTL frame rate: requested %.3f FPS" , target )
1660+
16081661 for attr in ("AcquisitionFrameRateEnable" , "AcquisitionFrameRateControlEnable" ):
16091662 try :
1610- getattr (node_map , attr ).value = True
1663+ node = getattr (node_map , attr )
1664+ before = getattr (node , "value" , None )
1665+ node .value = True
1666+ after = getattr (node , "value" , None )
1667+ LOG .info ("Enabled GenTL %s: before=%r after=%r" , attr , before , after )
16111668 break
16121669 except Exception :
16131670 pass
16141671
1615- for attr in ("AcquisitionFrameRate" , "ResultingFrameRate" , " AcquisitionFrameRateAbs" ):
1672+ for attr in ("AcquisitionFrameRate" , "AcquisitionFrameRateAbs" ):
16161673 try :
1617- getattr (node_map , attr ).value = target
1674+ node = getattr (node_map , attr )
1675+ before = getattr (node , "value" , None )
1676+ node .value = target
1677+ after = getattr (node , "value" , None )
1678+
1679+ LOG .info (
1680+ "Set GenTL %s: before=%r requested=%.3f after=%r" ,
1681+ attr ,
1682+ before ,
1683+ target ,
1684+ after ,
1685+ )
1686+
1687+ try :
1688+ accepted = float (after )
1689+ if accepted > 0 :
1690+ self ._actual_fps = accepted
1691+ except Exception :
1692+ pass
1693+
16181694 return
1695+
16191696 except AttributeError :
16201697 continue
16211698 except Exception as e :
16221699 LOG .warning ("Failed to set frame rate via %s: %s" , attr , e )
1700+
16231701 LOG .warning ("Could not set frame rate to %s FPS" , target )
16241702
16251703 def _read_telemetry (self , node_map ) -> None :
@@ -1629,20 +1707,86 @@ def _read_telemetry(self, node_map) -> None:
16291707 except Exception :
16301708 pass
16311709
1632- try :
1633- self ._actual_fps = float (node_map .ResultingFrameRate .value )
1634- except Exception :
1635- self ._actual_fps = None
1710+ # Prefer true/resulting frame-rate readback nodes.
1711+ resulting_fps = self ._node_float (
1712+ node_map ,
1713+ "AcquisitionResultingFrameRate" ,
1714+ "ResultingFrameRate" ,
1715+ "AcquisitionFrameRateResulting" ,
1716+ "DeviceFrameRate" ,
1717+ )
16361718
1637- try :
1638- self ._actual_exposure = float (node_map .ExposureTime .value )
1639- except Exception :
1640- self ._actual_exposure = None
1719+ # Fallback to requested/accepted frame-rate nodes only if no resulting node exists.
1720+ requested_fps = self ._node_float (
1721+ node_map ,
1722+ "AcquisitionFrameRate" ,
1723+ "AcquisitionFrameRateAbs" ,
1724+ )
1725+
1726+ if resulting_fps is not None :
1727+ self ._actual_fps = resulting_fps
1728+ elif requested_fps is not None :
1729+ self ._actual_fps = requested_fps
16411730
1731+ exposure = self ._node_float (
1732+ node_map ,
1733+ "ExposureTime" ,
1734+ "ExposureTimeAbs" ,
1735+ "Exposure" ,
1736+ allow_zero = True ,
1737+ )
1738+ if exposure is not None :
1739+ self ._actual_exposure = exposure
1740+
1741+ gain = self ._node_float (
1742+ node_map ,
1743+ "Gain" ,
1744+ "GainRaw" ,
1745+ allow_zero = True ,
1746+ )
1747+ if gain is not None :
1748+ self ._actual_gain = gain
1749+
1750+ # Persist useful telemetry into properties["gentl"] for GUI/debugging.
16421751 try :
1643- self ._actual_gain = float (node_map .Gain .value )
1752+ ns = self ._ensure_settings_ns ()
1753+
1754+ if self ._actual_width and self ._actual_height :
1755+ ns ["actual_resolution" ] = [int (self ._actual_width ), int (self ._actual_height )]
1756+
1757+ if self ._actual_fps is not None :
1758+ ns ["actual_fps" ] = float (self ._actual_fps )
1759+
1760+ if resulting_fps is not None :
1761+ ns ["actual_resulting_frame_rate" ] = float (resulting_fps )
1762+
1763+ if requested_fps is not None :
1764+ ns ["actual_acquisition_frame_rate" ] = float (requested_fps )
1765+
1766+ if self ._actual_exposure is not None :
1767+ ns ["actual_exposure" ] = float (self ._actual_exposure )
1768+
1769+ if self ._actual_gain is not None :
1770+ ns ["actual_gain" ] = float (self ._actual_gain )
1771+
1772+ exposure_auto = self ._node_str (node_map , "ExposureAuto" )
1773+ if exposure_auto is not None :
1774+ ns ["actual_exposure_auto" ] = exposure_auto
1775+
1776+ throughput = self ._node_float (node_map , "DeviceLinkThroughputLimit" , allow_zero = True )
1777+ if throughput is not None :
1778+ ns ["actual_device_link_throughput_limit" ] = float (throughput )
1779+
1780+ throughput_mode = self ._node_str (node_map , "DeviceLinkThroughputLimitMode" )
1781+ if throughput_mode is not None :
1782+ ns ["actual_device_link_throughput_limit_mode" ] = throughput_mode
1783+
1784+ pixel_format = self ._node_str (node_map , "PixelFormat" )
1785+ if pixel_format is not None :
1786+ ns ["actual_pixel_format" ] = pixel_format
1787+
16441788 except Exception :
1645- self . _actual_gain = None
1789+ pass
16461790
16471791 # ------------------------------------------------------------------
16481792 # Frame conversion / local helpers
0 commit comments