-
Notifications
You must be signed in to change notification settings - Fork 15
ARServer API
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.
- If the RPC request gets accepted, the server sends a response with
result=True
and without any message. Otherwise, there isresult=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 onlyBareProject
),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.
When a client connects to the server, it may receive one of the following events:
- ShowMainScreen - when no scene or project is opened, and no package is running.
- OpenScene followed by SceneState - when a scene is opened.
- OpenProject followed by SceneState - when a project is opened.
- PackageState followed by PackageInfo - when a package is opened.
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.
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.
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 receivesAddActionPointResponse(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 emptyWriteLockResponse
. - 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 receivesUpdateActionPointPositionResponse()
. - 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.
- 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
andGetRobotJoints
. Those should be used, when the information is needed just once. To get continuous updates, UI might callRegisterForRobotEvent
.