Antilatency Device Network Library

Antilatency Device Network Library

A library for interacting with Antilatency devices that use the ADN protocol.
Before you start:

Contents

Library’s Functions

The DeviceNetwork library allows you to work with devices that support the ADN protocol. It creates a hierarchy of connected devices and abstracts the user from the details of their physical connection. The current version of ADN supports devices' connection via USB and using an IP network. Moreover, some devices can be connected via other devices (e.g. by radio protocol).
The library provides a relatively low-level interface for working with ADN devices. The main functions of the library:

How To Use The Library

All libraries used for working with Antilatency devices interact with the DeviceNetwork library.
Typical use case for DeviceNetwork:
  1. load the library;
  2. create an empty device filter IDeviceFilter using createFilter;
  3. add the required USB and IP devices to the filter IDeviceFilter using addUsbDevice and addIpDevice;
  4. create an instance of INetwork using createNetwork with the filled in filter;
  5. monitor UpdateId changes using getUpdateId;
  6. optionally: read properties from devices;
  7. search for devices which support a required Task, and launch this task.
You can analyze the use case using an example for C# or C++.
This demo application prints information about connected Antilatency devices. Every time the UpdateId changes, the app writes information about ADN tree nodes. For a device tree consisting of a Universal Radio Socket with an Alt and a Bracer without an Alt, the application printed:

ILibrary

ILibrary allows the user to see the current version of the library using getVersion, and set the required LogLevel using setLogLevel. LogLevel = Off by default.
Before you get an INetwork instance, create and fill in an instance of IDeviceFilter. The device filter will be used for selecting devices to add to the network. With the filter, you can create an instance of INetwork using ILibrary.createNetwork.
You cannot create an INetwork instance with an empty filter.

IDeviceFilter

IDeviceFilter selects devices to connect to the INetwork instance. The filter uses the addition principle. You should create an empty filter and then add masks of devices to it that should be connected to the ADN instance.
First, use ILibrary.createFilter to create an IDeviceFilter, then use addUsbDevice and addIpDevice methods to add USB and IP devices to the filter. Both types of devices can be added to the same filter.
Example in C#:
/*
 * Get a device filter for Antilatency USB Sockets (HMD Radio Socket and Wired USB Socket).
 */
private static Antilatency.DeviceNetwork.IDeviceFilter GetAntilatencyUsbSocketDevicesFilter(Antilatency.DeviceNetwork.ILibrary deviceNetworkLibrary) {
    var result = deviceNetworkLibrary.createFilter();

    var usbDeviceFilter = new Antilatency.DeviceNetwork.UsbDeviceFilter() {
        vid = UsbVendorId.Antilatency, pid = 0x0000, pidMask = 0xFFFF
    };
    result.addUsbDevice(usbDeviceFilter);
    
    return result;
}

In most cases it is enough to create a filter for all devices using the AllUsbDevices, AllIpDevicesMask and AllIpDevicesIp constants.
Examples in C#:
#region DeviceFilter samples
/*
 * Get a device filter for all Antilatency USB devices.
 */
private static Antilatency.DeviceNetwork.IDeviceFilter GetAllUsbDevicesFilter(Antilatency.DeviceNetwork.ILibrary deviceNetworkLibrary) {
    var result = deviceNetworkLibrary.createFilter();
    result.addUsbDevice(Antilatency.DeviceNetwork.Constants.AllUsbDevices);
    return result;
}

/*
 * Get a device filter for all IP devices.
 */
private static Antilatency.DeviceNetwork.IDeviceFilter GetAllIpDevicesFilter(Antilatency.DeviceNetwork.ILibrary deviceNetworkLibrary) {
    var result = deviceNetworkLibrary.createFilter();
    result.addIpDevice(Antilatency.DeviceNetwork.Constants.AllIpDevicesIp, Antilatency.DeviceNetwork.Constants.AllIpDevicesMask);
    return result;
}

After the INetwork is created, the user can get the IDeviceFilter using the INetwork.getDeviceFilter method and check the used masks and devices with getIpDeviceMask, getIpDevice and getUsbDevice. Once an instance of INetwork is created, the filter can only be read, not modified.
You can use the IDeviceFilter to assign specific Antilatency devices to specific ADN instances running on the same Host. To do this, you need to create as many non-overlapping filters as there are ADN instances running at the same time. You cannot create INetwork objects with overlapping filters.

Masks Of A Device Filter

USB devices are identified using Vendor ID and Product ID (VID&PID). Vendor ID is the manufacturer's identifier (registered in USB IF), Product ID is the identifier of a specific device model. Device Vendor ID Antilatency = 0x3237, this constant can be obtained from UsbVendorId.
Next you need to set pid masks to add one or more devices to the filter. Let’s look at the target pid, mask, and possible device pids. Each ‘1’ in the mask means that this bit in the connected device's pid must be equal to the corresponding bit in the target pid. A ‘0’ in the mask means that the corresponding bit can have any value. Look at the example:
For IP devices, the masking rule applies the same way as for USB devices.
You can change the value of the usb/Pid property of Antilatency devices in AntilatencyService.

INetwork

Using INetwork, you can represent the connection hierarchy of all devices, since you can find a parent for each node. To do this, use getNodes and nodeGetParent.
One more function of INetwork is working with node properties.
More details on working with properties and property cache are described in the ADN article and the IPropertyCotask section.
Besides, INetwork is used when working with device Tasks. INetwork methods are not used directly to start tasks, for this purpose, a mechanism of Cotasks and Cotask Constructors has been developed.

INetwork: Time Synchronization

Starting from Antilatency SDK 4.0.0, ADN library includes support for time synchronization for USB devices. New methods are added to INetwork:
We use an adaptive algorithm that performs repeating time synchronization with an Antilatency device. As a result, the time between the application and devices align with an accuracy of about 100-200 microseconds. The time alignment on radio devices has an accuracy of about 10 microseconds.

NodeStatus

NodeStatus is the state of the node that should be checked before running any Task. The TaskRunning and Idle values indicate if any task is currently running on this node. If the device was disconnected or is unavailable, the nodeGetStatus returns the Invalid value, so no task can be executed on this device. The default NodeStatus of a newly connected device is Idle.

UpdateId

The library has a special UpdateId counter. This counter is incremented every time a device is connected/disconnected and every time a task is started/finished on any node. You can get the current value of the counter using INetwork.getUpdateId. The increment of the counter shows possible changes in the INetwork instance: you should update the list of connected devices using getNodes and check their state by nodeGetStatus.

ICotask and ICotaskConstructor

A Task, a Cotask, and a CotaskConstructor are interconnected SDK tools for managing device functions and tasks. Each task has at least one cotask and one cotaskConstructor of its own.
A Cotask is the link between the Task and the user. A cotask hides the data transfer, the work logic and provides an interface for working with a task. To get a cotask, you must first create an instance of ICotaskConstructor. A cotaskConstructor checks nodes for the task support and starts this task, then returns an ICotask instance.
You can read more about the Cotask and the Cotask Constructor here.
The DeviceNetwork library contains the ICotask and ICotaskConstructor interfaces. Every cotask from other libraries inherits the ICotask.isTaskFinished method, which checks if the corresponding task has completed. All CotaskConstructors inherit methods isSupported (whether the task is supported) and findSupportedNodes (find nodes that support it). The ICotaskConstructor methods overlap the low-level methods of the INetwork: no explicit use of nodeIsTaskSupported and nodeStartTask is required.

IPropertyCotask

IPropertyCotask is started with INetwork.nodeStartPropertyTask. Since this is also a task, you must first complete the device's previous task and then start this one. IPropertyCotask gets property values directly from a device, not from a property cache. The cache is automatically updated.
IPropertyCotask allows you to read, write and delete properties. You can get a property name by index using getPropertyKey. Use it to get the full set of properties on the device.

IPropertyCotask: Asynchronous Methods

In Antilatency SDK 4.2.0, copies of IPropertyCotask methods appear, but with the Deferred ending. These are asynchronous variants of methods having the same name, which do not block the thread calling them. Example: the method for deleting a property is available in the deleteProperty and deletePropertyDeferred options.
The new methods return the IFuture interface or its derivatives, such as IFutureString. This allows you to request to read, write, or delete a property without stopping the thread to complete the operation. So you can later access the IFuture object to check whether the operation has completed, or wait on the IFuture object for it to complete.
All the IFuture interface objects (and its derivatives) have two methods:
  • isDone indicates the operation is complete (even if it was unsuccessful). The method returns false only if the operation has not yet completed;
  • wait – wait until the operation is completed.

Errors And Exceptions