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

6.5 KiB

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:

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.

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

#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

  • Headers Arduino-free
  • Every cell in golden table asserted
  • Overlay does not mutate const steps
  • STATUS→DONE