SGKEMBEDDED PERFORMANCEEpicTunerSDK · PrereleaseContact ↗
EpicTuner SDK/ ET UI service v1 (SDK 0.4 / M2)

Download the complete Markdown manual ↓
One file with guides, API headers and examples for developers and coding agents.

ET UI service v1 (SDK 0.4 / M2)

<epictuner/ui.h> is the optional, Qt-free C service. Declare ui and et.ui.v1 in the development manifest, query ET_SERVICE_UI with ET_SERVICE_VERSION_1, and install one dispatcher listener. ABI 1.1 and its 1.0 prefixes are unchanged. <epictuner/sdk/ui.hpp> adds owning PanelBuilder storage and the Ui convenience client. The shipped hello-panels example is compilable author code, with no private host includes or QML.

Build, install for development, and open

cmake -S examples/hello-panels -B hello-out -G Ninja -DCMAKE_PREFIX_PATH=/absolute/sdk
cmake --build hello-out
epictuner --plugin-development --plugin-manifest /absolute/hello-out/plugin.json

Keep the generated plugin.json beside hello-panels.dll (Windows x64) or hello-panels.so (Linux x64/ARM64). For a multi-config generator, copy the manifest beside the library in Release. Installing this M2 development example means copying this directory and loading its manifest. Persistent unsigned enablement, signed package installation/update and licenses are M5.

Open Plugins → Examples/Hello panels. The example provides editable fields, a two-way name binding, conditional controls, selectable tabs, results and a chart, a control gallery, two independent inspectors, and a parent-modal dialog. Closing/reopening restores opted-in values, selected tabs, splitter ratio, focus, placement and geometry. Restart only restores compatible visible non-modal panels; it never repeats a previous button callback. Fault commands exist only when built with the repository test fixture's ET_HELLO_TEST_FAULTS definition.

Calls, results, and lifetime

All service calls require the worker dispatcher. Background jobs post their results to that dispatcher before updating UI. Define calls copy the panel, nodes, property spans and strings before returning; no author pointer crosses IPC. A definition is immutable and unique for the worker session. The panel ID must start with the plugin ID plus .; node IDs are unique within a definition. Parents may appear before or after children, but cycles and depth >32 are rejected.

ET_OK from a call means enqueued, not host acceptance. Pass et_async_options to receive acceptance/rejection through et_completion. The C++ Ui wrapper catches listener exceptions inside the plugin compiler and returns ET_ERROR_CALLBACK; the worker then fails visibly. C listeners return an et_result. Completion callbacks must be noexcept and must never throw across the C ABI (especially across compilers); the example logs rejections explicitly. Completion context and callback code must remain alive until completion or stop. Stop cancels pending completions before the plugin's stop callback. An operation that misses its deadline terminates the worker; it is never retried automatically.

Operation Host result
define_panel validates the complete definition before registering menu/navigation
open handle in completion.value (ET_VALUE_HANDLE); OPENED event follows
patch atomic property batch; accepted/current revision in completion.value (ET_VALUE_U64)
action(FOCUS/CLOSE) focuses the existing instance or begins asynchronous close
action(ALLOW_CLOSE/DENY_CLOSE) answers the matching CLOSE_REQUEST.origin; stale replies fail

Patch expected_revision must equal the latest event or patch-completion revision. Rejection leaves the complete model unchanged. Conflicts require using the newer revision and recomputing the patch; do not blindly retry stale changes. Node IDs remain stable and accepted patches update existing controls in place. In host 0.8.3+, multiple synchronous patch calls from one UI event callback, using that event's panel/revision/origin, are queued in call order. Each accepted reply provides the next call's revision; a failure cancels its dependent calls. New user edits still conflict. Status patches can safely precede options patches. Host 0.8.4 also supports the default origin 0 for these same-event calls. Prefer one batch for dependent controls. Timer/completion callbacks must use a fresh revision; see the migration guide. Layout topology is immutable for an instance; use visible conditions or a separate panel definition instead of injecting arbitrary UI source.

Handles include session identity, project generation, slot and generation. Closing, project replacement, disable and worker replacement invalidate the corresponding handles. Reopening creates a new handle. Events contain the handle, definition, instance, node, scalar value, revision and monotonic per-instance origin. Input previews do not change stored values or revisions. Commit/selection validates before publishing, increments the revision and sends the accepted value. Programmatic patches/binding propagation never generate user commits, avoiding feedback loops. Invalid input leaves the last valid value and an inline error; Enter commits, Escape restores, and normal text clipboard/Tab behavior comes from ET controls.

Controls and property contract

Property IDs and event types are separate domains. ET_UI_EVENT_SELECTED (legacy ET_UI_SELECTED) is an event, never a property. Set a select's index with ET_UI_SELECTED_INDEX / ET_UI_VALUE or the C++ selectedIndex() helpers. See the pre.2 migration example for dynamic options.

The C header lists the exact constants. UI properties accept finite F64, BOOL or bounded UTF-8 STRING values as appropriate. Other tagged variants are rejected. Text is plain text; QML, HTML, JavaScript and arbitrary expressions are never evaluated. Numeric fields use a locale-independent decimal point in this version.

Node kinds Rendering and value
column, row, grid, section, form ET spacing; form fields align labels and units; grid has 1–12 columns
tabs, splitter, scroll selected tab index; horizontal two-child splitter ratio 0.1–0.9; bounded nested scrolling
label, banner, separator plain text and themed feedback
button ordered click event on the worker dispatcher
text, number string or finite value with minimum/maximum/decimals; read-only support
checkbox, switch boolean value, keyboard/mouse input and ET theme
select newline-delimited options and zero-based selected index
slider, progress constrained number; progress is 0–1; combine buttons with jobs for Cancel
image package PNG/JPG beside the manifest or under assets/, at most 2 MiB; host constrains decoded display size
list, table, tree bounded data, virtualized rows, filter, selection; table sorts by column; tree expands/collapses
chart at most 512 finite samples, automatically scaled and theme-aware

Common properties: title, value, enabled, visible, tooltip, error, readOnly, help, min/preferred/max width and height, fill and stretch. help permits HTTPS only, opened by an explicit user click. Dimensions are logical pixels scaled by the current ET scale. Chart/data strings remain subject to the 4096-byte string bound. Table options supplies newline-delimited column headings; data uses newlines for rows and tabs for cells. Tree data uses leading tabs for indentation. All selection events report the original data row, independent of sorting/filtering. These are plugin-owned display datasets; they grant no calibration or ECU access.

Event value types

The following are input events; programmatic patches do not emit them. All control kinds are covered below. Lifecycle events (OPENED, CLOSED, CLOSE_REQUEST, FOCUSED, RESIZED) carry ET_VALUE_NULL; panel/instance and revision/origin identify their context.

Control Input event et_ui_event.value
button ET_UI_CLICK ET_VALUE_NULL
text ET_UI_PREVIEW, ET_UI_COMMIT ET_VALUE_STRING
number, slider ET_UI_PREVIEW, ET_UI_COMMIT ET_VALUE_F64
checkbox, switch ET_UI_COMMIT ET_VALUE_BOOL
select, tabs ET_UI_EVENT_SELECTED ET_VALUE_F64, zero-based option/tab index
list, table, tree ET_UI_EVENT_SELECTED ET_VALUE_F64, original data-row index before filtering/sorting
splitter ET_UI_COMMIT ET_VALUE_F64, ratio 0.1–0.9
plugin-owned calibration table, curve, surface ET_UI_COMMIT ET_VALUE_STRING, tab/newline numeric dataset
channel picker, analysis panel ET_UI_COMMIT ET_VALUE_STRING, chosen ID/text
field editor, host-backed calibration table/curve/surface host tuning operations Use tune/read-service events rather than generic UI input
column, row, grid, section, form, scroll, label, banner, separator, progress, image, chart, live panel, datalog panel none No generic value-input event

Property types and control use

The host validates property types on the complete resulting model. A property irrelevant to a control may be stored but has no rendered effect; it does not change that control's value type. All properties in a patch must validate.

Properties Type Control use
title, tooltip, error, help STRING Common text; help must be HTTPS
enabled, visible, readOnly, persist, twoWay, fill BOOL Common state/layout; persist opts a value into storage
bindValue, bindEnabled, bindVisible STRING Same-panel node IDs; value bindings must have compatible types
min/preferred/max width and height, stretch F64 Common layout limits; stretch is integral
minimum, maximum, step, decimals F64 Numeric controls and plugin datasets; decimals is integral 0–8
units STRING Numeric/dataset label; never a selected index
options STRING Select options or table headings, newline delimited; empty select = no options
value / selected index See event table Corresponding control's typed value; progress F64 0–1, select allows -1 for none
columns F64 Grid/dataset column count, integral 1–12
data STRING List/table/tree/chart or initial plugin dataset
source STRING Image: bounded adjacent PNG/JPG
target STRING Prefab host ID; empty target selects a plugin-owned dataset

For empty select options, value=-1 is recommended; legacy default 0 is also accepted, but no item is rendered and no user selection is emitted. Replace options and index together to avoid retaining an out-of-range old selection. Rejection details identify the node and property, appear in plugin diagnostics, and are delivered as the completion's code and borrowed detail string.

bindValue, bindEnabled and bindVisible name another node in the same instance. Conditions require boolean values. Value types and destination constraints are checked, cycles are rejected, and one-way binding is the default. Set twoWay for editable aliases; source read-only/disabled state cannot be bypassed. Channel and tune sources use the read services and calibration prefabs; local UI-model bindings do not confer access.

Panels, persistence and failure

Explicit docked and popout panels

Use a placement mask to say where a definition is allowed to open. A floating panel uses the same ET controls and prefabs as a workspace panel; it needs no custom renderer or native window library.

PanelBuilder docked("com.example.tool.main", "Calibration workspace");
docked.definition.placements = ET_PLACEMENT_WORKSPACE;
docked.add(ET_UI_COLUMN, "root");
docked.prefab(ET_UI_CALIBRATION_TABLE, "table", "fuelTable", "root");
ui.define(docked, &completion);

PanelBuilder popout("com.example.tool.popout", "Calibration popout");
popout.definition.placements = ET_PLACEMENT_FLOATING;
popout.add(ET_UI_COLUMN, "root");
popout.prefab(ET_UI_CALIBRATION_TABLE, "table", "fuelTable", "root");
ui.define(popout, &completion);

// After both definitions complete successfully, open on explicit user actions:
ui.open("com.example.tool.main", {}, ET_PLACEMENT_WORKSPACE);
ui.open("com.example.tool.popout", {}, ET_PLACEMENT_FLOATING);

Declare both definitions and their placements in the signed manifest. This example uses two singleton definitions, so each can be open and focused independently. Route events by event.definition and keep asynchronous callback/model state per view. Project-backed prefabs share calibration data; plugin-owned inputs and UI revisions belong to their view. See the complete calibration workbench.

Alternatively, one definition can allow both placements and multiple instances:

panel.definition.placements = ET_PLACEMENT_WORKSPACE | ET_PLACEMENT_FLOATING;
panel.definition.flags |= ET_PANEL_MULTIPLE;
// After successful definition:
ui.open("com.example.tool.main", "Docked", ET_PLACEMENT_WORKSPACE);
ui.open("com.example.tool.main", "Popout", ET_PLACEMENT_FLOATING);

In that case route state by event.instance and panel handle. Opening the same instance again focuses its existing window; it does not move it to another placement. Close then reopen explicitly to change its placement.

placements is a mask of Workspace, Floating and Modal. Placement zero chooses saved state, then the ET pop-outs preference where permitted. Single-instance panels use an empty instance ID; opening again focuses them. Multiple-instance definitions require explicit IDs and own independent nodes, values and handles. All floating/modal windows are ET-managed. Modality is parent-window modality. The GUI never waits synchronously for a plugin callback. Closing a dirty-check panel sends CLOSE_REQUEST; a missing answer closes it after 3000 ms. Worker failure immediately releases modal windows and disables other controls with a recovery message. Disable closes all windows and drains the worker.

ET_PANEL_PROJECT restricts open/restore to a current project. Closing/replacing that project invalidates its panels. ET_PANEL_PERSIST saves state under the plugin/panel/instance, schema and optional project identity. Individual node values require persist=true; sensitive values should not opt in. Schema changes start with new state. Restart restoration only occurs after explicitly loading an enabled, compatible development plugin. Licensing eligibility will be added at M5. Windows are clamped onto available screens at creation/screen changes, including positions saved on a removed monitor. Placement/geometry, workspace and internal tab selection, splitter values and last input focus are retained.

Per worker: 32 definitions, 64 live instances, 4096 total definition nodes and 4096 live nodes, depth 32, 32 pending requests, 30 UI calls/second. Persistence retains at most 64 identities per plugin. A serialized message must fit 64 KiB (including envelope); large definitions must use separate panels. Batch display patches at or below 30 Hz; Qt coalesces painting and input previews are debounced. Overflow returns ET_ERROR_LIMIT; it does not silently truncate or apply part of a batch. Revisions/origins/slots never wrap into reusable identities.

Acceptance boundaries

Windows x64 native Qt Quick UI is the M2 acceptance target on this workstation. Compiler/OS-specific results are recorded in the repository's M2 audit. Linux GUI, real second-monitor hot-plug, and physical Pi display/input certification require their respective environments. M4 supplies calibration prefabs and host edit services; M5 supplies signed installation/licensing; M6 supplies independent custom windows. None are implied by the host-rendered M2 panels.