@@ -104,17 +104,25 @@ class Cytation5ImagingConfig:
104104 filters : Optional [List [Optional [ImagingMode ]]] = None
105105
106106
107- @contextmanager
108- def try_often (times : int = 50 ):
109- """needed because the pyspin api is extremely unreliable."""
110- for i in range (times ):
107+ def retry (func , * args , ** kwargs ):
108+ """Call func with retries and logging."""
109+ max_tries = 10
110+ delay = 0.1
111+ tries = 0
112+ while True :
111113 try :
112- yield
113- break
114- except PySpin .SpinnakerException as e :
115- print (f"Attempt { i + 1 } /{ times } : Unable to use spinnaker: { e } " )
116- else :
117- raise RuntimeError ("Unable to do it" )
114+ return func (* args , ** kwargs )
115+ except SpinnakerException as ex :
116+ tries += 1
117+ if tries >= max_tries :
118+ raise RuntimeError (f"Failed after { max_tries } tries" ) from ex
119+ logger .warning (
120+ "Retry %d/%d failed: %s" ,
121+ tries ,
122+ max_tries ,
123+ str (ex ),
124+ )
125+ time .sleep (delay )
118126
119127
120128class Cytation5Backend (ImageReaderBackend ):
@@ -875,13 +883,13 @@ def _get_device_info(self, cam):
875883 def start_acquisition (self ):
876884 if self .cam is None :
877885 raise RuntimeError ("Camera is not initialized." )
878- self .cam .BeginAcquisition ( )
886+ retry ( self .cam .BeginAcquisition )
879887 self ._acquiring = True
880888
881889 def stop_acquisition (self ):
882890 if self .cam is None :
883891 raise RuntimeError ("Camera is not initialized." )
884- self .cam .EndAcquisition ( )
892+ retry ( self .cam .EndAcquisition )
885893 self ._acquiring = False
886894
887895 async def led_on (self , intensity : int = 10 ):
@@ -1039,14 +1047,14 @@ async def set_auto_exposure(self, auto_exposure: Literal["off", "once", "continu
10391047 if self .cam .ExposureAuto .GetAccessMode () != PySpin .RW :
10401048 raise RuntimeError ("unable to write ExposureAuto" )
10411049
1042- with try_often ():
1043- self .cam .ExposureAuto .SetValue (
1044- {
1045- "off" : PySpin .ExposureAuto_Off ,
1046- "once" : PySpin .ExposureAuto_Once ,
1047- "continuous" : PySpin .ExposureAuto_Continuous ,
1048- }[auto_exposure ]
1049- )
1050+ retry (
1051+ self .cam .ExposureAuto .SetValue ,
1052+ {
1053+ "off" : PySpin .ExposureAuto_Off ,
1054+ "once" : PySpin .ExposureAuto_Once ,
1055+ "continuous" : PySpin .ExposureAuto_Continuous ,
1056+ }[auto_exposure ],
1057+ )
10501058
10511059 async def set_exposure (self , exposure : Exposure ):
10521060 """exposure (integration time) in ms, or "machine-auto" """
@@ -1065,23 +1073,19 @@ async def set_exposure(self, exposure: Exposure):
10651073 self ._exposure = "machine-auto"
10661074 return
10671075 raise ValueError ("exposure must be a number or 'auto'" )
1068- with try_often ():
1069- self .cam .ExposureAuto .SetValue (PySpin .ExposureAuto_Off )
1076+ retry (self .cam .ExposureAuto .SetValue , PySpin .ExposureAuto_Off )
10701077
10711078 # set exposure time (in microseconds)
10721079 if self .cam .ExposureTime .GetAccessMode () != PySpin .RW :
10731080 raise RuntimeError ("unable to write ExposureTime" )
10741081 exposure_us = int (exposure * 1000 )
1075- with try_often ():
1076- min_et = self .cam .ExposureTime .GetMin ()
1082+ min_et = retry (self .cam .ExposureTime .GetMin )
10771083 if exposure_us < min_et :
10781084 raise ValueError (f"exposure must be >= { min_et } " )
1079- with try_often ():
1080- max_et = self .cam .ExposureTime .GetMax ()
1085+ max_et = retry (self .cam .ExposureTime .GetMax )
10811086 if exposure_us > max_et :
10821087 raise ValueError (f"exposure must be <= { max_et } " )
1083- with try_often ():
1084- self .cam .ExposureTime .SetValue (exposure_us )
1088+ retry (self .cam .ExposureTime .SetValue , exposure_us )
10851089 self ._exposure = exposure
10861090
10871091 async def select (self , row : int , column : int ):
@@ -1239,9 +1243,13 @@ async def _acquire_image(
12391243 return image_converted .GetNDArray () # type: ignore
12401244 except SpinnakerException as e :
12411245 # the image is not ready yet, try again
1242- logger .debug ("Failed to get image: %s" , e )
1246+ logger .warning ("Failed to get image: %s" , e )
12431247 self .stop_acquisition ()
12441248 self .start_acquisition ()
1249+ if "[-1011]" in str (e ):
1250+ logger .warning (
1251+ "[-1011] error might occur when the camera is plugged into a USB hub that does not have enough throughput."
1252+ )
12451253
12461254 num_tries += 1
12471255 await asyncio .sleep (0.3 )
0 commit comments