Robot Raconteur Client
1 Installation
2 Quick start
3 Connecting
rr-connect
rr-close
rr-object?
rr-object-path
rr-object-type
rr-object-members
rr-member-direction
rr-attributes
rr-service-definitions
4 Values
rr-struct
rr-struct-ref
rr-struct-set!
rr-new-structure
rr-multidim
rr-varvalue
rr-enum-values
rr-pack-element
rr-unpack-element
5 Members
5.1 Properties and functions
rr-get
rr-set!
rr-call
5.2 Object references
rr-objref
5.3 Events and callbacks
rr-add-event-listener!
rr-set-callback!
5.4 Generators
rr-generator?
rr-generator-next
rr-generator-close
rr-generator-abort
rr-generator->list
5.5 Wires
rr-wire-peek-in
rr-wire-peek-out
rr-wire-poke-out!
rr-wire-connect
rr-wire-connection?
rr-wire-in-value
rr-wire-wait-in-value
rr-wire-try-in-value
rr-wire-in-value-valid?
rr-wire-set-in-value-lifespan!
rr-wire-set-out-value!
rr-wire-out-value
rr-wire-try-out-value
rr-wire-add-listener!
rr-wire-close
5.6 Pipes
rr-pipe-connect
rr-pipe-endpoint?
rr-pipe-endpoint-index
rr-pipe-send
rr-pipe-receive
rr-pipe-try-receive
rr-pipe-set-request-ack!
rr-pipe-add-listener!
rr-pipe-add-ack-listener!
rr-pipe-close
6 Locks
rr-request-object-lock
rr-release-object-lock
rr-monitor-enter
rr-monitor-exit
call-with-rr-monitor-lock
7 Errors
exn:  fail:  robotraconteur
rr-error-type
8 TLS
rr-make-node-tls-context
rr-node-tls-context?
rr-make-ssl-context
9 Discovery
rr-node-info
rr-service-info
rr-find-nodes
rr-find-node-by-name
rr-find-node-by-id
rr-find-service-by-type
10 Threads
9.3

Robot Raconteur Client🔗ℹ

John Wason

This is an AI generation experiment. The library, its tests and this documentation were written by Claude (Anthropic’s Claude Opus 5.5, using Claude Code), at the direction of John Wason.

 (require robotraconteur) package: robotraconteur

A pure-Racket client for Robot Raconteur, the communication framework for robotics and automation. It connects to standard Robot Raconteur services over plain TCP, Robot Raconteur TLS, WebSocket and secure WebSocket, reads the service definitions from the service when it connects (no generated code is needed), and can find nodes and services on the local network.

The client speaks Message Version 2. It covers properties, functions, generators, events, callbacks, object references, wires, pipes, object and monitor locks, and remote errors. Memories, subscriptions and the local and intra transports are not implemented.

1 Installation🔗ℹ

  raco pkg install "git+https://github.com/johnwason/robotraconteur_racket?path=robotraconteur"

From a checkout of the repository, raco pkg install --link robotraconteur/ links the package to the working tree.

TLS uses the OpenSSL libraries that Racket’s openssl library loads. Discovery, and connecting to link-local IPv6 addresses without a zone, need Linux.

2 Quick start🔗ℹ

(require robotraconteur)
 
(define obj (rr-connect "rr+tcp://localhost:29200?service=reynard"))
(rr-object-type obj)              ; "experimental.reynard_the_robot.Reynard"
(rr-call obj "say" "Hello from Racket!")
(rr-set! obj "color" (vector 0.8 0.2 0.2))
(rr-get obj "robot_position")
(rr-call obj "teleport" 0.1 -0.2)
(rr-close obj)

Finding a service on the local network instead of naming its address:

(define found (rr-find-service-by-type "experimental.reynard_the_robot.Reynard"))
(define obj (rr-connect (car (rr-service-info-urls (car found)))))

3 Connecting🔗ℹ

procedure

(rr-connect url    
  [#:username username    
  #:credentials credentials    
  #:timeout timeout    
  #:ssl-context ssl-context    
  #:tls-context tls-context]) → rr-object?
  url : string?
  username : (or/c string? #f) = #f
  credentials : (or/c hash? #f) = #f
  timeout : (>/c 0) = 10
  ssl-context : (or/c ssl-client-context? #f) = #f
  tls-context : (or/c rr-node-tls-context? #f) = #f
Connects to the service named in url and returns its root object.

URLs take these forms; nodename= and nodeid= may be added to the query to require a particular node:

  • rr+tcp://host:port/?service=name: plain TCP.

  • rrs+tcp://host:port/?service=name: Robot Raconteur TLS (see TLS).

  • rr+ws://host:port/path?service=name: WebSocket, normally on the same port as rr+tcp.

  • rr+wss://host[:port]/path?service=name: WebSocket over standard TLS, for example behind an HTTPS reverse proxy. The default port is 443.

IPv6 hosts go in brackets. A link-local address may carry its zone, [fe80::1%25eth0]; without one, the address is tried on every local interface with a link-local address at the same time and the first connection is kept, as the C++ core does.

username and credentials are sent for services that require authentication, for example (rr-connect url #:username "user" #:credentials (hash "password" "pass")).

The TCP connect gives up after 5 seconds, and the whole connect (TCP, TLS, the WebSocket upgrade and the Robot Raconteur handshake) after timeout seconds. On timeout everything opened for the attempt is closed and an exn:fail:robotraconteur with name "RobotRaconteur.ConnectionError" is raised. Requests on an open connection time out after 30 seconds.

ssl-context is used for rr+wss; see rr-make-ssl-context. tls-context is used for rrs+tcp; see rr-make-node-tls-context.

procedure

(rr-close obj) → void?

  obj : rr-object?
Disconnects the connection that obj belongs to. Other objects from the same connection become unusable.

procedure

(rr-object? v) → boolean?

  v : any/c
Recognizes service objects: the root object returned by rr-connect and objects returned by rr-objref.

procedure

(rr-object-path obj) → string?

  obj : rr-object?
The object’s service path, such as "reynard" or "robot.tool[0]".

procedure

(rr-object-type obj) → string?

  obj : rr-object?
The fully qualified object type, such as "experimental.reynard_the_robot.Reynard".

procedure

(rr-object-members obj) → (listof (cons/c symbol? string?))

  obj : rr-object?
The object’s members, including those of the object types it implements, as pairs of kind and name. Kinds are 'property, 'function, 'event, 'objref, 'pipe, 'callback, 'wire and 'memory.

procedure

(rr-member-direction obj name)

 → (or/c 'both 'readonly 'writeonly)
  obj : rr-object?
  name : string?
The direction of a member, from its readonly or writeonly modifier.

procedure

(rr-attributes obj) → hash?

  obj : rr-object?
The service attributes the service sent when connecting, a hash from strings to values.

procedure

(rr-service-definitions obj) → (hash/c string? string?)

  obj : rr-object?
The service definitions received so far, as a hash from service definition name to its source text.

4 Values🔗ℹ

Values are converted to and from Robot Raconteur types using the service definitions:

Robot Raconteur

  

Racket

numbers, bool

  

numbers, booleans; cdouble and csingle are complex numbers

numeric arrays

  

vectors (lists are accepted when sending)

T[*]

  

rr-multidim

string

  

string

struct, pod, namedarray

  

rr-struct

T{int32}, T{string}

  

hash

T{list}

  

list

enum

  

integer; a symbol naming the value is accepted when sending

varvalue

  

any of the above, or rr-varvalue to fix the type

null

  

'null

Numbers inside a received varvalue are always vectors, because the wire does not distinguish a scalar from a one-element array.

struct

(struct rr-struct (type fields))

  type : string?
  fields : hash?
A structure, pod or namedarray value. fields is a mutable hash from field names to values. Use rr-new-structure to make one with every field set to its default.

procedure

(rr-struct-ref s field) → any/c

  s : rr-struct?
  field : string?
Gets a field.

procedure

(rr-struct-set! s field v) → void?

  s : rr-struct?
  field : string?
  v : any/c
Sets a field.

procedure

(rr-new-structure obj type) → rr-struct?

  obj : rr-object?
  type : string?
A new structure, pod or namedarray of type, qualified or relative to obj’s service definition, with default field values.

struct

(struct rr-multidim (dims array))

  dims : (listof exact-nonnegative-integer?)
  array : vector?
A multidimensional array. array holds the elements in column-major order, as Robot Raconteur and numpy’s order="F" do.

struct

(struct rr-varvalue (value type))

  value : any/c
  type : string?
A value with an explicit type for a varvalue field or parameter, for example (rr-varvalue (vector 1 2) "int16[]").

procedure

(rr-enum-values obj type)

 → (listof (cons/c string? exact-integer?))
  obj : rr-object?
  type : string?
The names and values of an enum type.

procedure

(rr-pack-element obj type v [name]) → any/c

  obj : rr-object?
  type : string?
  v : any/c
  name : string? = "value"
Converts v to a message element of type the way the client does on the wire.

procedure

(rr-unpack-element obj type element) → any/c

  obj : rr-object?
  type : string?
  element : any/c
Converts a message element back to a value; the inverse of rr-pack-element.

5 Members🔗ℹ

5.1 Properties and functions🔗ℹ

procedure

(rr-get obj name) → any/c

  obj : rr-object?
  name : string?
Reads a property.

procedure

(rr-set! obj name v) → void?

  obj : rr-object?
  name : string?
  v : any/c
Writes a property.

procedure

(rr-call obj name arg ...) → any/c

  obj : rr-object?
  name : string?
  arg : any/c
Calls a function and returns its result, or (void) for a void function. A function returning a generator, or taking a generator parameter, returns an rr-generator? value.

5.2 Object references🔗ℹ

procedure

(rr-objref obj name [index]) → rr-object?

  obj : rr-object?
  name : string?
  index : (or/c #f exact-integer? string?) = #f
Gets the object behind an objref member, indexed for objref T[] and objref T{string} members.

5.3 Events and callbacks🔗ℹ

procedure

(rr-add-event-listener! obj name proc) → void?

  obj : rr-object?
  name : string?
  proc : procedure?
Calls proc with the event’s arguments each time the service fires the event. See Threads.

procedure

(rr-set-callback! obj name proc) → void?

  obj : rr-object?
  name : string?
  proc : procedure?
Sets the procedure the service calls for a callback member. Its result is returned to the service; if it raises, the service receives the error.

5.4 Generators🔗ℹ

procedure

(rr-generator? v) → boolean?

  v : any/c
Recognizes generators returned by rr-call.

procedure

(rr-generator-next g [param]) → any/c

  g : rr-generator?
  param : any/c = 'none
Returns the next value, or eof once the generator is finished. param is sent to generators that take a parameter.

procedure

(rr-generator-close g) → void?

  g : rr-generator?
Finishes the generator normally; rr-generator-next then returns eof.

procedure

(rr-generator-abort g) → void?

  g : rr-generator?
Aborts the operation. The service reports the abort on the next rr-generator-next, usually as an OperationAborted error.

procedure

(rr-generator->list g) → list?

  g : rr-generator?
Collects the remaining values.

5.5 Wires🔗ℹ

Timestamps are pairs of seconds and nanoseconds.

procedure

(rr-wire-peek-in obj name) → 
any/c pair?
  obj : rr-object?
  name : string?
Reads the wire’s current in-value and its timestamp without connecting.

procedure

(rr-wire-peek-out obj name) → 
any/c pair?
  obj : rr-object?
  name : string?
Reads the wire’s current out-value and its timestamp without connecting.

procedure

(rr-wire-poke-out! obj name v) → void?

  obj : rr-object?
  name : string?
  v : any/c
Sets the wire’s out-value without connecting.

procedure

(rr-wire-connect obj name) → rr-wire-connection?

  obj : rr-object?
  name : string?
Connects to a wire. Values from the service arrive as the in-value; values set with rr-wire-set-out-value! go to the service.

procedure

(rr-wire-connection? v) → boolean?

  v : any/c
Recognizes wire connections.

procedure

(rr-wire-in-value w) → any/c

  w : rr-wire-connection?
The latest in-value. Raises ValueNotSet if none has arrived or it has expired (see rr-wire-set-in-value-lifespan!).

procedure

(rr-wire-wait-in-value w [#:timeout timeout]) → any/c

  w : rr-wire-connection?
  timeout : real? = 5
Like rr-wire-in-value, but waits up to timeout seconds for a value.

procedure

(rr-wire-try-in-value w) → 
boolean? any/c any/c
  w : rr-wire-connection?
Returns whether there is a current in-value, the value and its timestamp.

Whether there is a current in-value.

procedure

(rr-wire-set-in-value-lifespan! w seconds) → void?

  w : rr-wire-connection?
  seconds : (or/c real? #f)
How long an in-value stays current after it arrives; #f for ever.

procedure

(rr-wire-set-out-value! w v) → void?

  w : rr-wire-connection?
  v : any/c
Sends a new out-value.

procedure

(rr-wire-out-value w) → any/c

  w : rr-wire-connection?
The last out-value sent. Raises ValueNotSet if none has been sent.

procedure

(rr-wire-try-out-value w) → 
boolean? any/c any/c
  w : rr-wire-connection?
Returns whether an out-value has been sent, the value and its timestamp.

procedure

(rr-wire-add-listener! w proc) → void?

  w : rr-wire-connection?
  proc : (any/c pair? . -> . any)
Calls proc with each new in-value and its timestamp. Out-of-order packets are dropped. See Threads.

procedure

(rr-wire-close w) → void?

  w : rr-wire-connection?
Closes the wire connection.

5.6 Pipes🔗ℹ

procedure

(rr-pipe-connect obj name [index]) → rr-pipe-endpoint?

  obj : rr-object?
  name : string?
  index : exact-integer? = -1
Connects a pipe endpoint. With the default index the service picks the endpoint index. Packets that arrive before the connection completes are kept.

procedure

(rr-pipe-endpoint? v) → boolean?

  v : any/c
Recognizes pipe endpoints.

The endpoint index.

procedure

(rr-pipe-send p v) → exact-nonnegative-integer?

  p : rr-pipe-endpoint?
  v : any/c
Sends a packet and returns its packet number.

procedure

(rr-pipe-receive p [#:timeout timeout]) → any/c

  p : rr-pipe-endpoint?
  timeout : real? = 5
Waits up to timeout seconds for the next packet. Returns eof when the pipe is closed and raises OperationTimeout on timeout.

procedure

(rr-pipe-try-receive p [default]) → any/c

  p : rr-pipe-endpoint?
  default : any/c = #f
The next packet if one is waiting, otherwise default.

procedure

(rr-pipe-set-request-ack! p on?) → void?

  p : rr-pipe-endpoint?
  on? : boolean?
Whether packets sent from now on ask the service for an acknowledgement.

procedure

(rr-pipe-add-listener! p proc) → void?

  p : rr-pipe-endpoint?
  proc : (rr-pipe-endpoint? . -> . any)
Calls proc with the endpoint each time a packet arrives; the packet is still queued for rr-pipe-receive. See Threads.

procedure

(rr-pipe-add-ack-listener! p proc) → void?

  p : rr-pipe-endpoint?
  proc : (rr-pipe-endpoint? exact-nonnegative-integer? . -> . any)
Calls proc with the endpoint and packet number when the service acknowledges a packet.

procedure

(rr-pipe-close p) → void?

  p : rr-pipe-endpoint?
Closes the endpoint.

6 Locks🔗ℹ

procedure

(rr-request-object-lock obj [kind]) → string?

  obj : rr-object?
  kind : (or/c 'user 'client) = 'user
Locks obj and the objects below it: for every session of the authenticated user ('user) or for this connection only ('client). Returns "OK".

procedure

(rr-release-object-lock obj) → string?

  obj : rr-object?
Releases an object lock.

procedure

(rr-monitor-enter obj [timeout]) → void?

  obj : rr-object?
  timeout : exact-integer? = -1
Waits for the object’s monitor lock; timeout is in milliseconds, -1 to wait for ever.

procedure

(rr-monitor-exit obj) → void?

  obj : rr-object?
Releases the monitor lock.

procedure

(call-with-rr-monitor-lock obj    
  thunk    
  [#:timeout timeout]) → any
  obj : rr-object?
  thunk : (-> any)
  timeout : exact-integer? = -1
Calls thunk while holding the monitor lock, releasing it however thunk returns.

7 Errors🔗ℹ

struct

(struct exn:fail:robotraconteur exn:fail (error-code
    error-name
    error-message
    error-subname
    error-param))
  error-code : exact-integer?
  error-name : string?
  error-message : string?
  error-subname : string?
  error-param : any/c
Raised for errors reported by the service and for client-side Robot Raconteur errors such as timeouts. error-name is a Robot Raconteur error name such as "RobotRaconteur.InvalidOperation", or the name of an exception defined by the service. error-subname is "" and error-param is 'null when the service sent none.

procedure

(rr-error-type e) → symbol?

  e : exn:fail:robotraconteur?
The error code as a symbol, such as 'InvalidOperation, 'ObjectLockedError or 'RemoteError (for exceptions defined by a service).

8 TLS🔗ℹ

procedure

(rr-make-node-tls-context [#:extra-roots extra-roots 
  #:default-roots? default-roots?]) 
 → rr-node-tls-context?
  extra-roots : (listof path-string?) = '()
  default-roots? : boolean? = #t
A context for rrs+tcp. The client asks the server to start TLS, then checks its certificate with Robot Raconteur’s rules, like the C++ core:

  • the chain leads to a trusted root: by default the two Robot Raconteur node root certificates (2015 and 2020), which are built in;

  • each certificate carries the critical Robot Raconteur extension for its place in the chain, and no other unknown critical extension;

  • the node certificate is for CN=Robot Raconteur Node {nodeid} of the node that answered.

extra-roots are PEM files of further roots to trust; default-roots? #f leaves out the built-in ones. Client certificates are not supported.

procedure

(rr-node-tls-context? v) → boolean?

  v : any/c
Recognizes contexts from rr-make-node-tls-context.

procedure

(rr-make-ssl-context [#:trust trust 
  #:system-trust? system-trust?]) 
 → ssl-client-context?
  trust : (listof path-string?) = '()
  system-trust? : boolean? = #t
A client context for rr+wss that checks the certificate chain and the hostname, trusting the system’s certificate store (unless system-trust? is #f) and the PEM files in trust. Without #:ssl-context, rr-connect uses (ssl-secure-client-context).

9 Discovery🔗ℹ

Robot Raconteur nodes announce themselves with IPv6 link-local multicast (group FF02::BA86, UDP port 48653). These procedures send discovery requests on every interface with a link-local address, listen for timeout seconds, and return what answered. Discovery needs Linux.

struct

(struct rr-node-info (nodeid nodename urls))

  nodeid : string?
  nodename : string?
  urls : (listof string?)
A node that answered. nodeid has the form "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}". urls reach the node’s service index, one per interface the node was heard on, each with its zone.

struct

(struct rr-service-info (name
    root-object-type
    implements
    attributes
    nodeid
    nodename
    urls))
  name : string?
  root-object-type : string?
  implements : (listof string?)
  attributes : hash?
  nodeid : string?
  nodename : string?
  urls : (listof string?)
A service found by rr-find-service-by-type; pass one of urls to rr-connect.

procedure

(rr-find-nodes [#:timeout timeout]) → (listof rr-node-info?)

  timeout : real? = 3
Every node that answered.

procedure

(rr-find-node-by-name name 
  [#:timeout timeout]) 
 → (listof rr-node-info?)
  name : string?
  timeout : real? = 3
The nodes named name.

procedure

(rr-find-node-by-id id [#:timeout timeout])

 → (listof rr-node-info?)
  id : string?
  timeout : real? = 3
The node with NodeID id, with or without braces.

procedure

(rr-find-service-by-type type 
  [#:timeout timeout 
  #:transports transports]) 
 → (listof rr-service-info?)
  type : string?
  timeout : real? = 3
  transports : (listof string?) = '("rr+tcp" "rrs+tcp")
Services whose root object type is type or implements it, on every node that answered. Each node’s service index is reached over the first of its URLs, in transports order, that connects.

10 Threads🔗ℹ

Each connection runs a few Racket threads: one reads and dispatches incoming messages, one sends keep-alive messages, and one runs listeners. Any number of threads may use a connection at the same time; requests are matched to their replies.

Event, wire and pipe listeners run one at a time, in arrival order, on the connection’s listener thread. A listener must not block. In particular it must not wait on a plain channel that another listener feeds. Hand slow work to another thread or use an async channel (racket/async-channel). A listener may call the service. Callbacks run in their own threads, so they may block.

All of these are Racket threads, so they share one operating system thread.