# 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"