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 over USB. 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 implement 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.
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
As the device does not respond to subscription requests, a return of true will simply indicate that the write to the buffer of the device did not fail
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
Will need to periodically poll the device to determine if a change in connection has occurred
If a disconnect occurs without having been requested, attempt to reconnect to the device
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 |