Migration
API replacements and workflow adjustments for upgrading from SDK 1.3.0 to 1.4.0 and from earlier releases.
Migration
Upgrading from 1.3.0 to 1.4.0
The primary required migrations in 1.4.0 are the unified time unit and native ABI. An existing
standalone Glove application does not need to become a composite or call sync_time() unless it
uses an external time source, consumes numeric time fields, or is adopting Exo + Glove hardware.
Replace Core and Glove together and recompile
Update the 1.4.0 Core/Glove headers, libraries, CMake packages, and Conan packages together. Clear the CMake cache, object files, static libraries, and plug-ins, then perform a full rebuild. Mixing 1.3.0 and 1.4.0 headers or native libraries is unsupported, and the old ABI is rejected before SDK construction.
Change public numeric time values to milliseconds
Glove frames, runtime state, capture, calibration, and derived data use milliseconds in 1.4.0.
New output uses timestamp_ms. Update code that reads, stores, subtracts, or forwards a time value
as seconds.
Move external frames from the seconds field to the milliseconds field:
qnbot::ExternalGloveFrame frame;
// ...set thumb, index, middle, ring, and pinky...
frame.timestamp_ms = 1'700'000'000'123.0;The legacy frame.timestamp_sec remains an explicit compatibility input and is converted as
timestamp_ms = timestamp_sec * 1000. The SDK does not infer a unit from the numeric magnitude.
Do not set both std::optional fields; supplying both values is an argument error.
Set an external time reference when needed
Call the public qnbot::Glove::sync_time() entry point when every Glove source needs one external
absolute time reference. Its double argument is absolute Unix milliseconds, not seconds or
nanoseconds:
qnbot::Sdk sdk(config);
auto glove = sdk.glove();
glove.sync_time(reference_time_ms);
glove.start();sync_time() may be called again while running. Subsequent Glove frames without an explicit time
advance from the new reference without resetting subscriptions or lifecycle state. An explicit
timestamp_ms takes precedence. Without synchronization, the SDK uses host Unix milliseconds.
The call changes the Glove domain only and does not modify Exo time.
Adopt an Exo + Glove composite only when needed
Composite support is additive and does not change an existing qnbot::GloveConfig. For
shared-serial hardware, use qnbot::CompositeExoGloveConfig and obtain the Glove and Exo facades
from the same qnbot::Sdk. See Glove + Exo composite solution for the full
configuration and capability boundaries.
Migration checklist
- Replace Core and Glove 1.4.0 together, clear old CMake state and artifacts, and rebuild fully
- Convert second-valued output handling and downstream protocols to milliseconds and
timestamp_ms - If retaining
timestamp_secinput temporarily, ensuretimestamp_msis not set with it - Pass absolute Unix milliseconds to
glove.sync_time()when an external time source is required - Use
qnbot::CompositeExoGloveConfigonly for composite hardware - Re-test capture records, time alignment, reconnect recovery, and shutdown on real hardware
Upgrading from 1.2.x to 1.3.0
This section lists only what must change when upgrading the Glove SDK from 1.2.0 to 1.3.0. SerialConnection, background running, and the meaning of the algorithm package package_id are unchanged, so they are not part of this incompatible change set.
The capture entry points move from Glove to GloveDevice
In 1.2.0 the application built a request object and called the capture entry points on Glove, with the device selection inside the request:
qnbot::CaptureStartRequest request{
package_ids,
operator_id,
std::nullopt,
qnbot::Side::left,
false,
};
auto capture = glove.start_capture(request);In 1.3.0 CaptureStartRequest is gone; select the target device first and then run the capture:
auto device = glove.device(qnbot::Side::left);
auto capture = device.start_capture(
package_ids,
operator_id,
false
);The device can also be selected by name:
auto device = glove.device("my-glove");Device selection is now separate from the capture request: the application picks the GloveDevice explicitly first.
The C++ request structures are removed
qnbot::CaptureStartRequest and qnbot::CaptureStatusRequest are removed in 1.3.0, and the readiness query calls the device interface directly:
// 1.2.0
qnbot::CaptureStatusRequest status_request;
status_request.package_ids = package_ids;
status_request.operator_id = operator_id;
status_request.source = "left";
status_request.side = qnbot::Side::left;
auto readiness = glove.capture_status(status_request);
// 1.3.0
auto device = glove.device(qnbot::Side::left);
auto readiness = device.capture_status(package_ids, operator_id);Anywhere the project, a wrapper layer, or configuration conversion code constructs these two structures, it must call the device interface directly instead.
start_calibration() moves to GloveDevice
1.2.0:
auto calibration = glove.start_calibration(left_session, targets);1.3.0:
auto device = glove.device(qnbot::Side::left);
auto calibration = device.start_calibration(left_session, targets);The capture session and the calibration session are managed by the same GloveDevice:
auto device = glove.device(qnbot::Side::left);
auto capture = device.start_capture(package_ids, operator_id, false);
auto calibration = device.start_calibration(capture, targets);CalibrationInteractionMode is renamed
The 1.2.0 qnbot::CalibrationInteractionMode is renamed to qnbot::CaptureInteractionMode in 1.3.0, with the same values (auto_terminal, external):
qnbot::CaptureInteractionMode::externalProject code, configuration mappings, and custom wrappers that use the old enum name must all be updated.
Capture interaction configuration moves from CalibrationConfig to CaptureConfig
1.3.0 separates capture-stage configuration from calibration-stage configuration: interaction and operator_id belong to the capture stage and have moved from CalibrationConfig to CaptureConfig, leaving only force on CalibrationConfig.
The 1.2.0 form:
config.algorithms.calibration.interaction =
qnbot::CalibrationInteractionMode::external;
config.algorithms.calibration.operator_id = "operator-001";
config.algorithms.calibration.force = false;1.3.0 requires:
config.algorithms.capture.interaction =
qnbot::CaptureInteractionMode::external;
config.algorithms.capture.operator_id = "operator-001";
config.algorithms.calibration.force = false;See CaptureConfig and CalibrationConfig for the field definitions.
C++ projects must recompile
1.3.0 removes the capture and calibration entry points on Glove along with the two request structures, renames the interaction enum, and moves the capture interaction configuration. C++ projects must replace both the 1.3.0 Core/Glove headers and libraries, clear the old CMake build cache, and recompile completely; 1.2.0 and 1.3.0 headers, libraries, and Conan packages must not be mixed.
Migration checklist
- Change
glove.start_capture(request)todevice.start_capture(package_ids, operator_id, force) - Change
glove.capture_status(request)todevice.capture_status(package_ids, operator_id) - Change
glove.start_calibration(...)todevice.start_calibration(...) - Remove
CaptureStartRequest - Remove
CaptureStatusRequest - Change
CalibrationInteractionModetoCaptureInteractionMode - Move
interactionandoperator_idfromalgorithms.calibrationtoalgorithms.capture - Recompile with the 1.3.0 Core/Glove headers and libraries
- Make sure capture and calibration use the same
GloveDevice
Upgrading from 1.1.x to 1.2.0
Minimum migration requirements: replace both the Core and Glove 1.2.0 packages, clear the old
CMake build cache, and recompile completely; do not mix 1.1.x headers, dynamic libraries, object
files, or plug-ins. Applications no longer rely on connect() to establish a connection, and
both SDK construction and start() should be inside exception handling; Skeleton joint angle
arrays or maps should be handled as 25 active joints.
The following lists only the incompatible changes that require modifying existing code or rebuilding when upgrading. For the complete list of new features, fixes, and version changes, see the C++ SDK changelog.
| Incompatible item | Previous usage | Current behavior | Migration action |
|---|---|---|---|
| Core and Glove installation packages | Headers or dynamic libraries could be replaced partially, and old object files reused | The public type layout and the underlying ABI have changed, and mixing different versions is not supported | Replace both Core and Glove, clear the CMake cache, object files, static libraries, and plug-ins, then recompile completely |
| Connection lifecycle | Call connect() first, then start() | connect() is deprecated and kept as a successful no-op; start() is responsible for connecting and starting | Remove the assumption that a successful connect() means the connection is established, and call start() directly |
| Stop lifecycle | Use disconnect() to release the connection | stop() stops the data streams and releases the connection, and disconnect() delegates to stop() | Use stop() uniformly |
| SDK construction | Assume that construction does not access hardware | Construction discovers devices, verifies identity, and resolves targets | Put construction inside exception handling, and create the SDK after the hardware and algorithm packages are ready |
get_device_info() | Re-query the device identity on every call | Returns the verified identity snapshot saved when the SDK was created; the legacy firmware explicit-port fallback has no available identity | Do not rely on this call to refresh the identity, and handle UnavailableFeatureError in deployments that allow unverified devices |
| Capture request fields | Use candidate_ids | The field was changed to package_ids | Rename the field and pass a non-empty package ID list |
| Capture result | Obtain a CaptureSet from Host Capture | Subsequent calibration uses the still-open move-only CaptureSession directly | Keep the completed Session and no longer save a CaptureSet |
| Calibration object | One CalibrationJob per target | One move-only CalibrationSession handles multiple targets in sequence | Switch to start_calibration(session, targets) and read the result vector |
| Capture control | Use calibration_control | Source-level interaction uses capture_control | Read Capture and Calibration progress separately, and send operations back through Capture control |
| Workflow state model | Use the old state and snapshot fields | Use Session, stage progress, structured failure, and multi-target results | Update the UI state mapping and field access |
| Calibration directory | Use CalibrationStoreConfig or calibration.store | The application data directory is controlled uniformly by QNBOT_HOME | Remove the old field and set QNBOT_HOME before constructing the SDK |
| Debug directory | Use DebugConfig.log_dir | Logs are written to <QNBOT_HOME>/logs | Remove the old field and set QNBOT_HOME as needed |
| Debug detail | Use DebugDetail::raw | Use summary or full | Change raw to full; prefer summary for ordinary diagnostics |
| Debug module | Use old module names or string fields | Use the six DebugModule enum values | Update the configuration field types and module names |
| Skeleton joints and calibration | Handle 24 active joints and reuse calibration from older versions | There are now 25, and index 1 adds <side>_thumb_cmc_flexion; older calibration is not reused as a currently compatible record | Update fixed arrays, index mappings, and downstream protocols, and allow recalibration after upgrading |