Files
AutoFilm-ESP32/docs/megaplans/refactor/P02-ui-cmd-process.md
2026-09-16 11:30:47 +00:00

198 lines
6.5 KiB
Markdown

# P02 — ui_cmd + app_process + golden tests
STATUS: DONE
DEPENDS: P01
READ: this file, `src/devSequence.cpp` (values only), `docs/megaplans/REFACTOR-MEGAPLAN.md`
OUT: FreeRTOS tasks, HAL, display, Arduino, CI yaml, machine states
FORBIDDEN: mutate recipe numbers; `String`; Arduino headers in new public `.h`
## IN
C components + host tests that lock recipe bytes. No firmware behaviour change yet (`main.c` may log process count).
## FILES
```
components/ui_cmd/CMakeLists.txt
components/ui_cmd/include/ui_cmd.h
components/app_process/CMakeLists.txt
components/app_process/include/app_process.h
components/app_process/app_process.c
tests/host/CMakeLists.txt
tests/host/test_process.c
tests/host/main.c # if Unity/CTest needs a runner; else add_executable per test
```
Host CMake **must not** require `IDF_PATH`. Link `app_process` sources directly. Provide `esp_err_t` typedef if needed:
```c
typedef int esp_err_t;
#define ESP_OK 0
#define ESP_ERR_NOT_FOUND 0x105
#define ESP_ERR_INVALID_ARG 0x102
```
in `tests/host/esp_host_compat.h` and include it only in host builds.
## ui_cmd.h (lock)
Packed, no pointers in the union payload except none — use ids.
```c
#pragma once
#include <stdint.h>
#include <stdbool.h>
#define UI_CMD_QUEUE_LEN 8
#define UI_EVT_QUEUE_LEN 16
typedef enum {
CMD_SELECT_PROCESS = 1,
CMD_BROWSE_STEP = 2,
CMD_ADJUST_STEP_TIME = 3,
CMD_ARM_STEP = 4,
CMD_START_STEP = 5,
CMD_STOP = 6,
CMD_RESUME = 7,
CMD_RETURN_TO_STEP_SELECT = 8,
CMD_CANCEL_ARMED = 9,
CMD_PUMP_SET = 100,
CMD_VALVE_SET = 101,
CMD_RESERVOIR_SELECT = 102,
CMD_DRAIN_START = 103,
CMD_DRAIN_STOP = 104,
CMD_HEATER_SETPOINT = 105
} ui_cmd_id_t;
typedef enum {
EVT_PROCESS_SELECTED = 1,
EVT_STEP_VIEW = 2,
EVT_STEP_ARMED = 3,
EVT_STEP_STARTED = 4,
EVT_STEP_PROGRESS = 5,
EVT_STEP_COMPLETE = 6,
EVT_STEP_STOPPED = 7,
EVT_STEP_RESUMED = 8,
EVT_PROCESS_IDLE = 9,
EVT_TEMP_UPDATED = 10,
EVT_FAULT = 11
} ui_evt_id_t;
typedef enum { MOTION_IDLE = 0, MOTION_CW = 1, MOTION_CCW = 2 } motion_t;
typedef struct {
ui_cmd_id_t id;
uint8_t process_id;
int8_t step_delta; /* BrowseStep */
int8_t time_delta_s; /* AdjustStepTime, typically ±5 */
uint8_t step_index; /* Arm/Start */
} ui_cmd_t;
typedef struct {
ui_evt_id_t id;
uint8_t process_id;
uint8_t step_index;
uint8_t step_count;
uint16_t time_s;
uint32_t remaining_ms;
float temp_c; /* NAN if disconnected */
bool temp_ok;
float temp_min_c, temp_pref_c, temp_max_c;
float cw_revs, ccw_revs;
motion_t motion;
int16_t fault_code;
} ui_evt_t;
```
P03+ fill unused fields with 0 / NAN. Do not add `char name[n]` to the event if it forces copies; UI looks up names via `app_process` by id+index.
## app_process.h (lock)
```c
#pragma once
#include <stdint.h>
#include "esp_err.h" /* or host compat */
#define PROCESS_COUNT 6
#define PROCESS_MAX_STEPS 20
enum {
PROC_C41 = 0,
PROC_E6 = 1,
PROC_ECN2 = 2,
PROC_BW = 3,
PROC_CUSTOM = 4,
PROC_BWREV = 5
};
typedef struct {
const char *name;
uint16_t time_s;
float cw_revs, ccw_revs;
float temp_min_c, temp_pref_c, temp_max_c;
} process_step_t;
typedef struct {
const char *name;
uint8_t step_count;
const process_step_t *steps;
} process_def_t;
void app_process_init(void); /* zero session deltas */
const process_def_t *app_process_get(uint8_t id);
const process_def_t *app_process_find_name(const char *name);
uint16_t app_process_time_s(uint8_t id, uint8_t step); /* default + session delta, min 0 */
esp_err_t app_process_adjust_time(uint8_t id, uint8_t step, int8_t delta_s);
void app_process_reset_session(uint8_t id);
```
Session overlay: `static int16_t time_delta_s[PROCESS_COUNT][PROCESS_MAX_STEPS]`. `adjust_time` adds delta; does **not** write `process_step_t.time_s`. Clamp resulting time to `[0, 36000]`.
## Golden table (assert exact)
Copy from `src/devSequence.cpp`. Names `strcmp`. Floats: compare with `fabsf(a-b)<1e-4` except integers as exact.
**C41** `name="C41"` count=7
| i | name | t | cw | ccw | min | pref | max |
|---|---|---:|---:|---:|---:|---:|---:|
| 0 | Prewarm | 180 | 1 | 1 | 37.8 | 38 | 38.2 |
| 1 | Developer | 195 | 5.5 | 5 | 37.8 | 38 | 38.2 |
| 2 | Bleach | 45 | 5.5 | 5 | 32 | 38 | 38.2 |
| 3 | Fix | 180 | 5.5 | 5 | 32 | 38 | 38.2 |
| 4 | Rinse 1 | 60 | 3.5 | 3 | 32 | 38 | 38.2 |
| 5 | Rinse 2 | 60 | 3.5 | 3 | 32 | 38 | 38.2 |
| 6 | Fin Rinse | 30 | 1 | 1 | 32 | 38 | 38.2 |
**E6** `name="E6"` 12: times `{180,360,120,120,360,120,360,240,120,120,120,30}` names `Preheat,FirstDev,Wash 1,Reversal,ColorDev,PreBleach,Bleach,Fixer,Wash 2,Wash 3,Wash 4,Fin Rinse` cw `{1,5.5,3.5,5,5.5,5.5,5.5,5.5,3.5,3.5,3.5,3.5}` ccw `{1,5,3,5.5,5,5.5,5.5,5.5,3,3,3,3}` min `{37.5,37.7,33.0,37.7,37.0,37,37.5,37.5,33.0,33.0,33.0,19.0}` pref `{38,38,38,38,38,38,38,38,38,38,38,20}` max `{38.5,38.3,38.0,38.3,39.0,38,38.5,38.5,38.5,38.5,38.5,21.0}`
**ECN-2** `name="ECN-2"` 9: t `{180,0,210,60,180,150,120,300,120}` names `Prebath,RemJet,Developer,Stop Bath,Wash,Bleach,Fixer,Wash 2,Fin Rinse` cw `{1,0,5.5,3.5,3.5,5.5,5.5,3.5,1}` ccw `{1,0,5,3,3,5,5,3,1}` min `{27,0,40.8,27,27,27,27,27,27}` pref `{38,0,41,38,38,38,38,38,38}` max `{38,0,41.2,38,38,38,38,38,38}`
**B&W** `name="B&W"` 7: t `{510,30,300,60,90,120,30}` names `Developer,Stop,Fix,Rinse 1,Rinse 2,Rinse 3,Fin Rinse` cw `{5.5,3.5,3.5,3.5,3.5,3.5,1}` ccw `{5,3,3,3,3,3,1}` min 19 pref 20 max 21 all.
**Custom** `name="Custom"` 4: t 10, names `Developer,Stop,Fix,Rinse` cw `{5.5,3.5,3.5,3.5}` ccw `{5,3,3,3}` min 19 pref 20 max 21.
**B&WREV** `name="B&WREV"` 12: t `{720,300,300,60,120,60,120,360,60,300,60,60}` names `FirstDev,Wash 1,Bleach,Wash 2,Clearing,Wash 3,Reversal,SecondDev,Wash 4,Fix,Wash 5,Fin Rinse` cw `{5.5,3.5,5.5,3.5,5.5,3.5,5.5,5.5,3.5,5.5,3.5,3.5}` ccw `{5,3,5,3,5,3,5,5,3,5,3,3}` min `{19.5,15.5,19.5,15.5,19.5,15.5,19.5,19.5,15.5,19.5,15.5,15.5}` pref 20 all max `{20.5,22.5,22.5,22.5,22.5,22.5,22.5,22.5,22.5,22.5,22.5,22.5}`
Tests also: `find_name("nope")==NULL`; `adjust_time(C41,1,+5)` → 200, const table still 195; `reset_session` restores 195.
## VERIFY
```
cmake -S tests/host -B build/host && cmake --build build/host && ctest --test-dir build/host --output-on-failure
```
All golden tests pass.
## COMMITS
1. `Add ui_cmd and app_process with frozen recipe tables`
2. `Add host tests for process lookup and session time overlay`
## DoD
- [x] Headers Arduino-free
- [x] Every cell in golden table asserted
- [x] Overlay does not mutate const steps
- [x] STATUS→DONE