def test_gain_and_exposure_as_functions():
    class GainAndExposure():
        def __init__(self):
            self.gain = 0
            self.exposure = 0

        def get_gain(self):
            self.gain += 1
            return self.gain

        def get_exposure(self):
            self.exposure += 1
            return self.exposure

    # Check that gain/exposure functions are accepted inputs and get recalled each time you get the
    # capture parameters proto.
    ge = GainAndExposure()
    visual_src = VisualImageSource("source1",
                                   FakeCamera(capture_fake, decode_fake),
                                   gain=ge.get_gain,
                                   exposure=ge.get_exposure)
    capture_params = visual_src.get_image_capture_params()
    assert capture_params.gain == 1
    assert capture_params.exposure_duration.seconds == 1

    capture_params = visual_src.get_image_capture_params()
    assert capture_params.gain == 2
    assert capture_params.exposure_duration.seconds == 2
def test_make_image_source():
    # Create a visual source with no rows/cols/image type provided.
    src_name = "source1"
    visual_src = VisualImageSource(src_name,
                                   FakeCamera(capture_fake, decode_fake))
    assert visual_src.image_source_proto.name == src_name
    assert visual_src.image_source_proto.image_type == image_pb2.ImageSource.IMAGE_TYPE_VISUAL
    assert visual_src.image_source_proto.rows == 0
    assert visual_src.image_source_proto.cols == 0

    # Create a visual source with rows and cols provided.
    src_name2 = "source2"
    src_rows2 = 60
    src_cols2 = 100
    visual_src2 = VisualImageSource(src_name2,
                                    FakeCamera(capture_fake, decode_fake),
                                    src_rows2, src_cols2)
    assert visual_src2.image_source_proto.name == src_name2
    assert visual_src2.image_source_proto.image_type == image_pb2.ImageSource.IMAGE_TYPE_VISUAL
    assert visual_src2.image_source_proto.rows == src_rows2
    assert visual_src2.image_source_proto.cols == src_cols2

    # Test the static method to create an ImageSourceProto
    src_name3 = "source3"
    src_rows3 = 10
    src_cols3 = 15
    src_type3 = image_pb2.ImageSource.IMAGE_TYPE_DEPTH
    img_proto = VisualImageSource.make_image_source(src_name3, src_rows3,
                                                    src_cols3, src_type3)
    assert img_proto.name == src_name3
    assert img_proto.image_type == src_type3
    assert img_proto.rows == src_rows3
    assert img_proto.cols == src_cols3
def test_clear_not_active_faults():
    # Check that clearing faults works with non active faults.

    class MockFaultClientFaultNotActive:
        def clear_service_fault(self, service_fault_id, clear_all_service_faults=False,
                                clear_all_payload_faults=False, **kwargs):
            raise ServiceFaultDoesNotExistError(None, "error")

    visual_src = VisualImageSource("source1", FakeCamera(capture_with_error, decode_with_error))
    fault_client = MockFaultClientFaultNotActive()
    visual_src.initialize_faults(fault_client, "service1")
    assert len(visual_src.active_fault_id_names) == 0
def test_make_capture_params():
    # Create a visual source with no gain/exposure provided.
    visual_src = VisualImageSource("source1",
                                   FakeCamera(capture_fake, decode_fake))
    params = visual_src.get_image_capture_params()
    assert params.gain == 0
    assert params.exposure_duration.seconds == 0
    assert params.exposure_duration.nanos == 0

    # Create a visual source with gain and exposure provided.
    gain = 1.5
    exposure = 101.005
    visual_src = VisualImageSource("source1",
                                   FakeCamera(capture_fake, decode_fake),
                                   gain=gain,
                                   exposure=exposure)
    params = visual_src.get_image_capture_params()
    assert abs(params.gain - gain) < 1e-3
    assert abs(params.exposure_duration.seconds - 101) < 1e-3
    assert abs(params.exposure_duration.nanos - int(.005 * 1e9)) <= 1

    # Test the static method to create a CaptureParameters proto
    cap_proto = VisualImageSource.make_capture_parameters(gain, exposure)
    assert abs(cap_proto.gain - gain) < 1e-3
    assert abs(cap_proto.exposure_duration.seconds - 101) < 1e-3
    assert abs(cap_proto.exposure_duration.nanos - int(.005 * 1e9)) <= 1

    # Test the static method while passing functions for gain and exposure
    cap_proto = VisualImageSource.make_capture_parameters(
        lambda: gain, lambda: exposure)
    assert abs(cap_proto.gain - gain) < 1e-3
    assert abs(cap_proto.exposure_duration.seconds - 101) < 1e-3
    assert abs(cap_proto.exposure_duration.nanos - int(.005 * 1e9)) <= 1
def test_visual_source_with_thread():
    barrier = threading.Barrier(2)
    inc = Increment(barrier)

    visual_src = VisualImageSource(
        "source1", FakeCamera(inc.capture_increment_count, decode_fake))
    visual_src.create_capture_thread()
    # The Increment class has a barrier looking for "2" wait calls. If the blocking capture function gets
    # called as expected, and we call wait in the main thread of the test, then it will release.
    try:
        barrier.wait(timeout=1)
    except threading.BrokenBarrierError:
        pytest.fail(
            "Barrier reached the timeout, therefore blocking_capture is not called."
        )
Beispiel #6
0
def make_ricoh_theta_image_service(theta_ssid, theta_password, theta_client, robot, logger=None,
                                   use_background_capture_thread=False):
    # Create an theta instance, which will perform the HTTP requests to the ricoh theta
    # camera (using the Ricoh Theta API: https://api.ricoh/docs/#ricoh-theta-api).
    theta_instance = Theta(theta_ssid=theta_ssid, theta_pw=theta_password, client_mode=theta_client,
                           show_state_at_init=False)
    try:
        # Test that communication to the camera works before creating the complete image service.
        theta_instance.showState()
    except json.decoder.JSONDecodeError as err:
        _LOGGER.warning("Ricoh Theta faulted on initialization.")
        # The JSONDecodeError signifies that the response from the ricoh theta HTTP post requests are
        # coming back empty, meaning the camera is not setup correctly to communicate with the theta instance.
        fault_client = robot.ensure_client(FaultClient.default_service_name)
        fault = CAMERA_SETUP_FAULT
        fault.error_message = "Failed to communicate with the camera at %s: server is " % theta_instance.baseip + \
                                "responding with empty json messages."
        resp = fault_client.trigger_service_fault_async(fault)
        return False, None
    except Exception as err:
        error = "Exception raised when attempting to communicate with the ricoh theta: %s" % str(
            err)
        _LOGGER.warning(error)
        fault_client = robot.ensure_client(FaultClient.default_service_name)
        fault = CAMERA_SETUP_FAULT
        fault.error_message = error
        resp = fault_client.trigger_service_fault_async(fault)
        return False, None

    ricoh_helper = RicohThetaServiceHelper(theta_ssid, theta_instance, logger)
    img_src = VisualImageSource(ricoh_helper.image_source_name, ricoh_helper, ricoh_helper.rows,
                                ricoh_helper.cols, ricoh_helper.camera_gain,
                                ricoh_helper.camera_exposure, logger)
    return True, CameraBaseImageServicer(robot, DIRECTORY_NAME, [img_src], logger,
                                         use_background_capture_thread)
def test_not_camera_interface():
    class WrongFakeCamera():
        def blocking_capture(self):
            return 1

    # Check that the camera_interface argument ensures that it is a class which subclasses
    # the provided CameraInterface class.
    with pytest.raises(AssertionError):
        visual_src = VisualImageSource("source1", WrongFakeCamera())

    # Check that instantiating a class with camera interface without the methods expected
    # will fail on creation.
    class BadFakeCamera(CameraInterface):
        def missing_everything(self):
            pass

    with pytest.raises(TypeError):
        visual_src = VisualImageSource("source2", BadFakeCamera())
Beispiel #8
0
def make_webcam_image_service(bosdyn_sdk_robot, service_name, device_names, logger=None):
    image_sources = []
    for device in device_names:
        web_cam = WebCam(device)
        img_src = VisualImageSource(web_cam.image_source_name, web_cam, rows=web_cam.rows,
                                    cols=web_cam.cols, gain=web_cam.camera_gain,
                                    exposure=web_cam.camera_exposure)
        image_sources.append(img_src)
    return CameraBaseImageServicer(bosdyn_sdk_robot, service_name, image_sources, logger)
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
def test_faults_are_cleared_on_success():
    # Check that captures/decodes that fail and then later succeed will cause the faults to get cleared.
    class FailsThenSucceeds(CameraInterface):
        def __init__(self):
            self.capture_count = 0
            self.decode_count = 0

        def blocking_capture(self):
            self.capture_count += 1
            if self.capture_count == 1:
                raise Exception("Fake bad capture.")
            return "image", 1

        def image_decode(self, image_data, image_proto, image_format,
                         quality_percent):
            self.decode_count += 1
            if self.decode_count == 1:
                raise Exception("Fake bad decode.")

    visual_src = VisualImageSource("source1", FailsThenSucceeds())
    fault_client = MockFaultClient()
    visual_src.initialize_faults(fault_client, "service1")
    # The first call to the capture and decode functions cause a fault to be thrown.
    image, timestamp = visual_src.get_image_and_timestamp()
    assert fault_client.service_fault_counts[
        visual_src.camera_capture_fault.fault_id.fault_name] == 1
    success = visual_src.image_decode_with_error_checking(
        None, image_pb2.Image(rows=21), None, None)
    assert fault_client.service_fault_counts[
        visual_src.decode_data_fault.fault_id.fault_name] == 1

    # The second calls will succeed, and now cause the faults to be cleared.
    image, timestamp = visual_src.get_image_and_timestamp()
    assert fault_client.service_fault_counts[
        visual_src.camera_capture_fault.fault_id.fault_name] == 0
    success = visual_src.image_decode_with_error_checking(
        None, image_pb2.Image(rows=21), None, None)
    assert fault_client.service_fault_counts[
        visual_src.decode_data_fault.fault_id.fault_name] == 0
Beispiel #11
0
def make_webcam_image_service(bosdyn_sdk_robot,
                              service_name,
                              device_names,
                              show_debug_information=False,
                              logger=None,
                              codec="",
                              res_width=-1,
                              res_height=-1):
    image_sources = []
    for device in device_names:
        web_cam = WebCam(device,
                         show_debug_information=show_debug_information,
                         codec=codec,
                         res_width=res_width,
                         res_height=res_height)
        img_src = VisualImageSource(web_cam.image_source_name,
                                    web_cam,
                                    rows=web_cam.rows,
                                    cols=web_cam.cols,
                                    gain=web_cam.camera_gain,
                                    exposure=web_cam.camera_exposure)
        image_sources.append(img_src)
    return CameraBaseImageServicer(bosdyn_sdk_robot, service_name,
                                   image_sources, logger)
def _test_camera_service(use_background_capture_thread, logger=None):
    robot = MockRobot()

    src_name = "source1"
    r_amt = 10
    c_amt = 21
    gain = 25
    visual_src = VisualImageSource(src_name,
                                   FakeCamera(capture_fake, decode_fake),
                                   rows=r_amt,
                                   cols=c_amt,
                                   gain=gain)
    src_name2 = "source_cap_error"
    visual_src2 = VisualImageSource(src_name2,
                                    FakeCamera(capture_with_error,
                                               decode_fake),
                                    rows=r_amt,
                                    cols=c_amt)
    src_name3 = "source_decode_error"
    visual_src3 = VisualImageSource(src_name3,
                                    FakeCamera(capture_fake,
                                               decode_with_error),
                                    rows=r_amt,
                                    cols=c_amt)
    src_name4 = "source_capture_malformed"
    visual_src4 = VisualImageSource(src_name4,
                                    FakeCamera(capture_return_onething,
                                               decode_fake),
                                    rows=r_amt,
                                    cols=c_amt)
    src_name5 = "source_decode_malformed"
    visual_src5 = VisualImageSource(src_name5,
                                    FakeCamera(capture_fake, decode_less_args),
                                    rows=r_amt,
                                    cols=c_amt)
    src_name6 = "source2"
    visual_src6 = VisualImageSource(src_name6,
                                    FakeCamera(capture_fake, decode_fake),
                                    rows=r_amt,
                                    cols=c_amt,
                                    gain=gain)
    image_sources = [
        visual_src, visual_src2, visual_src3, visual_src4, visual_src5,
        visual_src6
    ]
    camera_service = CameraBaseImageServicer(
        robot,
        "camera-service",
        image_sources,
        use_background_capture_thread=use_background_capture_thread,
        logger=logger)

    req = image_pb2.ListImageSourcesRequest()
    resp = camera_service.ListImageSources(req, None)
    assert resp.header.error.code == header_pb2.CommonError.CODE_OK
    assert len(resp.image_sources) == 6
    found_src1, found_src2, found_src3, found_src4, found_src5, found_src6 = False, False, False, False, False, False
    for src in resp.image_sources:
        if src.name == src_name:
            found_src1 = True
        if src.name == src_name2:
            found_src2 = True
        if src.name == src_name3:
            found_src3 = True
        if src.name == src_name4:
            found_src4 = True
        if src.name == src_name5:
            found_src5 = True
        if src.name == src_name6:
            found_src6 = True
    assert found_src1 and found_src2 and found_src3 and found_src4 and found_src5 and found_src6

    # Request a known image source and make sure the response is as expected.
    req = image_pb2.GetImageRequest()
    req.image_requests.extend([
        image_pb2.ImageRequest(image_source_name=src_name, quality_percent=10)
    ])
    resp = camera_service.GetImage(req, None)
    assert resp.header.error.code == header_pb2.CommonError.CODE_OK
    assert len(resp.image_responses) == 1
    img_resp = resp.image_responses[0]
    assert img_resp.source.name == src_name
    assert img_resp.source.rows == r_amt
    assert img_resp.source.cols == c_amt
    assert img_resp.shot.capture_params.gain == gain
    assert img_resp.shot.image.rows == 15  # Output of decode_fake
    assert img_resp.shot.image.cols == c_amt
    assert abs(img_resp.shot.acquisition_time.seconds -
               10) < 1e-3  # Robot converted timestamp
    assert abs(img_resp.shot.acquisition_time.nanos -
               20) < 1e-3  # Robot converted timestamp

    # Request multiple image sources and make sure the response is complete.
    req = image_pb2.GetImageRequest()
    req.image_requests.extend([
        image_pb2.ImageRequest(image_source_name=src_name, quality_percent=10),
        image_pb2.ImageRequest(image_source_name=src_name6)
    ])
    resp = camera_service.GetImage(req, None)
    assert resp.header.error.code == header_pb2.CommonError.CODE_OK
    assert len(resp.image_responses) == 2
    found_src1, found_src6 = False, False
    for src in resp.image_responses:
        if src.source.name == src_name:
            found_src1 = True
        if src.source.name == src_name6:
            found_src6 = True
    assert found_src6 and found_src1

    # Request an image source that does not exist.
    req = image_pb2.GetImageRequest()
    req.image_requests.extend([
        image_pb2.ImageRequest(image_source_name="unknown", quality_percent=10)
    ])
    resp = camera_service.GetImage(req, None)
    assert resp.header.error.code == header_pb2.CommonError.CODE_OK
    assert len(resp.image_responses) == 1
    img_resp = resp.image_responses[0]
    assert img_resp.status == image_pb2.ImageResponse.STATUS_UNKNOWN_CAMERA

    # Request an image from a source with a bad capture function.
    req = image_pb2.GetImageRequest()
    req.image_requests.extend([
        image_pb2.ImageRequest(image_source_name=src_name2, quality_percent=10)
    ])
    resp = camera_service.GetImage(req, None)
    assert resp.header.error.code == header_pb2.CommonError.CODE_OK
    assert len(resp.image_responses) == 1
    img_resp = resp.image_responses[0]
    assert img_resp.status == image_pb2.ImageResponse.STATUS_IMAGE_DATA_ERROR

    # Request an image from a source with a decode error.
    req = image_pb2.GetImageRequest()
    req.image_requests.extend([
        image_pb2.ImageRequest(image_source_name=src_name3, quality_percent=10)
    ])
    resp = camera_service.GetImage(req, None)
    assert resp.header.error.code == header_pb2.CommonError.CODE_OK
    assert len(resp.image_responses) == 1
    img_resp = resp.image_responses[0]
    print(img_resp)
    assert img_resp.status == image_pb2.ImageResponse.STATUS_UNSUPPORTED_IMAGE_FORMAT_REQUESTED

    # Request an image with a malformed capture function (should raise an error so developer can fix).
    req = image_pb2.GetImageRequest()
    req.image_requests.extend([
        image_pb2.ImageRequest(image_source_name=src_name4, quality_percent=10)
    ])
    resp = camera_service.GetImage(req, None)
    assert resp.header.error.code == header_pb2.CommonError.CODE_OK
    assert len(resp.image_responses) == 1
    img_resp = resp.image_responses[0]
    assert img_resp.status == image_pb2.ImageResponse.STATUS_IMAGE_DATA_ERROR
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