Skip to content

ARServer API

Zdeněk Materna edited this page Nov 3, 2022 · 3 revisions

Communication between ARServer and UI (e.g., AREditor) happens over Websocket, where the protocol is briefly described here - it is based on events and RPCs, which are defined in the form of dataclasses and serialized into JSON. From dataclasses, it is then possible to generate an OpenAPI definition and generate the respective, e.g., C# representation (AREditor has a script for that). The OpenAPI for all models used by ARServer can be generated by running arserver.py --openapi. Or, if the interface is written in Python, it can import necessary dataclasses from the arcor2_arserver_data package. There is also a very simple Python client for ARServer.

RPCs and events patterns

  • If the RPC request gets accepted, the server sends a response with result=True and without any message. Otherwise, there is result=False and exactly one error message.
  • In case of events, it is necessary to look at their type (change_type), which might be: ADD (e.g., a new action point was added to the project), UPDATE (something was changed), UPDATE_BASE (e.g., the project was renamed, but it child objects as action points were not changed, the server sends only BareProject), REMOVE (something was removed).
  • Moreover, an event might have parent_id specified, which is the case, for instance, when adding an orientation to an action point.

Connecting to server

When a client connects to the server, it may receive one of the following events:

To check whether the UI is compatible with the server, it should call SystemInfo, which returns ARServer version (version of the arcor2_arsever package) and more importantly its API version (version of the arcor2_arserver_data) package. The response also contains a list of supported RPCs.

Showing something

If the UI wants, for instance, just to display action points, it has to:

  • Wait for the OpenProject event that contains the project definition (which might be empty or might already contain some data).
  • Wait for any subsequent ActionPointChanged events and to update its internal knowledge about the project accordingly.

...the full project is sent only when UI connects to the server, and then, the UI has to handle changes based on events.

Modifying something

In order to modify, for instance, a project, an interface has to call RegisterUser, otherwise, the user won't be able to acquire a lock for the project or its elements. The user will be unregistered automatically once the connection is closed. In general, to be able to modify something that has an ID (e.g., action point), it has to be locked first by calling WriteLock. Then, in some cases, the lock is released automatically, and in others, it has to be released by the UI. Please also see the related README. When an object is locked or unlocked, all UIs receive ObjectsLocked or ObjectsUnlocked event. Locks are held for a certain time (5 minutes by default) even if the user is not registered - so if, for instance, the UI crashes and the user reconnects within the given time, locks will still be there. On the other hand, if the UI exits normally, it should release all user's locks.

If any UI/user makes a change to any element of the scene or project (using an RPC), all connected UIs will receive a notification. Take an action point as an example (it will be created, modified and the project will be saved, code is slightly simplified):

  • The UI sends AddActionPointRequest(name='ap', position=Position()) and receives AddActionPointResponse(result=True) (no need to lock anything, user just has to log in).
  • All UIs receive the event: ActionPointChanged(type=ADD, data=BareActionPoint(id='uuid', name='ap', ...)).
  • In order to modify AP, UI has to lock it (and all its child objects) using WriteLockRequest(object_id='uuid', lock_tree=True), which will be confirmed by an empty WriteLockResponse.
  • Aftewards, all UIs will receive ObjectsLocked(data=[LockData(object_id='uuid', owner='user1')]).
  • The UI call UpdateActionPointPositionRequest(action_point_id='uuid', new_position=Position()) and receives UpdateActionPointPositionResponse().
  • All UIs receive the event: ActionPointChanged(type=UPDATE_BASE, data=BareActionPoint(id='uuid', name='ap', position=Position())).
  • ...the position (or whatever else on the locked AP and/or its children) can be updated multiple times.
  • Once done, the AP should be unlocked with WriteUnlockRequest, and again, all UIs will be notified.

Working with objects

  • UI should use GetObjectTypes to get information about objects.
  • For each Object Type, it is possible to get the list of its actions with GetActions.
  • For getting info about robots, there is GetRobotMeta.
  • To get robot joints or pose, there are GetEndEffectorPose and GetRobotJoints. Those should be used, when the information is needed just once. To get continuous updates, UI might call RegisterForRobotEvent.
Clone this wiki locally