Skip to main content

gRPC Service Call Service

Services are a low-level abstraction for instructing an individual robot to perform an action, using a request/response message paradigm.

Protocol and Server Selection

You can request the protocol file from DroneDeploy support team. Refer to the gRPC documentation regarding how to use the gRPC tools to compile this protocol file into usable library code in your preferred language.

The Service Call service which this protocol allows access to is provided by a gRPC server: there are two server implementations which you may be using:

  • The service-gateway cloud server, named serviette, a part of the Robot Automation cloud platform (api2.rocos.io:443 for most regions, and used for regular cloud platform connectivity).
  • An instance of the service-gateway agent plugin being hosted by an agent on one of your robots (use for situations where your robots need to operate in an internet denied environment).

Setting Up a Connection

The Service server will only accept incoming gRPC connections which use some specific parameters, provided by way of Dial Options, Call Options, and Call Context Meta-data:

Keep-alive Parameters

Ensure that you Dial the connection using compatible keep-alive options, or the server may hang up unexpectedly:

PermitWithoutStream: true
Time: 5 sec
Timeout: 5 sec

Credentials and Authentication

If you are connecting to the serviette cloud server, you must use a gRPC connection with a secure channel. A default set of TLS/SSL transport credentials must be provided as a Dial Option.

You will need to include an appropriate JWT authentication token as a Call Option each time you make a request to the server (which you can do using the PerRPCCredentials option). Alternatively, this can be provided as a Meta-data field in the Call Context.

Context

Each request to the server must be accompanied with appropriate meta-data:

FieldTypeDescription
r-csstringThe robot callsign of the robot this request relates to.
r-pstringThe project ID of the project containing the robot in question.
r-sstringThe robot subsystem identifier of the robot this request relates to. At the present, you should leave this empty.
authorizationstringJWT_token for authentication, found inside the agent.json configuration file on the robot.
grpc-timeoutintOptional. This value can be used to set a deadline for the gRPC request to be completed within, after which the request will be aborted.

Service Structure

There are six different gRPC services exposed by the serviette proto. One, ServiceReceiver is for registering a Service Receiver, which will be the DroneDeploy Agent in most cases. ServiceCaller and WebServiceCaller, are for calling/invoking a Service Call from a client (using a regular gRPC client and a web-gRPC client respectively); this is the one you will be using.

Note

The services WebServiceReceiver, MeshAdmin and Mesh are not implemented at this time.

Sending/Invoking Service Requests using ServiceCaller

After ensuring the connection parameters are correct, you should be able to dial the serviette server and create a new client stub ServiceCallerClient, upon which you can perform service requests.

To instruct a robot to perform a Service Call, use CallServices(). The method takes a stream of RequestChannelMessage structures which specifies the target Robot and the payload of the Request which should be invoked. Each RequestChannelMessage can hold one of four possible types:

RequestChannelMessage

NameTypeDescription
heartbeatHeartbeatAn empty messages, used to indicate that the stream is still operating correctly. The server will hang up any stream which has not transmitted at least one RequestChannelMessage in the two seconds, so if you have no requests to make, you should periodically send a heartbeat to keep the stream alive.
requestsServiceRequestsOne or more requests to invoke a named service. Multiple requests may be sent within a single RequestChannelMessage.
chunksServiceRequestChunksOne or more chunks of a single large request to invoke a named service. If a single request is too large to transmit in a single gRPC message then the request should be broken into chunks to allow transmission: all requests larger than [slightly less than 10MB] must be chunked, but DroneDeploy recommends that any requests larger than 100kB also be chunked to improve responsiveness. While the RequestChannelMessage allows multiple chunks to be passed at once, attempting to send multiple payload chunks in the same message is self-defeating.
cancellationsRequestCancellationsOne or more instructions to cancel an existing Service Call which is in progress.

The CallServices method returns a stream of ResponseChannelMessage messages back to from the server, which inform the caller about the status of the Service Call. Each response message may hold one of three possible response types:

ResponseChannelMessage

NameTypeDescription
heartbeatHeartbeatAn empty messages, used to indicate that the stream is still operating correctly. The server will send a heartbeat message no less than once every two seconds, if no other ResponseChannelMessage has been sent in that time.
responsesServiceResponsesOne or more responses from an invoked Service Call. While in principle, responses from multiple requests maybe sent within a single ResponseChannelMessage, at this time the server implementation will only send one response per ResponseChannelMessage.
chunksServiceResponseChunksOne or more chunks of a response from an invoked Service Call. The chunking behaviour is determined by the agent, not the server; at this time, responses larger than 100kB will be automatically chunked, and each ResponseChannelMessage will contain a single chunk.

Sending/Invoking Service Requests using WebServiceCaller

After ensuring the connection parameters are correct, you should be able to dial the serviette server and create a new client stub WebServiceCallerClient, upon which you can perform service requests.

To instruct a robot to perform a Service Call, use CallService(). The method takes a ServiceRequest structure which specifies the target Robot and the payload of the Request which should be invoked. You may also use CallServices() to call multiple services at once.

Because the web compatible service only takes a unary argument, there is no need for an outgoing heartbeat, and there is no ability to break the outgoing request up into chunks.

The CallServices method returns a stream of ResponseChannelMessage messages back to from the server, which inform the caller about the status of the Service Call. Behaviour is the same as for the streaming service.

Request and Response Types

ServiceRequest

FieldTypeDescription
uidServiceCallUIDA globally unique ID which can be used to identify this particular operation, allowing responses to be distinguished. You should generate a random 16-byte value for the hash field, and set the version field to HEADER_HASH_RAND.
header.sourceuri.v1.RocosURIA object encapsulating a data URI which describes the destination where the Service Call should be invoked. For example:

{Project: "MyProject", Callsign: "MyRobot", Subsystem: "", Component: "ros", Topic: "MyRosService"}
header.createdint64The time in nanoseconds since the Unix epoch when the Request was created.
header.metamap<string, string>Any payload-specific meta-data to be supplied with the message (for ROS services, this must include the ROS type to convert into).
header.responseLevelServiceResponseLevelThe level of response which the invoker is expecting from the the execution (this is basically equivalent to verbosity):

ALL = 0; Receive transmission acknowledgements, Service Call return values, and Service Call result.

RETURN_AND_RESULT = 1; Receive Service Call return values and Service Call result.

RESULT_ONLY = 2; Receive Service Call result only.

NONE = 3; Receive no response at all (fire and forget).
payloadbytesThe raw content of the Request, normally this will be a base64 encoded JSON object.

Service Response

FieldTypeDescription
uidServiceCallUIDThe unique transaction ID of the corresponding Service Call request.
ackServiceAckA simple acknowledgement of the Service Call's progress through routing, delivery and execution.
returnServiceReturnA message containing data which is being returned from the execution of the Service Call itself. These messages typically encapsulate the "response" part of the request/response mechanic.
resultServiceResultA single message indicating the execution of the Service Call has completed, and the result the execution ended.

Based on the value of responseLevel passed with the request, some of these response messages may not be transmitted.

Service Response Types

ServiceAck

ServiceAck is used to provide simple acknowledgement that the Service Call request has reached different stages of the routing/delivery/execution pathway, and that execution is ongoing:

FieldTypeDescription
stageenumIndicates which part of the delivery/execution pipeline is acknowledging the request.

AGENT = 0; From the agent itself.

CLOUD_GATEWAY = 1; From the cloud gateway server.

DEVICE_GATEWAY = 2; From a gateway server running as an Agent plugin.
statusenumIndicates the status of the acknowledgement.

RECEIVED = 0; The Service Call has been received and processed.

QUEUED = 1; The Service Call has been queued but is not being executed yet.

PROGRESS = 2; Execution of the Service Call has begun and the Service Call is in progress; the Agent may send this message multiple times, so as to periodically keep the caller informed that a long running Service Call is still in progress.
messagestringIn the case of PROGRESS messages, the Command may include a brief text string to indicate how it is progressing.

ServiceReturn

ServiceReturn is used for carrying output (response) data from the Service Call itself back to the caller. The specific nature of the response message will depend on the Service Call being invoked. For a long running Service Call, there may be multiple return messages:

FieldTypeDescription
header.createdint64The time in nanoseconds since the Unix Epoch when the Return message was created.
header.metamap<string,string>Any message meta-data which accompanies the payload.
payloadbytesThe raw content of the Response, normally this will be a base64 encoded JSON object.

ServiceResult

ServiceResult will be returned when the Service Call execution is completed; a single Service Call will return one result message; there will be no further response message after the result. The result will indicate the reason that the execution is completed:

FieldTypeDescription
statusenumCOMPLETE_SUCCESS = 0;Service Call completed successfully.

CANCELLED = 10;Service Call was cancelled by request from the caller.

COMPLETE_ERROR = 11;Service Call was started, but ended reporting an error.

TIMED OUT = 12;Service Call timed out during delivery or execution.

REJECTED_ID = 13;Service Call was rejected because its command ID was invalid.

REJECTED_AUTH = 14; Service Call was rejected because of an authentication failure.

REJECTED_PAYLOAD = 15;``Service Call was rejected because the payload was invalid.

REJECTED_NO_RECEIVER = 16; The Service Call could not be routed to the Robot.

FATAL = 17; Some type of unknown internal error.
messagestringIf the Service Call did not complete successfully, then a brief text description of the reason may be provided.

A single Service Call transaction will always end with a single ServiceResult message. In the case of web compatible APIs, after the last Service Call transaction is completed, the stream will be closed. A typical sequence of responses for a simple Service Call which executes quickly and returns a single value would be:

Ack from cloud gateway, Ack from agent, Return data, Result.

Message Chunking

Both large Requests and Responses are broken up into chunks using the same chunking strategy. A single ServiceRequestChunk is constructed as follows:

FieldTypeDescription
uidServiceCallUIDThe unique transaction ID of the Service Call request; each of the chunks must have the same ID.
chunkIndexuint32The index of this chunk within the total set of chunks making up this request. Since the header chunk always comes first, the header chunk always has index 0, with the payload chunks incrementing subsequently.
chunkCountuint32The total number of chunks which make up this request.
content.headerServiceRequestHeaderThe first chunk in a request consists of the header data by itself.
content.payloadbytesEach subsequent chunk consists of a sequential set of bytes which make up the complete payload of the request. The exact size of each chunk is not fixed, and may vary; the important thing is that the chunks are transmitted in order, and when the payloads are concatenated together, the original request payload is restored.

Each message that is broken up into chunks consists of a header chunk, followed by N payload chunks. Chunks must be transmitted in the correct order for reassembly.

Note that the server does not manipulate, verify or unpack chunks; chunks are transmitted between the agent and client without any alteration.

Cancellation

Long running Service Calls may be interrupted by issuing a cancellation: each RequestCancellations message consists of one or more RequestCancellation messages:

FieldTypeDescription
uidServiceCallUIDThe unique transaction ID of the Service Call which should be cancelled.

There is no direct acknowledgement of a cancellation being accepted. The server will send a ServiceAck message to indicate that the request was received, but is not able to confirm whether the robot actually intends to honour the cancellation: some Service Calls may not be able to be interrupted.

To determine the effect of an attempted cancellation, you should monitor the response messages from the robot: a successful cancellation will yield a ServiceResult message with status CANCELLED.

Service Discovery

Discovery of available services can be achieved by first calling a service to list those plugins on a robot which have services available: svc:[subsystem].[callsign].[project]/rocos/getCallables

This service takes an empty request, and returns a single JSON response which contains a list of the components on the agent which have callable Services available (which should basically be all of them, since new versions of the agent also use Services for discovery of Telemetry Message Topics).

Subsequently, you can then iterate over this list of components, and call a service on each component to obtain the schema for the Services exposed by that component:

svc:[subsystem].[callsign].[project]/[component]/getServices

The getServices service takes a request which defines a specific query to perform; the following query takes a wildcard parameter to return all available services:

{
"query": {
"type": 0,
"data": {
"path": "*"
}
}
}

The response from the getServices service will contain the JSON schema for the request and response to each of the available services exposed by that component.