Hardware Control Core Source

Overview

Cross Platform Hardware Communication Code.

Core Components

ControlSystem

The primary class that manages an entire hardware control system.

  • Managing device controllers and devices
  • Initializing and uninitializing the system
  • Handles command execution and synchronization
  • Provids logging and debugging capabilities

DeviceController

Base class for hardware controllers:

  • Manages one or more devices
  • Implements device communication
  • Provides device connection/disconnection management

DeviceInfo

Base class for device descriptions that:

  • Stores device-specific information (devices connected to a controller)
  • Handles device state tracking

CommandPackage and CommandStatus

The command execution system consists of:

  • DeviceCommandPackage: Defines command parameters and behavior
  • CommandStatus: Tracks command execution state and progress

Development Concepts

Control Systems, Controllers, and Devices

The API has a primary class called ControlSystem. Typically 1 of these needs to be instantiated.

Controllers can be added to the ControlSystem. These objects control 1 or more devices and sometimes represent a hub or stand alone physical controller.

Devices can be added to the Controllers. Controllers Expect 1 or more (depending on the nature of the controller) devices that the controller is responsible for managing.

After adding Controllers and Devices, calling ControlSystem::initializeDevices() will connect (USB connection or similar) to all connected controllers and devices.

After this initialization is finished, Commands can be added to devices for runtime one-way or bi-directional control.

CommandPackage, and CommandStatus

A Command is created by creating a CommandPackage and adding it to a device, this will create a CommandStatus object that can be used to control the commands execution progress and monitor it's status:

//create a CommandPackage
auto cp1 = std::make_shared<DeviceCommandPackage>();

//create a CommandStatus object after adding the CommandPackage to the device.
auto cs = myDevice->addCommand(cp1.get());

A command will be created with the parameters given in the passed DeviceCommandPackage (cp1). The CommandStatus object (cs) can then be used to start/stop/update the command on the device:

//run the command until it is finished (blocking execution)
cs->runFull();

//start a command to run asynchronously ()
cs->runAsync();

//wait until the above command is finished.
cs->waitFinished();

A command can also be manual stepped during program execution by calling cs->update() methods.

while commands are running - parameters from the CommandPackage are directly used and accessed. (such as target positions for actuators etc.)

These parameters can be changed while a command is running - however it is not strictly thread safe - meaning a command could be making calculations on changing parameters before a final communication result is passed to the hardware. Thread-safe alterations to parameters can be made by utilizing the UpdatePackage facility:

//start an async command
cs->runASync();

//prepare an UpdatePackage which will be a deep copy of the original cp1 CommandPackage.
auto updatePackage = dynamic_cast<MovementCommandPackage*>(cp1->prepareUpdatePackage());

//update parameters
updatePackage->disp_target -= 1;

//mark the UpdatePackage as ready.  This will signal that the cp1 parameters be updated when the Command is ready to safely read the parameters before its next internal update.
cp1->markUpdatePackageReady();

//wait until command is finished at program end.
cs->waitFinished();

Feedback state information can be read from a command by using the CommandStatus::stateInfo() function, this will return a CommandStateInfo object containing the any relevent state information from the device. For example the current position and velocity of an actuator:

//start an async command
cs->runASync();

while (!cs->isFinished()) {

    //print the current position
    auto state = dynamic_cast<MovementStateInfo*>(cs->stateInfo());
    printf("position: %f, velocity: %f", state.currentPos, state.currentVel);
}

This can be used in tandum with Command UpdatePackages to achieve bi-directional control loops with a device.

Memory Management

ControlSystems, Controllers, Devices, Command Packages are created in user-side code. Management of the memory is left to the user-side code. The Library does not own and will not delete these objects.

ControlStatus Objects are created/owned by the library and released by the library.

ControlSystems must be un-initilized before user-side managed Controllers, Devices, and Command Packages are released by using the ControlSystem::uninitialize() command which will properly free ControlStatus objects and un-bind other objects tracked by the controlsystem and clean up any outstanding connections etc. It is important this is called before objects can be freed on user side code.