Engine

Covers Engine 2.0.0

CAESAR Tasks

Create tasks, understand their schedule, order task groups, and inspect failures.

CAESAR runs DARTWIC’s tasks. A task combines an implementation, configuration, start/stop lifecycle and diagnostics. A plugin task might read a device; a DCode task might calculate its next command. Both use the same task scheduler.

For the contract behind these diagnostics—periodic execution, health/response, and TaskGroup composition—read Deterministic Automation. It distinguishes the engine’s observable behavior from device-specific safety decisions.

Create and run a task

For a plugin, choose its registered task type in Managing Tasks, configure its module and arguments, then start it. For DCode, activating a script registers its tasks; starting a task runs its callbacks. Selecting Native C++ changes how supported DCode logic executes, while CAESAR continues to schedule it. For a repeated control path, configure fixed channels, bindings, and reusable task/driver buffers before start so the callback does not need to allocate or discover its work each release.

For example, this task requests 10 Hz: one release every 100 milliseconds:

task_periodic sample_control 10 normal:
    task:
        local measured = |device_measurement|
        local desired = measured * 2.0
        command |device_command| = desired

This illustrates scheduling and channel ownership, not a complete device controller. Configure the channels first. The fixed-channel driver walkthrough gives a runnable simulated example and explains native compilation.

How scheduling works

A standalone periodic task runs on its own thread. CAESAR uses a monotonic clock and advances the next planned release by the period. It does not add a fresh 100 ms sleep after completing each callback, which would accumulate execution time as scheduling drift.

The first release can run immediately after startup. A frequency is a target: a thread may wake late, and callbacks take time. CAESAR records those separately:

MeasurementMeaning
Release latenessHow late execution starts relative to the planned release.
Callback durationTime spent in the callback wrapper, including fixed snapshots, commit and synchronous reactions for scheduled tasks.
Missed cyclesPlanned releases skipped because execution has fallen behind. A slightly late wakeup does not necessarily skip a release.
OverrunsCallbacks whose measured duration reaches or exceeds their target period.
Actual frequencyObserved iteration rate; an average can conceal individual late releases.

When overdue, CAESAR advances to a future release rather than replaying every skipped callback. DCode’s missed: section, or a plugin’s existing on_missed callback, receives the skipped count and lateness. Keep it short: it runs on the execution thread. Releases consumed by the missed handler are counted without recursively calling it again.

A missed handler can respond after a delay, but cannot run while its own thread is permanently blocked. A requested priority influences OS scheduling; it does not establish a deadline or interrupt a blocking callback. See Execution and Timing for measurement boundaries.

Use a group when existing tasks should execute in order:

task_group device_cycle:
    run: device_read
    run: sample_control
    run: device_write

Configure all three at 10 Hz when every release should run acquisition, calculation and output in that order. The group runs due members sequentially on a coordinator thread, using one shared monotonic epoch. The members retain their names, lifecycle and diagnostics.

Members can have different frequencies. The group orders the members that are due together; it does not force slower members to run on every faster release. Its next wakeup comes from member schedules, and it uses the highest requested member priority. There is no additional group frequency or priority to configure.

A blocking member delays later members. Stopped members are skipped. If a callback throws, CAESAR records the failure and continues to later due members. dartwic/get-task-groups reports each member’s most recent due-release outcome (completed, callback_failed, skipped_stopped, or not_dispatched). Ordering does not mean that the previous step succeeded or supplied fresh data. Use application logic to decide whether to send an output after an acquisition failure. The scheduler does not choose a device’s fault response.

For declaration details and restrictions, use the task-group reference.

Task structures and lifecycle

StructureExecution
PeriodicRepeats a callback at its requested frequency.
State machineSchedules the current named state’s behavior.
SequenceRuns ordered work with managed progress.
TimelineRuns the timeline implementation supplied by the task type.
WorkerInvokes long-running worker work; the worker manages its internal repetitions.

A task’s type identifies the implementation; its structure determines execution. Do not infer a DCode keyword from every native structure name. Plugin workers publish writes immediately instead of retaining one fixed transaction for their entire lifetime.

Holding preserves task state. Stopping ends the run and clears hold. Stop waits for executing work to return; it is not forced thread cancellation. A task’s stop callback supplies its final behavior. Task-owned automatic channel authority is released on stop; observe-only outputs remain protected until explicitly freed.

A module owns a connection or shared service. One or more tasks can use it. Configure channels, bindings and reusable buffers before running, and use the existing reconfiguration lifecycle when mappings change.

Controls and failure diagnostics

For a task named sample_control, append these suffixes to its name:

SuffixPurpose
_runningStart/stop request.
_holdHold/release request on supported structures.
_target_frequencyRequested Hz on periodic, state-machine and timeline tasks.
_actual_frequencyObserved rate on those scheduled structures. Workers instead expose _worker_rate.
_execution_timeSmoothed callback duration in milliseconds on scheduled structures.
_missed_cyclesCumulative skipped releases on scheduled structures.
_timing_statusTiming diagnostic: 0 OK, 1 warning, 2 late; not a hardware-safety verdict.
_failed_cyclesCumulative callback exceptions caught by CAESAR, on all task structures.

Failure counts persist across stop/start of the same runtime task. A successful later cycle does not erase them. The failure channel is published by task synchronization outside the callback, so it is not an immediate exception handler. Code that catches its own error without rethrowing must report that error itself. Start/end lifecycle errors and process crashes are not counted as failed cycles.

The registered-tasks operation also returns an execution object with failed_cycles, last_cycle_failed, callback_active and callback_active_ms, plus an independently sampled health object when the task-health monitor has observed the task. The active duration includes time waiting inside the callback. A callback whose active duration reaches its completion deadline is reported as active_overdue; CAESAR never forcibly terminates it. A prepared fixed-channel release that finds a configuration writer is rejected promptly and reported as configuration_contention, rather than waiting behind that writer. Set health_completion_deadline_ms in task metadata or arguments to override the default one-period completion deadline. Application code may subscribe through CAESAR’s task-health observer and choose the recovery action.

Task diagnostics tell an application what happened. They do not decide whether the next command is safe. For the prepared-path requirements and a practical response checklist, see qualifying an automation deployment.

Use Writing Periodic Tasks for start:, task:, missed: and end: examples. Use Deterministic Automation to connect this scheduling model to prepared fixed storage, Native C++ DCode, bounded delivery, and health reporting.