def _do_image_capture(self): """Main loop for the image capture thread, which requests and saves images.""" # The service fault to report when this thread fails to capture an image from a source capture_fault_id = service_fault_pb2.ServiceFaultId( fault_name='Image Capture Failure', service_name=self.service_name) capture_fault = service_fault_pb2.ServiceFault( fault_id=capture_fault_id, error_message='Failed to capture an image from ' + self.device_name, severity=service_fault_pb2.ServiceFault.SEVERITY_WARN) try: self.fault_client.clear_service_fault(capture_fault_id) except ServiceFaultDoesNotExistError: pass fault_active = False while self.is_alive: # Get the image from the video capture. capture_time = time.time() success, image = self.capture.read() if success: self.set_last_frame(image, capture_time) if fault_active: self.fault_client.clear_service_fault(capture_fault_id) fault_active = False elif not fault_active: self.fault_client.trigger_service_fault(capture_fault) fault_active = True # Cleanup the video capture when the thread is done. self.capture.release()
def direct_faulting(robot): """Trigger and clear ServiceFaults directly using the SDK clients methods.""" # Set up robot state client robot_state_client = robot.ensure_client( RobotStateClient.default_service_name) # Set up fault client fault_client = robot.ensure_client(FaultClient.default_service_name) # Set up fake service fault service_fault = service_fault_pb2.ServiceFault() service_fault.fault_id.fault_name = 'ManualExampleFault' service_fault.fault_id.service_name = kServiceName service_fault.error_message = 'Example service fault triggered by the service_faults.py SDK example.' service_fault.severity = service_fault_pb2.ServiceFault.SEVERITY_WARN # Trigger the service fault try: fault_client.trigger_service_fault(service_fault) except ServiceFaultAlreadyExistsError: print('\nServiceFault was already registered. Proceeding...') # Continually display ServiceFaults. Should see the manually triggered fault. print('\n\n\nShould see locally triggered fault') watch_service_fault_state(robot_state_client, 4) # Clear the service fault fault_client.clear_service_fault(service_fault.fault_id) # Continually display ServiceFaults. Should not see the manually triggered fault. print('\n\n\nShould not see locally triggered fault.') watch_service_fault_state(robot_state_client, 4)
def test_active_faults(): # Check that active faults are managed correctly. visual_src = VisualImageSource("source1", FakeCamera(capture_with_error, decode_with_error)) fault_client = MockFaultClient() visual_src.initialize_faults(fault_client, "service1") # Initialize function calls the clear_service_fault RPC. assert fault_client.clear_service_fault_called_count == 1 assert len(visual_src.active_fault_id_names) == 0 fake_fault_id = service_fault_pb2.ServiceFaultId(fault_name="Fake Capture Failure") fake_fault = service_fault_pb2.ServiceFault( fault_id=fake_fault_id, severity=service_fault_pb2.ServiceFault.SEVERITY_WARN) # Make sure we don't call clear_service_fault RPC because the fake fault is not active yet. visual_src.clear_fault(fake_fault) assert len(visual_src.active_fault_id_names) == 0 assert fault_client.clear_service_fault_called_count == 1 # Make the fake fault active. visual_src.trigger_fault("error", fake_fault) assert len(visual_src.active_fault_id_names) == 1 assert fault_client.service_fault_counts[fake_fault.fault_id.fault_name] == 1 # Clear the fake fault, but make sure RPC is only called once. visual_src.clear_fault(fake_fault) assert fault_client.clear_service_fault_called_count == 2 visual_src.clear_fault(fake_fault) # Make sure clear_service_fault is not called for a cleared fault. assert fault_client.clear_service_fault_called_count == 2 assert len(visual_src.active_fault_id_names) == 0
def __init__(self, image_name, camera_interface, rows=None, cols=None, gain=None, exposure=None, logger=None): self.image_source_name = image_name self.image_source_proto = self.make_image_source( image_name, rows, cols) self.get_image_capture_params = lambda: self.make_capture_parameters( gain, exposure) # Ensure the camera_interface is a subclass of CameraInterface and has the defined capture and # decode methods. assert isinstance(camera_interface, CameraInterface) self.camera_interface = camera_interface self.capture_function = lambda: self._do_capture_with_error_checking( self.camera_interface.blocking_capture) # Optional background thread to continuously capture image data. This will help an image # service to respond quickly to a GetImage request, since it can use the last captured image. self.capture_thread = None # Fault client to report errors. Requires the image service name to # properly create the fault id. self.fault_client = None # Create fault to throw if a failure occurs when calling the blocking_capture function. camera_capture_fault_id = service_fault_pb2.ServiceFaultId( fault_name="Image Capture Failure for %s" % self.image_source_name) self.camera_capture_fault = service_fault_pb2.ServiceFault( fault_id=camera_capture_fault_id, severity=service_fault_pb2.ServiceFault.SEVERITY_WARN) # Create a fault to throw if a failure occurs when calling the image_decode function. decode_data_fault_id = service_fault_pb2.ServiceFaultId( fault_name="Decoding Image %s Failure" % self.image_source_name) self.decode_data_fault = service_fault_pb2.ServiceFault( fault_id=decode_data_fault_id, severity=service_fault_pb2.ServiceFault.SEVERITY_WARN) # Logger for warning messages and the last error message (used only when the background thread # is capturing such that error messages from the last capture can be shown still). self.logger = logger or _LOGGER self.last_error_message = None
def test_faults_in_visual_source(): # Create the fault client fault_client = MockFaultClient() # Populate fault client with at least one "old" fault. fault_client.trigger_service_fault_async( service_fault_pb2.ServiceFault( fault_id=service_fault_pb2.ServiceFaultId(fault_name="fault1"))) init_fault_amount = fault_client.get_total_fault_count() visual_src = VisualImageSource( "source1", FakeCamera(capture_with_error, decode_with_error)) # Attempt to get an image with no fault client enabled. Make sure no error is raised, and # values are returned as none. image, timestamp = visual_src.get_image_and_timestamp() assert image is None assert timestamp is None # attempt to decode an image with no fault client enabled. Make sure no error is raised. im_proto = image_pb2.Image(rows=10) success = visual_src.image_decode_with_error_checking( None, im_proto, None, None) assert im_proto.rows == 15 assert not success # Setup faults visual_src.initialize_faults(fault_client, "service1") assert fault_client.get_total_fault_count() == 0 assert visual_src.camera_capture_fault is not None assert visual_src.camera_capture_fault.fault_id.service_name == "service1" assert visual_src.decode_data_fault is not None assert visual_src.decode_data_fault.fault_id.service_name == "service1" # With fault client setup, check that faults are thrown when the bad functions get called. image, timestamp = visual_src.get_image_and_timestamp() assert image is None assert timestamp is None assert fault_client.service_fault_counts[ visual_src.camera_capture_fault.fault_id.fault_name] == 1 im_proto = image_pb2.Image(rows=21) success = visual_src.image_decode_with_error_checking( None, im_proto, None, None) assert im_proto.rows == 15 assert fault_client.service_fault_counts[ visual_src.decode_data_fault.fault_id.fault_name] == 1 assert not success
PLACEHOLDER_PAYLOAD = payload_protos.Payload() PLACEHOLDER_PAYLOAD.name = 'Ricoh Theta Image Service' PLACEHOLDER_PAYLOAD.description = 'This is currently a virtual/weightless payload defined for the software service only. \ Please define weight and dimensions if the Ricoh Theta is mounted to Spot.' # See https://dev.bostondynamics.com/docs/payload/configuring_payload_software#registering-payloads for more information. DIRECTORY_NAME = 'ricoh-theta-image-service' AUTHORITY = 'robot-ricoh-theta' SERVICE_TYPE = 'bosdyn.api.ImageService' _LOGGER = logging.getLogger(__name__) # Define ServiceFaultIds for possible faults that will be thrown by the Ricoh Theta image service. CAMERA_SETUP_FAULT = service_fault_pb2.ServiceFault( fault_id=service_fault_pb2.ServiceFaultId(fault_name='Ricoh Theta Initialization Failure', service_name=DIRECTORY_NAME), severity=service_fault_pb2.ServiceFault.SEVERITY_CRITICAL) class RicohThetaServiceHelper(CameraInterface): def __init__(self, theta_ssid, theta_instance, logger=None): # Setup the logger. self.logger = logger or _LOGGER # Name of the image source that is being requested from. self.theta_ssid = theta_ssid self.image_source_name = "RicohTheta_" + theta_ssid # Default value for JPEG image quality, in case one is not provided in the GetImage request. self.default_jpeg_quality = 95
from bosdyn.api import image_pb2 from bosdyn.api import image_service_pb2 from bosdyn.api import image_service_pb2_grpc from bosdyn.api import service_fault_pb2 from ricoh_theta import Theta DIRECTORY_NAME = 'ricoh-theta-image-service' AUTHORITY = 'robot-ricoh-theta' SERVICE_TYPE = 'bosdyn.api.ImageService' _LOGGER = logging.getLogger(__name__) # Define ServiceFaultIds for possible faults that will be thrown by the Ricoh Theta image service. CAMERA_SETUP_FAULT = service_fault_pb2.ServiceFault( fault_id=service_fault_pb2.ServiceFaultId(fault_name='Ricoh Theta Initialization Failure', service_name=DIRECTORY_NAME), severity=service_fault_pb2.ServiceFault.SEVERITY_CRITICAL) CAPTURE_PARAMETERS_FAULT = service_fault_pb2.ServiceFault( fault_id=service_fault_pb2.ServiceFaultId(fault_name='Get CaptureParameters Failure', service_name=DIRECTORY_NAME), error_message="Failed to get exposure/gain parameters.", severity=service_fault_pb2.ServiceFault.SEVERITY_WARN) CAPTURE_FAILURE_FAULT = service_fault_pb2.ServiceFault( fault_id=service_fault_pb2.ServiceFaultId(fault_name='RicohTheta Image Capture Failure', service_name=DIRECTORY_NAME), severity=service_fault_pb2.ServiceFault.SEVERITY_WARN) class RicohThetaImageServicer(image_service_pb2_grpc.ImageServiceServicer): """GRPC service to provide access to multiple different image sources. The service can list the