def acquire_get_status_and_save(acquisition_request, capability_name, action_name, group_name, data_acq_client, data_store_client, verbose): """Helper function which issues an acquisition request and checks that its data is saved to the data store, and the GetStatus RPC eventually respond with a status "complete". Args: acquisition_request (AcquisitionRequestList): The acquisition request proto message to send to the AcquireData RPC. capability_name (string): The name of the data capability being acquired. action_name (string): The action name for the acquisition request's CaptureActionId. group_name (string): The group name for the acquisition request's CaptureActionId. data_acq_client (DataAcquisitionClient): The client for the data acquisition service running on robot. data_store_client (DataAcquisitionStoreClient): The client for the data acquisition store service running on robot. verbose (boolean): Print additional logging information on failure. Returns: A boolean indicating that the acquisition request received a "complete" status for the GetStatus RPC and an action id for the acquisition can be found in the data store. """ # Make a request for this data capability and check that it completes successfully. acquire_success, acquired_request_id = acquire_data_and_check_errors( acquisition_request, data_acq_client, capability_name, action_name, group_name, verbose) if not acquire_success: # Exit early if the AcquireData RPC did not succeed and did not return a request_id. return False success = monitor_status_until_complete_or_failed(acquired_request_id, data_acq_client, capability_name, action_name, verbose) if success: # If the GetStatus responds with "Complete", then check the data store for the action id. action_id = data_acquisition_pb2.CaptureActionId(action_name=action_name, group_name=group_name) query_params = data_acquisition_store_pb2.DataQueryParams( action_ids=data_acquisition_store_pb2.ActionIdQuery(action_ids=[action_id])) try: saved_capture_actions = data_store_client.list_capture_actions(query_params) if len(saved_capture_actions) == 0: # Nothing saved with a matching action and group name! _LOGGER.error( "The request %s for data '%s' with action_name '%s' did NOT save to the data " "acquisition store or returned prematurely with a STATUS_COMPLETE in the GetStatus RPC.", acquired_request_id, capability_name, action_name) if verbose: _LOGGER.info("ListCaptureAction RPC's query parameters: ", query_params) return False except ResponseError as err: _LOGGER.error( "Exception raised when checking if request %s for data '%s' with action_name '%s' was " "saved in the data acquisition store.", request_id, capability_name, action_name) if verbose: log_debug_information(err) return False else: # The GetStatus checks failed in some way. return False # The acquisition request went through, the GetStatus RPC responded with "status complete" and the data was # successfully found in the data store service. return True
def monitor_status_until_complete_or_failed(request_id, client, capability_name, action_name, verbose): """Helper status that monitors the status (using the GetStatus RPC) for the acquisition request until it completes, fails, or times out. Args: request_id (int): The request_id for the acquisition request (returned by the AcquireData RPC). client (DataAcquisitionClient): The client for the data acquisition service running on robot. capability_name (string): The data source name being acquired. action_name (string): The action name from the AcquireData RPC being monitored. verbose (boolean): Print additional logging information on failure. Returns: A boolean indicating that the GetStatus RPC eventually received a "complete" status. """ start_time = time.time() should_continue = time.time() - start_time < kMonitorStatusTimeoutSecs while should_continue: get_status_response = None try: get_status_response = client.get_status(request_id) except ResponseError as err: _LOGGER.error( "Exception raised when monitoring the status of request %s for data '%s' with action_name '%s'.", request_id, capability_name, action_name) if verbose: log_debug_information(err) return False if get_status_response.status in kAcquisitionSucceededStatuses: return True elif get_status_response.status in kAcquisitionFailedStatuses: _LOGGER.error( "Request %s for data '%s' with action_name '%s' failed the GetStatus RPC with status %s.", request_id, capability_name, action_name, data_acquisition_pb2.GetStatusResponse.Status.Name(get_status_response.status)) if verbose: _LOGGER.info("The full GetStatus response: %s", get_status_response) return False elif get_status_response.status in kAcquisitionContinuesStatuses: # Sleep briefly, then re-attempt to make a GetStatus RPC to see if the acquisition has completed. time.sleep(0.2) should_continue = time.time() - start_time < kMonitorStatusTimeoutSecs continue else: _LOGGER.error( "Unexpected status %s when monitoring request %s for data '%s' with action_name '%s'.", data_acquisition_pb2.GetStatusResponse.Status.Name(get_status_response.status), request_id, capability_name, action_name) if verbose: _LOGGER.info("The full GetStatus response: %s", get_status_response) return False _LOGGER.warning( "No result to the GetStatus RPC within %s seconds for request %s of data '%s' with action_name '%s'.", kMonitorStatusTimeoutSecs, request_id, capability_name, action_name) return True
def acquire_data_and_check_errors(acquisition_request, data_acq_client, capability_name, action_name, group_name, verbose): """Helper function which makes the acquisition request for this data source and checks for errors. Args: acquisition_request (AcquisitionRequestList): The acquisition request for the capability_name. data_acq_client (DataAcquisitionClient): The client for the on-robot data acquisition service. capability_name (string): The data source name being acquired. action_name (string): The action name for saving in the CaptureActionId of the AcquireData request. group_name (string): The group name for saving in the CaptureActionId of the AcquireData request. verbose (boolean): Print additional logging information on failure. Returns: A tuple consisting of 1) a boolean indicating that the AcquireData RPC completed without any errors, and 2) an integer representing the request_id for the acquisition request. The request_id will be None if the AcquireData RPC fails. """ acquire_data_request_id = None try: # Issue the AcquireData RPC to the on-robot data acquisition service. acquire_data_request_id = data_acq_client.acquire_data( acquisition_request, action_name, group_name) except InvalidRequestError as err: _LOGGER.error( "The AcquireData RPC to the data-acquisition service for %s was invalid: %s", capability_name, err.error_message) if verbose: _LOGGER.info( "The capture action ID associated with the request: %s", data_acquisition_pb2.CaptureActionId(action_name=action_name, group_name=group_name)) log_debug_information(err, acquisition_request) return False, acquire_data_request_id except ResponseError as err: _LOGGER.error( "Exception raised when testing the AcquireData RPC to the data-acquisition service for %s: %s", capability_name, err) if verbose: _LOGGER.info( "The capture action ID associated with the request: %s", data_acquisition_pb2.CaptureActionId(action_name=action_name, group_name=group_name)) log_debug_information(err, acquisition_request) return False, acquire_data_request_id return True, acquire_data_request_id
def cancel_request_and_monitor_status(request_id, client, capability_name, action_name, verbose): """Helper status that monitors the status (using the GetStatus RPC) for the acquisition request after it is cancelled until it recieves a "cancellation complete" status, or fails and times out. Args: request_id (int): The request_id for the acquisition request (returned by the AcquireData RPC). client (DataAcquisitionClient): The client for the data acquisition service running on robot. capability_name (string): The data source name being acquired. action_name (string): The action name from the AcquireData RPC being monitored. verbose (boolean): Print additional logging information on failure. Returns: A boolean indicating that the GetStatus RPC eventually recieved a "cancellation complete" status. """ try: cancel_res = client.cancel_acquisition(request_id) except CancellationFailedError as err: _LOGGER.error( "The CancelAcquisition RPC for request %s of data '%s' with action_name '%s' failed. " "Check that the CancelAcquisition RPC is implemented and working in the plugin service.", request_id, capability_name, action_name) if verbose: log_debug_information(err) return False except RequestIdDoesNotExistError as err: _LOGGER.error( "The request ID %s for data '%s' with action_name '%s' does not exist. Something may have " "gone wrong with the AcquireData request or the id was not saved/deleted by the plugin service.", request_id, capability_name, action_name) if verbose: log_debug_information(err) return False except ResponseError as err: _LOGGER.error( "Exception raised when cancelling request %s for data '%s' with action_name '%s'.", request_id, capability_name, action_name) if verbose: log_debug_information(err) return False start_time = time.time() should_continue = time.time() - start_time < kMonitorStatusTimeoutSecs first_time_warning = True while should_continue: get_status_response = None try: get_status_response = client.get_status(request_id) except ResponseError as err: _LOGGER.error( "Exception raised when monitoring the cancellation status of request %s for data '%s'" " with action_name '%s'.", request_id, capability_name, action_name) if verbose: log_debug_information(err) return False if get_status_response.status in kAcquisitionCancellationSucceededStatuses: return True elif get_status_response.status in kAcquisitionCancellationContinuesStatuses: # Sleep breifly, then re-attempt to make a GetStatus RPC to see if the acquisition has cancelled. time.sleep(0.2) elif get_status_response.status in kAcquisitionContinuesStatuses: # Warning that plugin did not update status to reflect that cancel rpc was recieved. if first_time_warning: _LOGGER.warning( "The plugin did not update the status to reflect that a CancelAcquisition RPC was recieved. Try " "setting the status as STATUS_CANCEL_IN_PROGRESS after responding to the RPC." ) _LOGGER.info("Request %s for data '%s' with action_name '%s", request_id, capability_name, action_name) if verbose: _LOGGER.info( "Request %s for data '%s' with action_name '%s", request_id, capability_name, action_name) _LOGGER.info("The full GetStatus response: %s", get_status_response) first_time_warning = False # Sleep breifly, then re-attempt to make a GetStatus RPC to see if the acquisition has cancelled. time.sleep(0.2) elif get_status_response.status in kAcquisitionCancellationFailedStatuses: # Cancellation-specific failure. _LOGGER.error( "The cancellation request %s for data '%s' with action_name '%s' failed the GetStatus " "RPC with status %s.", request_id, capability_name, action_name, data_acquisition_pb2.GetStatusResponse.Status.Name( get_status_response.status)) if verbose: _LOGGER.info("The full GetStatus response: %s", get_status_response) return False elif get_status_response.status in kAcquisitionFailedStatuses: # Failed for reason other than cancellation failed statuses. _LOGGER.error( "The cancellation request %s for data '%s' with action_name '%s' failed the GetStatus RPC with status %s." "This is not an expected failure status after a CancelAcquisition RPC.", request_id, capability_name, action_name, data_acquisition_pb2.GetStatusResponse.Status.Name( get_status_response.status)) if verbose: _LOGGER.info("The full GetStatus response: %s", get_status_response) return False else: _LOGGER.error( "Unexpected status %s when monitoring cancellation request %s for data '%s' with action_name '%s'.", data_acquisition_pb2.GetStatusResponse.Status.Name( get_status_response.status), request_id, capability_name, action_name) if verbose: _LOGGER.info("The full GetStatus response: %s", get_status_response) return False should_continue = time.time() - start_time < kMonitorStatusTimeoutSecs _LOGGER.warning( "Did not get a result for the GetStatus RPC within %s seconds for request %s of data '%s' with " "action_name '%s'.", kMonitorStatusTimeoutSecs, request_id, capability_name, action_name) return True
def request_image_and_save(image_sources, image_client, filepath, verbose=False): """Request the image sources in all possible formats and save the data. This makes GetImage RPCs for the image sources for each type of image format. The requests which respond without errors are then checked for completeness in the proto response, and saved. A warning message is printed if an image source cannot respond in a format that the tablet will be able to understand. Args: image_sources (List[string]): The image source names from the image service. image_client (ImageClient): The client for the image service being tested. filepath (string): A destination folder to save the images at. verbose (boolean): Print additional logging information on failure. Returns: Boolean indicating if the image sources could be successfully requested in one of the possible image formats and the image response is validated. """ successful_request_found = False successful_tablet_request_found = False # Check that one of the formats requested by the tablet will work. for img_format in ALL_FORMATS: img_req = [ build_image_request(source_name, image_format=img_format) for source_name in image_sources ] img_resps = None try: img_resps = image_client.get_image(img_req) except UnsupportedImageFormatRequestedError as err: _LOGGER.error("The image format %s is unsupported for image sources %s.", image_pb2.Image.Format.Name(img_format), image_sources) if verbose: log_debug_information(err, img_req, strip_response=True) continue except ImageDataError as err: _LOGGER.error( "The image sources (%s) were unable to be captured and decoded in format %s.", image_sources, image_pb2.Image.Format.Name(img_format)) if verbose: log_debug_information(err, img_req, strip_response=True) continue except UnknownImageSourceError as err: unknown_sources = [] for img_resp in err.response.image_responses: if img_resp.status == image_pb2.ImageResponse.STATUS_UNKNOWN_CAMERA: unknown_sources.append(img_resp.source.name) _LOGGER.error("The image sources %s are unknown by the image service.", unknown_sources) if verbose: log_debug_information(err, img_req, strip_response=True) continue except SourceDataError as err: _LOGGER.error("The image sources (%s) do not have image source information.", image_sources) if verbose: log_debug_information(err, img_req, strip_response=True) continue except ResponseTooLargeError as err: _LOGGER.warning( "Note: the response for requesting image sources %s in format %s is too large and they cannot " "all be requested at once unless the ImageClient's grpc message limit is increased.", image_sources, image_pb2.Image.Format.Name(img_format)) if verbose: log_debug_information(err, img_req, strip_response=True) # Exit out when the request is too large. return True # Check that the bare minimum required fields of the image response are populated. if len(img_resps) != len(img_req): # Found too many or too few image responses in a request for only one image. _LOGGER.warning( "The GetImageResponse RPC contains %d image responses, when %d images were requested.", len(img_resps), len(img_req)) if verbose: _LOGGER.info("GetImage requests: %s", img_req) _LOGGER.info("GetImage response: %s", [copy_image_response_and_strip_bytes(img) for img in img_resp]) continue _LOGGER.info("Successfully saved image sources %s in format %s", image_sources, image_pb2.Image.Format.Name(img_format)) for img_data in img_resps: if not validate_image_response(img_data, img_req, img_format, verbose): # The image response did not succeed in the validation checks, therefore the format # requested does not completely work. Continue to the next potential image format # and attempt to request it. continue # All checks for the image response have succeeded for this image format! successful_request_found = True if img_format in TABLET_REQUIRED_IMAGE_FORMATS: successful_tablet_request_found = True # Save all the collect images. save_images_as_files(img_resps, filepath=filepath) if not successful_tablet_request_found: _LOGGER.warning( "The image sources %s did not respond successfully to a GetImage RPC with one of the " "known image formats (%s) used by the tablet. This means the images will NOT appear successfully " "on the tablet.", image_sources, [image_pb2.Image.Format.Name(f) for f in TABLET_REQUIRED_IMAGE_FORMATS]) return successful_request_found