long_running_command

Example of the component adhuldas/edgecommand v0.1.1
# EdgeCommand: Long-Running Command Example

Demonstrates progress reporting, cooperative timeout, and cooperative
cancellation, all with a single handler run in three different scenarios.

## Purpose

- Show a handler doing bounded chunks of work, reporting progress after
  each chunk with `edgecommand_report_progress()`, and checking
  `edgecommand_is_cancelled()` between chunks — the required pattern for
  any handler that runs longer than a trivial amount of time.
- Scenario 1: a short per-command timeout fires while the handler is still
  working, ending the command as `TIMED_OUT`.
- Scenario 2: the application calls `edgecommand_cancel()` while the
  handler is still working, ending the command as `CANCELLED`.
- Scenario 3: the handler is left alone with enough time and runs to
  `COMPLETED`.

## Requirements

- ESP-IDF v5.0 or newer.
- Any ESP-IDF-supported target.
- No external hardware required.

## Build and flash

```sh
cd examples/long_running_command
idf.py set-target esp32
idf.py build flash monitor
```

## Expected output (abbreviated)

```
I (300) long_running_example: === scenario 1: timeout ===
I (900) long_running_example: status: id=long-timeout-demo state=EXECUTING progress=0%
I (1400) long_running_example: status: id=long-timeout-demo state=EXECUTING progress=10%
I (1900) long_running_example: status: id=long-timeout-demo state=EXECUTING progress=20%
I (2400) long_running_example: command long-timeout-demo observed cancellation at step 3/10; returning early
I (2405) long_running_example: status: id=long-timeout-demo state=TIMED_OUT progress=20%
I (2410) long_running_example: scenario 1 result: TIMED_OUT (expected TIMED_OUT)
I (2415) long_running_example: === scenario 2: cancellation ===
...
I (3620) long_running_example: command long-cancel-demo observed cancellation at step 3/10; returning early
I (3625) long_running_example: scenario 2 result: CANCELLED (expected CANCELLED)
I (3630) long_running_example: === scenario 3: completes normally ===
...
I (8630) long_running_example: scenario 3 result: COMPLETED (expected COMPLETED)
```

The example runs for roughly 12 seconds total (timeout scenario stops
early at ~2.4s, cancellation scenario stops early at ~1.7s, completion
scenario runs the full ~5s).

## What's happening

- **Timeout is cooperative, not preemptive.** `edgecommand_timeout_arm()`
  only starts a clock; when it fires, it sets a flag the handler must
  observe via `edgecommand_is_cancelled()`. EdgeCommand never kills the
  worker task or forcibly interrupts the handler — see `RELIABILITY.md`
  for why, and what happens if a handler never checks the flag.
- **The timeout clock starts at `EXECUTING`, not at submission.** Queueing
  delay (time spent `PENDING`, waiting for the single worker task to reach
  this command) is a separate concern from the execution timeout budget.
- **The engine, not the handler, has the final say on `TIMED_OUT` vs.
  `CANCELLED`.** In scenario 1, the handler returns exactly the same
  `EDGECOMMAND_ERR_COMMAND_CANCELLED` / `EDGECOMMAND_RESULT_CANCELLED` it
  would for a user-requested cancellation — it cannot tell the two apart
  from inside the handler. The engine tracks *why* cancellation was
  requested and reports `TIMED_OUT` when the reason was a timeout,
  `CANCELLED` otherwise.
- **Progress is not persisted to NVS.** It's reported live via the
  reporter/event callbacks (and readable via `edgecommand_get_status()`
  while the command is in memory) but is not durable — see
  `RELIABILITY.md` for why, and what is durable at each state transition.

To create a project from this example, run:

idf.py create-project-from-example "adhuldas/edgecommand=0.1.1:long_running_command"

or download archive (~3.80 KB)