Introduction
The Windows USB Communications interface will provide a set of interfaces and abstract classes that enable developers to communicate with usb devices, facilitating communication with VictoR devices in the VictoR Core library. The abstract device class in the VictorCommunications library will be used to provide a means to read, write, connect to, and disconnect from usb devices, while refraining from direct references to core VictoR concepts.
Requirements
Constraints
Performance
Similar to other interfaces dealing with communications to the VictoR device, asynchronous Read/Write calls will be made available from the interface..
OS Requirements
The interface will target Windows 10 platforms.
Ease of Use
The process of connecting, disconnecting, reading, and writing to a usb device often requires multiple steps. To help soften the workflow when dealing with this process, the aim for each of the abstract classes is to wrap this behavior in single call functions.
Detailed Design
A VictorUsbDevice will derive from the IVictorDevice interface to define how users can read from and write to specific characteristics. These calls will be handled asynchronously to allow for the continued execution of code while the device performs the requested action and reports back the success of the read or write operation. Subscription functionality is also defined in the VictorUsbDevice class to raise events when a characteristic’s value has changed, as long as the characteristic in question supports notifications. The SerialDevice within the VictorUsbDevice class is what allows for the communication with a VictoR device connected through usb. The SerialDevice will be hidden from the user, only allowing modifications to be made to the device through the available functions within the VictorUsbDevice class. A device manager will contain a dictionary of devices to allow for the management of these devices, and ensuring the user does not need to retain their own collection of devices within their application.
In order to read from the device, a read request must first be sent to the device. A response will then be sent to the input stream by the device that contains the result of that request (either an error message or the data requested). When writing to the device, a result is also sent out to the input stream by the device. Additionally, subscriptions use the same input stream to send notifications on updates to any characteristics that have been subscribed to. Because of the potential for a steady stream of subscription notifications, reads and writes cannot reliably read the next available message after performing an initial write. Instead, a background thread that continuously reads from the input stream will be started on successful SerialDevice creation, parsing the header of the next available message to determine the message type, and feed the payload to the appropriate queue: a queue for notification messages of subscriptions and a queue for non-notification messages. As read/write operations will be limited to one at a time, this second queue can be expected to only contain a single byte array. Reads/writes will be limited through the use of a slim semaphore to prevent simultaneous reads.
In the case of subscriptions, delegate cannot be sent through the usb interface. To enable notifications within the system established by the SDK, a hash map of events tied to attributes will be managed by the VictorUsbDevice, with calls to subscribe being handled through services, passed to the VictorUsbDevice via the VictorUsbDeviceManager.
VictorUsbDeviceManager/VictorUsbDevice
Connect
Given the user has completed a scan for devices
Create a SerialDevice using the desired deviceID
Set the SerialDevice’s baud rate to a value of 921600
Add device to a hashed collection in DeviceManager
Disconnect
Dispose the SerialDevice
GetConnectedDeviceIDs
Return list of IDs from DeviceManager’s connected device hashed collection
ScanForDevices
Create a DeviceWatcher
Use a device selector to filter the devices you want the DeviceWatcher to notify you of
Register event handlers for devices being added, removed, or updated according to the Device Watcher
Register event handler for EnumerationCompleted and Stopped events
Start a scan with the DeviceWatcher
Add any devices found matching the DeviceWatcher criteria to a list of scanned devices
Once the EnumerationCompleted event is raised, Stop the DeviceWatcher’s scan
Subscribe
Retrieve desired device from DeviceManager’s hashed collection of connected devices
Attempt to write a subscribe request to the device
If the device responds with a success, add callback to device manager’s internal dictionary of callbacks
NOTE: It does not seem possible to determine if an attribute can be subscribed to over usb until you attempt to do so, expecting that doing so will give you an error response (though error received is also unknown). Need to talk with Jabil about this.
Unsubscribe
A list or dictionary of supported services from device should be available in DeviceManager
Remove callback from device manager’s internal dictionary
ReadAsync
Wait on the semaphore slim object before attempting to read
Perform a write with the appropriate read request (See WriteAsync below)
Await a valid value from the response queue for a set amount of time
If no value is present after set time, report failure and pass null array
If value is present, but is an error response, report failure and pass null array
If value is present and is a valid response, report success and pass payload
Release the semaphore slim
WriteAsync
Wait on the semaphore slim object before attempting to write
Create a DataWriter object and attach it to the SerialDevice’s output stream
Write the desired bytes with the DataWriter
Use the DataWriter’s StoreAsync method to write to the device’s output stream
Await a response from the device to confirm successful write (see Read above)
Will return false if a WASP_EWRITEFAIL is sent by the device in response
Release the semaphore slim
ConnectionStatusChanged
May need to periodically poll the device to determine if a change in connection has occurred
InputStreamThread
On a periodic basis:
Create a DataReader object and attach it to the SerialDevice’s input stream
Set ByteOrder of DataReader to LittleEndian
Use the DataReader’s LoadAsync method to load bytes from the device’s input stream matching the length of the message header for Victor messages
Will return the number of bytes successfully read from the device
If no value is retrieved after a set amount of time, cancel the read attempt and delay a set amount of time before performing a new write
If the device is no longer valid, then a disconnect has most likely occurred. Trigger connection state change callback
If there are bytes read by the DataReader, use the DataReader’s ReadBytes method to read the bytes loaded from the device’s input stream
If the header reports the message type as a subscription
Read the bytes from the DataReader
Pass the payload to the notifications queue to trigger the appropriate callback
If the header reports a read response as the message type
Read the bytes from the DataReader
Pass the payload to the response data queue
Glossary
Term | Description |
---|---|
User/End-User | A developer utilizing the VictoR SDK libraries for software application development |
OS | Operating System |
SDK | Software Development Kit |
USB | Universal Serial Bus |