spot-sdk-js
Version:
Develop applications and payloads for Spot using the unofficial Boston Dynamics Spot Node.js SDK.
287 lines (253 loc) • 12.5 kB
JavaScript
const {ResponseError} = require('./exceptions');
const {
common_header_errors,
error_factory,
handle_common_header_errors,
handle_unset_status_error,
error_pair,
BaseClient
} = require('./common');
const data_acquisition = require('../bosdyn/api/data_acquisition_pb');
const data_acquisition_service = require('../bosdyn/api/data_acquisition_service_grpc_pb');
const {now_timestamp} = require('../bosdyn-core/util');
class DataAcquisitionResponseError extends ResponseError {
constructor(msg){
super(msg);
this.name = 'DataAcquisitionResponseError';
}
};
class RequestIdDoesNotExistError extends DataAcquisitionResponseError {
constructor(msg){
super(msg);
this.name = 'RequestIdDoesNotExistError';
}
};
class UnknownCaptureTypeError extends DataAcquisitionResponseError {
constructor(msg){
super(msg);
this.name = 'UnknownCaptureTypeError';
}
};
class CancellationFailedError extends DataAcquisitionResponseError {
constructor(msg){
super(msg);
this.name = 'CancellationFailedError';
}
};
/**
* A client for triggering data acquisition and logging.
* @class DataAcquisitionClient
* @extends BaseClient
*/
class DataAcquisitionClient extends BaseClient {
static default_service_name = 'data-acquisition';
static service_type = 'bosdyn.api.DataAcquisitionService';
/**
* Create an instance of AuthClient's class.
* @param {?string} name
*/
constructor(name=null){
super(data_acquisition_service.DataAcquisitionServiceClient, name);
this._timesync_endpoint = null;
}
/**
* Update instance from another object.
* @param {object} other The object where to copy from.
* @return {void}
*/
update_from(other){
super.update_from(other);
try{
this._timesync_endpoint = other.time_sync.endpoint;
}catch(e){
}
}
/**
* Trigger a data acquisition to save data and metadata to the data buffer.
* @param {object} acquisition_requests The different image sources and
* data sources to capture from and save to the data buffer with the same timestamp.
* @param {string} action_name The unique action name that all data will be saved with.
* @param {string} group_name The unique group name that all data will be saved with.
* @param {?google.protobuf.Timestamp} data_timestamp The unique timestamp that all data will be
* saved with.
* @param {?(Metadata|object)} metadata The JSON structured metadata to be associated with
* the data returned by the DataAcquisitionService when logged in the data buffer
* service.
* @param {object} [args] Extra arguments for controlling RPC details.
* @return {*} If the RPC is successful, then it will return the acquire data request id, which can be
* used to check the status of the acquisition and get feedback.
* @throws {RpcError} Problem communicating with the robot.
*/
async acquire_data(acquisition_requests, action_name, group_name, data_timestamp = null, metadata = null, args){
if(data_timestamp == null){
if(!this._timesync_endpoint){
data_timestamp = now_timestamp();
}else{
data_timestamp = this._timesync_endpoint.robot_timestamp_from_local_secs(Date.now());
}
}
const action_id = new data_acquisition.CaptureActionId()
.setActionName(action_name)
.setGroupName(group_name)
.setTimestamp(data_timestamp);
const metadata_proto = metadata_to_proto(metadata);
const request = new data_acquisition.AcquireDataRequest()
.setActionId(action_id)
.setMetadata(metadata_proto)
.setAcquisitionRequests(acquisition_requests);
return await this.call(this._stub.acquireData, request, get_request_id, acquire_data_error, args);
}
/**
* Async version of acquire_data().
* @param {object} acquisition_requests The different image sources and
* data sources to capture from and save to the data buffer with the same timestamp.
* @param {string} action_name The unique action name that all data will be saved with.
* @param {string} group_name The unique group name that all data will be saved with.
* @param {?google.protobuf.Timestamp} data_timestamp The unique timestamp that all data will be
* saved with.
* @param {?(Metadata|object)} metadata The JSON structured metadata to be associated with
* the data returned by the DataAcquisitionService when logged in the data buffer
* service.
* @param {object} [args] Extra arguments for controlling RPC details.
* @return {*} If the RPC is successful, then it will return the acquire data request id, which can be
* used to check the status of the acquisition and get feedback.
* @throws {RpcError} Problem communicating with the robot.
*/
acquire_data_async(acquisition_requests, action_name, group_name, data_timestamp = null, metadata = null, args){
if(data_timestamp == null){
if(!this._timesync_endpoint){
data_timestamp = now_timestamp();
}else{
data_timestamp = this._timesync_endpoint.robot_timestamp_from_local_secs(Date.now());
}
}
const action_id = new data_acquisition.CaptureActionId()
.setActionName(action_name)
.setGroupName(group_name)
.setTimestamp(data_timestamp);
const metadata_proto = metadata_to_proto(metadata);
const request = new data_acquisition.AcquireDataRequest()
.setActionId(action_id)
.setMetadata(metadata_proto)
.setAcquisitionRequests(acquisition_requests);
return this.call_async(this._stub.acquireData, request, get_request_id, acquire_data_error, args);
}
/**
* Check the status of a data acquisition based on the request id.
* @param {number} request_id The request id associated with an AcquireData request.
* @param {object} [args] Extra arguments for controlling RPC details.
* @return {*} If the RPC is successful, then it will return the full status response, which includes the
* status as well as other information about any possible errors.
* @throws {RpcError} Problem communicating with the robot.
* @throws {RequestIdDoesNotExistError} The request id provided is incorrect.
*/
async get_status(request_id, args){
const request = new data_acquisition.GetStatusRequest().setRequestId(request_id);
return await this.call(this._stub.getStatus, request, null, _get_status_error, args);
}
/**
* Async version of get_status().
* @param {number} request_id The request id associated with an AcquireData request.
* @param {object} [args] Extra arguments for controlling RPC details.
* @return {*} If the RPC is successful, then it will return the full status response, which includes the
* status as well as other information about any possible errors.
* @throws {RpcError} Problem communicating with the robot.
* @throws {RequestIdDoesNotExistError} The request id provided is incorrect.
*/
get_status_async(request_id, args){
const request = new data_acquisition.GetStatusRequest().setRequestId(request_id);
return this.call_async(this._stub.getStatus, request, null, _get_status_error, args);
}
/**
* Get information from a DAQ service to list it's capabilities - which data, metadata,
* or processing the DAQ service will perform.
* @param {object} [args] Extra arguments for controlling RPC details.
* @return {data_acquisition.GetServiceInfoResponse} The GetServiceInfoResponse message, which contains all the different capabilities.
* @throws {RpcError} Problem communicating with the robot.
*/
async get_service_info(args){
const request = new data_acquisition.GetServiceInfoRequest();
return await this.call(this._stub.getServiceInfo, request, _get_service_info_capabilities, common_header_errors, args);
}
/**
* Async version of get_service_info().
* @param {object} [args] Extra arguments for controlling RPC details.
* @return {data_acquisition.GetServiceInfoResponse} The GetServiceInfoResponse message, which contains all the different capabilities.
* @throws {RpcError} Problem communicating with the robot.
*/
get_service_info_async(args){
const request = new data_acquisition.GetServiceInfoRequest();
return this.call_async(this._stub.getServiceInfo, request, _get_service_info_capabilities, common_header_errors, args);
}
/**
* Cancel a data acquisition based on the request id.
* @param {number} request_id The request id associated with an AcquireData request.
* @param {object} [args] Extra arguments for controlling RPC details.
* @return {*} If the RPC is successful, then it will return the full status response, which includes the
* status as well as other information about any possible errors.
* @throws {RpcError} Problem communicating with the robot.
* @throws {CancellationFailedError} The data acquisitions associated with the request id were unable to be cancelled.
* @throws {RequestIdDoesNotExistError} The request id provided is incorrect.
*/
async cancel_acquisition(request_id, args){
const request = new data_acquisition.CancelAcquisitionRequest().setRequestId(request_id);
return await this.call(this._stub.cancelAcquisition, request, null, _cancel_acquisition_error, args);
}
/**
* Async version of cancel_acquisition().
* @param {number} request_id The request id associated with an AcquireData request.
* @param {object} [args] Extra arguments for controlling RPC details.
* @return {*} If the RPC is successful, then it will return the full status response, which includes the
* status as well as other information about any possible errors.
* @throws {RpcError} Problem communicating with the robot.
* @throws {CancellationFailedError} The data acquisitions associated with the request id were unable to be cancelled.
* @throws {RequestIdDoesNotExistError} The request id provided is incorrect.
*/
cancel_acquisition_async(request_id, args){
const request = new data_acquisition.CancelAcquisitionRequest().setRequestId(request_id);
return this.call_async(this._stub.cancelAcquisition, request, null, _cancel_acquisition_error, args);
}
};
const _ACQUIRE_DATA_STATUS_TO_ERROR = {
STATUS_OK: [null, null],
STATUS_UNKNOWN_CAPTURE_TYPE: error_pair(UnknownCaptureTypeError)
};
const _GET_STATUS_STATUS_TO_ERROR = {
STATUS_REQUEST_ID_DOES_NOT_EXIST: error_pair(RequestIdDoesNotExistError)
};
const _CANCEL_ACQUISITION_STATUS_TO_ERROR = {
STATUS_REQUEST_ID_DOES_NOT_EXIST: error_pair(RequestIdDoesNotExistError),
STATUS_FAILED_TO_CANCEL: error_pair(CancellationFailedError)
};
function metadata_to_proto(metadata){
let metadata_proto = null;
if(metadata instanceof data_acquisition.Metadata){
metadata_proto = metadata;
}else if(metadata instanceof Object){
metadata_proto = new data_acquisition.Metadata();
metadata_proto.setData(metadata);
}
return metadata_proto;
}
function acquire_data_error(response){
return error_factory(response, response.getStatus(), Object.keys(data_acquisition.AcquireDataResponse.Status), _ACQUIRE_DATA_STATUS_TO_ERROR);
}
function _get_status_error(response){
return error_factory(response, response.getStatus(), Object.keys(data_acquisition.GetStatusResponse.Status), _GET_STATUS_STATUS_TO_ERROR);
}
function _cancel_acquisition_error(response){
return error_factory(response, response.getStatus(), Object.keys(data_acquisition.CancelAcquisitionResponse.Status), _CANCEL_ACQUISITION_STATUS_TO_ERROR);
}
function _get_service_info_capabilities(response){
return response.getCapabilities();
}
function get_request_id(response){
return response.getRequestId();
}
module.exports = {
DataAcquisitionClient,
DataAcquisitionResponseError,
RequestIdDoesNotExistError,
UnknownCaptureTypeError,
CancellationFailedError
};