Covers Engine 2.0.0
Writing Periodic Tasks
Run scheduled logic with persistent state and missed-release diagnostics.
Use task_periodic for work that repeats at a requested frequency. It samples its inputs when its iteration runs; it does not run once per input update.
A stateful filter
Assume another source publishes demo_noise:
task_periodic tasks_noise_filter:
start:
state.last = |demo_noise|
task elapsed_seconds:
local raw = |demo_noise|
state.last = state.last + (raw - state.last) * 0.2
|demo_noise_filtered| = state.last
missed:
print("Skipped releases", missed_count, "lateness ms", lateness_ms)
end:
print("Noise filter stopped")start: runs at task start. task elapsed_seconds: repeats while running and not held. The elapsed value excludes held time. end: runs on stop. The filter’s coefficient is applied once per iteration, so changing task frequency changes its time response.
Operate the task
| Channel suffix | Purpose |
|---|---|
_running | Write 1 to start or 0 to stop. |
_hold | Write 1 to hold, 0 to resume. |
_target_frequency | Requested Hz; defaults to 10. |
_actual_frequency | Measured Hz; observe-only. |
For this example, the start channel is tasks_noise_filter_running. Task Lifecycle explains ownership and stopping.
Missed releases
CAESAR can skip overdue releases. missed: runs once for the skipped group with missed_count and lateness_ms. Keep this handler short; it cannot recover physical samples that were never acquired. Inspect execution time and missed-cycle diagnostics when the requested frequency is not being achieved.
A 10 Hz loop has a nominal 100 ms interval. It is not an immediate limit detector. Use a dependency-driven event when the condition should be evaluated in the input-update path.
Dynamic channel names
This section requires the Lua backend. Native C++ requires static pipe names and supports only single-argument print statements, so the earlier multi-argument missed-release log also needs simplifying before native compilation.
{expression} inside a pipe reference builds a name at runtime:
task_periodic tasks_state_copy:
task elapsed_seconds:
for name in {"alpha", "beta", "gamma"}:
|demo_{name}_state| = |demo_{name}_command|With name equal to alpha, the target is demo_alpha_state. Dynamic names work in calculations too, but require explicit trigger dependencies there. See Channel Calculations.