Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b5c7a096f | ||
|
|
8f5b41ebba | ||
|
|
c047d5d96f | ||
|
|
461771cc02 | ||
|
|
9b2c6bf3ee | ||
|
|
415c13a605 | ||
|
|
d8397778d5 | ||
|
|
255db87881 | ||
|
|
179efb54cd | ||
|
|
01cc76b8b1 | ||
|
|
a9298ae521 |
@@ -0,0 +1,81 @@
|
||||
name: ci
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [develop]
|
||||
push:
|
||||
branches: [develop, "refactor/**", "feature/**"]
|
||||
tags: ["v*"]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install host toolchain
|
||||
if: hashFiles('tests/host/CMakeLists.txt') != ''
|
||||
run: |
|
||||
export DEBIAN_FRONTEND=noninteractive && \
|
||||
if command -v sudo >/dev/null 2>&1 && [ "$(id -u)" -ne 0 ]; then SUDO=sudo; else SUDO=; fi && \
|
||||
$SUDO apt-get update && \
|
||||
$SUDO apt-get install -y --no-install-recommends cmake gcc g++ make
|
||||
- name: Host unit tests
|
||||
if: hashFiles('tests/host/CMakeLists.txt') != ''
|
||||
run: |
|
||||
cmake -S tests/host -B build/host && \
|
||||
cmake --build build/host && \
|
||||
ctest --test-dir build/host --output-on-failure
|
||||
|
||||
firmware:
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: docker.io/espressif/idf:v5.4
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- board: wroom
|
||||
target: esp32
|
||||
sdkconfig: sdkconfig.wroom
|
||||
- board: jc4827w543
|
||||
target: esp32s3
|
||||
sdkconfig: sdkconfig.s3
|
||||
steps:
|
||||
- name: Install Node.js
|
||||
run: |
|
||||
export DEBIAN_FRONTEND=noninteractive && \
|
||||
if command -v sudo >/dev/null 2>&1 && [ "$(id -u)" -ne 0 ]; then SUDO=sudo; else SUDO=; fi && \
|
||||
$SUDO apt-get update && \
|
||||
$SUDO apt-get install -y --no-install-recommends curl ca-certificates && \
|
||||
curl -fsSL https://deb.nodesource.com/setup_20.x | $SUDO bash - && \
|
||||
$SUDO apt-get install -y --no-install-recommends nodejs
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
if: hashFiles('CMakeLists.txt') != ''
|
||||
run: |
|
||||
git config --global --add safe.directory '*' && \
|
||||
idf.py -B build/${{ matrix.board }} set-target ${{ matrix.target }} && \
|
||||
idf.py -B build/${{ matrix.board }} \
|
||||
-D SDKCONFIG_DEFAULTS="sdkconfig.defaults;${{ matrix.sdkconfig }}" \
|
||||
-D AUTOFILM_BOARD=${{ matrix.board }} \
|
||||
build && \
|
||||
idf.py -B build/${{ matrix.board }} size | tee size-${{ matrix.board }}.txt && \
|
||||
python $IDF_PATH/components/esptool_py/esptool/esptool.py \
|
||||
--chip ${{ matrix.target }} merge_bin \
|
||||
-o autofilm-${{ matrix.board }}-${{ gitea.sha }}.bin \
|
||||
@build/${{ matrix.board }}/flash_args
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: hashFiles('CMakeLists.txt') != ''
|
||||
with:
|
||||
name: firmware-${{ matrix.board }}
|
||||
path: |
|
||||
autofilm-${{ matrix.board }}-*.bin
|
||||
build/${{ matrix.board }}/*.bin
|
||||
build/${{ matrix.board }}/bootloader/bootloader.bin
|
||||
build/${{ matrix.board }}/partition_table/partition-table.bin
|
||||
build/${{ matrix.board }}/flasher_args.json
|
||||
build/${{ matrix.board }}/sdkconfig
|
||||
size-${{ matrix.board }}.txt
|
||||
|
||||
@@ -3,3 +3,8 @@
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
build/
|
||||
sdkconfig
|
||||
sdkconfig.old
|
||||
managed_components/
|
||||
dependencies.lock
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(autofilm)
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "app_machine.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES ui_cmd app_process)
|
||||
@@ -0,0 +1,425 @@
|
||||
#include "app_machine.h"
|
||||
#include "app_process.h"
|
||||
#include "hal_motor.h"
|
||||
#include "hal_temp.h"
|
||||
#include "hal_audio.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define AGITATE_RPM 60u
|
||||
|
||||
static machine_state_t s_state;
|
||||
static uint8_t s_proc;
|
||||
static uint8_t s_step;
|
||||
static uint32_t s_remaining_ms;
|
||||
static uint32_t s_deadline_ms;
|
||||
static bool s_have_deadline;
|
||||
static bool s_resume_pending;
|
||||
static bool s_auto_advance;
|
||||
static ui_evt_t s_q[UI_EVT_QUEUE_LEN];
|
||||
static uint8_t s_q_head;
|
||||
static uint8_t s_q_tail;
|
||||
static uint8_t s_q_count;
|
||||
|
||||
static void emit(ui_evt_t ev)
|
||||
{
|
||||
if (s_q_count == UI_EVT_QUEUE_LEN) {
|
||||
s_q_head = (uint8_t)((s_q_head + 1u) % UI_EVT_QUEUE_LEN);
|
||||
s_q_count--;
|
||||
}
|
||||
s_q[s_q_tail] = ev;
|
||||
s_q_tail = (uint8_t)((s_q_tail + 1u) % UI_EVT_QUEUE_LEN);
|
||||
s_q_count++;
|
||||
}
|
||||
|
||||
static void fill_common(ui_evt_t *ev)
|
||||
{
|
||||
const process_def_t *p = app_process_get(s_proc);
|
||||
memset(ev, 0, sizeof(*ev));
|
||||
ev->process_id = s_proc;
|
||||
ev->step_index = s_step;
|
||||
ev->step_count = (p != NULL) ? p->step_count : 0;
|
||||
ev->time_s = app_process_time_s(s_proc, s_step);
|
||||
ev->remaining_ms = s_remaining_ms;
|
||||
if (p != NULL && s_step < p->step_count) {
|
||||
ev->cw_revs = p->steps[s_step].cw_revs;
|
||||
ev->ccw_revs = p->steps[s_step].ccw_revs;
|
||||
ev->temp_min_c = p->steps[s_step].temp_min_c;
|
||||
ev->temp_pref_c = p->steps[s_step].temp_pref_c;
|
||||
ev->temp_max_c = p->steps[s_step].temp_max_c;
|
||||
}
|
||||
}
|
||||
|
||||
static void emit_id(ui_evt_id_t id)
|
||||
{
|
||||
ui_evt_t ev;
|
||||
fill_common(&ev);
|
||||
ev.id = id;
|
||||
emit(ev);
|
||||
}
|
||||
|
||||
static void emit_fault(int16_t code)
|
||||
{
|
||||
ui_evt_t ev;
|
||||
fill_common(&ev);
|
||||
ev.id = EVT_FAULT;
|
||||
ev.fault_code = code;
|
||||
emit(ev);
|
||||
}
|
||||
|
||||
static void apply_step_index(uint8_t step)
|
||||
{
|
||||
const process_def_t *p = app_process_get(s_proc);
|
||||
if (p == NULL || p->step_count == 0) {
|
||||
return;
|
||||
}
|
||||
if (step >= p->step_count) {
|
||||
step = (uint8_t)(p->step_count - 1u);
|
||||
}
|
||||
s_step = step;
|
||||
}
|
||||
|
||||
static void replace_session_time_s(uint16_t time_s)
|
||||
{
|
||||
uint16_t cur = app_process_time_s(s_proc, s_step);
|
||||
int32_t left = (int32_t)time_s - (int32_t)cur;
|
||||
while (left != 0) {
|
||||
int8_t chunk = (left > 127) ? 127 : (left < -128) ? -128 : (int8_t)left;
|
||||
if (app_process_adjust_time(s_proc, s_step, chunk) != ESP_OK) {
|
||||
break;
|
||||
}
|
||||
uint16_t now = app_process_time_s(s_proc, s_step);
|
||||
int32_t next_left = (int32_t)time_s - (int32_t)now;
|
||||
if (next_left == left) {
|
||||
break;
|
||||
}
|
||||
left = next_left;
|
||||
}
|
||||
}
|
||||
|
||||
static void go_step_select(void)
|
||||
{
|
||||
s_state = ST_STEP_SELECT;
|
||||
s_have_deadline = false;
|
||||
s_resume_pending = false;
|
||||
emit_id(EVT_STEP_VIEW);
|
||||
}
|
||||
|
||||
static void go_idle(void)
|
||||
{
|
||||
s_state = ST_IDLE;
|
||||
s_have_deadline = false;
|
||||
s_resume_pending = false;
|
||||
s_remaining_ms = 0;
|
||||
emit_id(EVT_PROCESS_IDLE);
|
||||
}
|
||||
|
||||
static void enter_armed(void)
|
||||
{
|
||||
s_state = ST_ARMED;
|
||||
s_have_deadline = false;
|
||||
s_resume_pending = false;
|
||||
emit_id(EVT_STEP_ARMED);
|
||||
}
|
||||
|
||||
static void start_running(uint32_t now_ms)
|
||||
{
|
||||
const process_def_t *p = app_process_get(s_proc);
|
||||
uint16_t t_s = app_process_time_s(s_proc, s_step);
|
||||
s_remaining_ms = (uint32_t)t_s * 1000u;
|
||||
s_deadline_ms = now_ms + s_remaining_ms;
|
||||
s_have_deadline = true;
|
||||
s_resume_pending = false;
|
||||
|
||||
if (t_s == 0 || p == NULL) {
|
||||
hal_motor_enable(false);
|
||||
} else {
|
||||
(void)hal_motor_agitate_start(p->steps[s_step].cw_revs,
|
||||
p->steps[s_step].ccw_revs,
|
||||
AGITATE_RPM);
|
||||
hal_motor_enable(true);
|
||||
}
|
||||
s_state = ST_RUNNING;
|
||||
emit_id(EVT_STEP_STARTED);
|
||||
}
|
||||
|
||||
static void complete_step(void)
|
||||
{
|
||||
hal_motor_agitate_stop();
|
||||
hal_motor_enable(false);
|
||||
s_remaining_ms = 0;
|
||||
s_have_deadline = false;
|
||||
s_resume_pending = false;
|
||||
s_state = ST_COMPLETE;
|
||||
hal_audio_alarm_complete();
|
||||
emit_id(EVT_STEP_COMPLETE);
|
||||
}
|
||||
|
||||
static void maybe_auto_advance(uint32_t now_ms)
|
||||
{
|
||||
(void)now_ms;
|
||||
if (!s_auto_advance) {
|
||||
return;
|
||||
}
|
||||
const process_def_t *p = app_process_get(s_proc);
|
||||
if (p == NULL) {
|
||||
go_idle();
|
||||
return;
|
||||
}
|
||||
if ((uint16_t)s_step + 1u < p->step_count) {
|
||||
s_step++;
|
||||
enter_armed();
|
||||
} else {
|
||||
go_idle();
|
||||
}
|
||||
}
|
||||
|
||||
static void arm_or_next_from_complete(void)
|
||||
{
|
||||
const process_def_t *p = app_process_get(s_proc);
|
||||
if (p == NULL) {
|
||||
go_idle();
|
||||
return;
|
||||
}
|
||||
if ((uint16_t)s_step + 1u < p->step_count) {
|
||||
s_step++;
|
||||
enter_armed();
|
||||
} else {
|
||||
go_idle();
|
||||
}
|
||||
}
|
||||
|
||||
void app_machine_init(void)
|
||||
{
|
||||
s_state = ST_IDLE;
|
||||
s_proc = 0;
|
||||
s_step = 0;
|
||||
s_remaining_ms = 0;
|
||||
s_deadline_ms = 0;
|
||||
s_have_deadline = false;
|
||||
s_resume_pending = false;
|
||||
s_auto_advance = false;
|
||||
s_q_head = 0;
|
||||
s_q_tail = 0;
|
||||
s_q_count = 0;
|
||||
app_process_init();
|
||||
hal_motor_init();
|
||||
hal_temp_init();
|
||||
hal_audio_init();
|
||||
}
|
||||
|
||||
machine_state_t app_machine_state(void)
|
||||
{
|
||||
return s_state;
|
||||
}
|
||||
|
||||
uint8_t app_machine_process_id(void)
|
||||
{
|
||||
return s_proc;
|
||||
}
|
||||
|
||||
uint8_t app_machine_step_index(void)
|
||||
{
|
||||
return s_step;
|
||||
}
|
||||
|
||||
uint32_t app_machine_remaining_ms(void)
|
||||
{
|
||||
return s_remaining_ms;
|
||||
}
|
||||
|
||||
int app_machine_last_event(ui_evt_t *out)
|
||||
{
|
||||
if (s_q_count == 0 || out == NULL) {
|
||||
return 0;
|
||||
}
|
||||
*out = s_q[s_q_head];
|
||||
s_q_head = (uint8_t)((s_q_head + 1u) % UI_EVT_QUEUE_LEN);
|
||||
s_q_count--;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void app_machine_on_temp(float c, bool ok)
|
||||
{
|
||||
ui_evt_t ev;
|
||||
fill_common(&ev);
|
||||
ev.id = EVT_TEMP_UPDATED;
|
||||
ev.temp_c = c;
|
||||
ev.temp_ok = ok;
|
||||
emit(ev);
|
||||
}
|
||||
|
||||
void app_machine_tick(uint32_t now_ms)
|
||||
{
|
||||
if (s_state == ST_RUNNING && s_resume_pending) {
|
||||
s_deadline_ms = now_ms + s_remaining_ms;
|
||||
s_have_deadline = true;
|
||||
s_resume_pending = false;
|
||||
}
|
||||
|
||||
if (s_state == ST_RUNNING && s_have_deadline) {
|
||||
if (now_ms >= s_deadline_ms) {
|
||||
s_remaining_ms = 0;
|
||||
complete_step();
|
||||
maybe_auto_advance(now_ms);
|
||||
return;
|
||||
}
|
||||
s_remaining_ms = s_deadline_ms - now_ms;
|
||||
emit_id(EVT_STEP_PROGRESS);
|
||||
}
|
||||
}
|
||||
|
||||
void app_machine_handle_cmd(const ui_cmd_t *cmd)
|
||||
{
|
||||
if (cmd == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cmd->id) {
|
||||
case CMD_SELECT_PROCESS:
|
||||
if (app_process_get(cmd->process_id) == NULL) {
|
||||
emit_fault(1);
|
||||
return;
|
||||
}
|
||||
s_proc = cmd->process_id;
|
||||
s_step = 0;
|
||||
s_remaining_ms = 0;
|
||||
s_state = ST_STEP_SELECT;
|
||||
emit_id(EVT_PROCESS_SELECTED);
|
||||
emit_id(EVT_STEP_VIEW);
|
||||
return;
|
||||
|
||||
case CMD_BROWSE_STEP:
|
||||
if (s_state != ST_STEP_SELECT) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
const process_def_t *p = app_process_get(s_proc);
|
||||
if (p == NULL || p->step_count == 0) {
|
||||
return;
|
||||
}
|
||||
int32_t next = (int32_t)s_step + (int32_t)cmd->step_delta;
|
||||
if (next < 0) {
|
||||
next = 0;
|
||||
}
|
||||
if (next >= (int32_t)p->step_count) {
|
||||
next = (int32_t)p->step_count - 1;
|
||||
}
|
||||
s_step = (uint8_t)next;
|
||||
emit_id(EVT_STEP_VIEW);
|
||||
}
|
||||
return;
|
||||
|
||||
case CMD_ADJUST_STEP_TIME:
|
||||
if (s_state != ST_STEP_SELECT) {
|
||||
return;
|
||||
}
|
||||
if (app_process_adjust_time(s_proc, s_step, cmd->time_delta_s) == ESP_OK) {
|
||||
emit_id(EVT_STEP_VIEW);
|
||||
} else {
|
||||
emit_fault(1);
|
||||
}
|
||||
return;
|
||||
|
||||
case CMD_ARM_STEP:
|
||||
if (s_state == ST_STEP_SELECT) {
|
||||
apply_step_index(cmd->step_index);
|
||||
enter_armed();
|
||||
} else if (s_state == ST_COMPLETE) {
|
||||
arm_or_next_from_complete();
|
||||
}
|
||||
return;
|
||||
|
||||
case CMD_START_STEP:
|
||||
if (s_state == ST_STEP_SELECT) {
|
||||
apply_step_index(cmd->step_index);
|
||||
enter_armed();
|
||||
} else if (s_state == ST_ARMED) {
|
||||
apply_step_index(cmd->step_index);
|
||||
start_running(0);
|
||||
} else if (s_state == ST_COMPLETE) {
|
||||
arm_or_next_from_complete();
|
||||
}
|
||||
return;
|
||||
|
||||
case CMD_CANCEL_ARMED:
|
||||
if (s_state == ST_ARMED) {
|
||||
hal_motor_enable(false);
|
||||
go_step_select();
|
||||
}
|
||||
return;
|
||||
|
||||
case CMD_STOP:
|
||||
if (s_state == ST_IDLE) {
|
||||
return;
|
||||
}
|
||||
if (s_state == ST_RUNNING) {
|
||||
/* request_stop before any other work */
|
||||
hal_motor_request_stop();
|
||||
hal_motor_enable(false);
|
||||
if (s_have_deadline && !s_resume_pending) {
|
||||
/* remaining already updated by last tick; if never ticked, keep start remaining */
|
||||
}
|
||||
hal_audio_beep_short();
|
||||
s_state = ST_STOPPED;
|
||||
s_have_deadline = false;
|
||||
s_resume_pending = false;
|
||||
emit_id(EVT_STEP_STOPPED);
|
||||
return;
|
||||
}
|
||||
if (s_state == ST_ARMED) {
|
||||
hal_motor_enable(false);
|
||||
go_step_select();
|
||||
return;
|
||||
}
|
||||
if (s_state == ST_STOPPED) {
|
||||
{
|
||||
uint16_t secs = (uint16_t)((s_remaining_ms + 999u) / 1000u);
|
||||
replace_session_time_s(secs);
|
||||
}
|
||||
hal_motor_enable(false);
|
||||
go_step_select();
|
||||
return;
|
||||
}
|
||||
if (s_state == ST_COMPLETE) {
|
||||
hal_audio_alarm_cancel();
|
||||
hal_audio_beep_short();
|
||||
go_step_select();
|
||||
return;
|
||||
}
|
||||
return;
|
||||
|
||||
case CMD_RESUME:
|
||||
if (s_state == ST_STOPPED) {
|
||||
const process_def_t *p = app_process_get(s_proc);
|
||||
hal_audio_beep_short();
|
||||
s_resume_pending = true;
|
||||
s_have_deadline = false;
|
||||
if (p != NULL && s_remaining_ms > 0) {
|
||||
(void)hal_motor_agitate_start(p->steps[s_step].cw_revs,
|
||||
p->steps[s_step].ccw_revs,
|
||||
AGITATE_RPM);
|
||||
hal_motor_enable(true);
|
||||
} else {
|
||||
hal_motor_enable(false);
|
||||
}
|
||||
s_state = ST_RUNNING;
|
||||
emit_id(EVT_STEP_RESUMED);
|
||||
}
|
||||
return;
|
||||
|
||||
case CMD_RETURN_TO_STEP_SELECT:
|
||||
if (s_state == ST_STOPPED) {
|
||||
uint16_t secs = (uint16_t)((s_remaining_ms + 999u) / 1000u);
|
||||
replace_session_time_s(secs);
|
||||
hal_motor_enable(false);
|
||||
go_step_select();
|
||||
} else if (s_state == ST_COMPLETE) {
|
||||
go_step_select();
|
||||
}
|
||||
return;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "ui_cmd.h"
|
||||
|
||||
typedef enum {
|
||||
ST_IDLE = 0,
|
||||
ST_STEP_SELECT,
|
||||
ST_ARMED,
|
||||
ST_RUNNING,
|
||||
ST_STOPPED,
|
||||
ST_COMPLETE
|
||||
} machine_state_t;
|
||||
|
||||
void app_machine_init(void);
|
||||
machine_state_t app_machine_state(void);
|
||||
void app_machine_handle_cmd(const ui_cmd_t *cmd);
|
||||
void app_machine_tick(uint32_t now_ms); /* drive timers; tests call this */
|
||||
void app_machine_on_temp(float c, bool ok);
|
||||
int app_machine_last_event(ui_evt_t *out); /* pop 1 event; 0 empty, 1 ok */
|
||||
uint8_t app_machine_process_id(void);
|
||||
uint8_t app_machine_step_index(void);
|
||||
uint32_t app_machine_remaining_ms(void);
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "app_process.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES ui_cmd)
|
||||
@@ -0,0 +1,148 @@
|
||||
#include "app_process.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static const process_step_t s_c41_steps[] = {
|
||||
{"Prewarm", 180, 1.f, 1.f, 37.8f, 38.f, 38.2f},
|
||||
{"Developer", 195, 5.5f, 5.f, 37.8f, 38.f, 38.2f},
|
||||
{"Bleach", 45, 5.5f, 5.f, 32.f, 38.f, 38.2f},
|
||||
{"Fix", 180, 5.5f, 5.f, 32.f, 38.f, 38.2f},
|
||||
{"Rinse 1", 60, 3.5f, 3.f, 32.f, 38.f, 38.2f},
|
||||
{"Rinse 2", 60, 3.5f, 3.f, 32.f, 38.f, 38.2f},
|
||||
{"Fin Rinse", 30, 1.f, 1.f, 32.f, 38.f, 38.2f},
|
||||
};
|
||||
|
||||
static const process_step_t s_e6_steps[] = {
|
||||
{"Preheat", 180, 1.f, 1.f, 37.5f, 38.f, 38.5f},
|
||||
{"FirstDev", 360, 5.5f, 5.f, 37.7f, 38.f, 38.3f},
|
||||
{"Wash 1", 120, 3.5f, 3.f, 33.0f, 38.f, 38.0f},
|
||||
{"Reversal", 120, 5.f, 5.5f, 37.7f, 38.f, 38.3f},
|
||||
{"ColorDev", 360, 5.5f, 5.f, 37.0f, 38.f, 39.0f},
|
||||
{"PreBleach", 120, 5.5f, 5.5f, 37.f, 38.f, 38.f},
|
||||
{"Bleach", 360, 5.5f, 5.5f, 37.5f, 38.f, 38.5f},
|
||||
{"Fixer", 240, 5.5f, 5.5f, 37.5f, 38.f, 38.5f},
|
||||
{"Wash 2", 120, 3.5f, 3.f, 33.0f, 38.f, 38.5f},
|
||||
{"Wash 3", 120, 3.5f, 3.f, 33.0f, 38.f, 38.5f},
|
||||
{"Wash 4", 120, 3.5f, 3.f, 33.0f, 38.f, 38.5f},
|
||||
{"Fin Rinse", 30, 3.5f, 3.f, 19.0f, 20.f, 21.0f},
|
||||
};
|
||||
|
||||
static const process_step_t s_ecn2_steps[] = {
|
||||
{"Prebath", 180, 1.f, 1.f, 27.f, 38.f, 38.f},
|
||||
{"RemJet", 0, 0.f, 0.f, 0.f, 0.f, 0.f},
|
||||
{"Developer", 210, 5.5f, 5.f, 40.8f, 41.f, 41.2f},
|
||||
{"Stop Bath", 60, 3.5f, 3.f, 27.f, 38.f, 38.f},
|
||||
{"Wash", 180, 3.5f, 3.f, 27.f, 38.f, 38.f},
|
||||
{"Bleach", 150, 5.5f, 5.f, 27.f, 38.f, 38.f},
|
||||
{"Fixer", 120, 5.5f, 5.f, 27.f, 38.f, 38.f},
|
||||
{"Wash 2", 300, 3.5f, 3.f, 27.f, 38.f, 38.f},
|
||||
{"Fin Rinse", 120, 1.f, 1.f, 27.f, 38.f, 38.f},
|
||||
};
|
||||
|
||||
static const process_step_t s_bw_steps[] = {
|
||||
{"Developer", 510, 5.5f, 5.f, 19.f, 20.f, 21.f},
|
||||
{"Stop", 30, 3.5f, 3.f, 19.f, 20.f, 21.f},
|
||||
{"Fix", 300, 3.5f, 3.f, 19.f, 20.f, 21.f},
|
||||
{"Rinse 1", 60, 3.5f, 3.f, 19.f, 20.f, 21.f},
|
||||
{"Rinse 2", 90, 3.5f, 3.f, 19.f, 20.f, 21.f},
|
||||
{"Rinse 3", 120, 3.5f, 3.f, 19.f, 20.f, 21.f},
|
||||
{"Fin Rinse", 30, 1.f, 1.f, 19.f, 20.f, 21.f},
|
||||
};
|
||||
|
||||
static const process_step_t s_custom_steps[] = {
|
||||
{"Developer", 10, 5.5f, 5.f, 19.f, 20.f, 21.f},
|
||||
{"Stop", 10, 3.5f, 3.f, 19.f, 20.f, 21.f},
|
||||
{"Fix", 10, 3.5f, 3.f, 19.f, 20.f, 21.f},
|
||||
{"Rinse", 10, 3.5f, 3.f, 19.f, 20.f, 21.f},
|
||||
};
|
||||
|
||||
static const process_step_t s_bwrev_steps[] = {
|
||||
{"FirstDev", 720, 5.5f, 5.f, 19.5f, 20.f, 20.5f},
|
||||
{"Wash 1", 300, 3.5f, 3.f, 15.5f, 20.f, 22.5f},
|
||||
{"Bleach", 300, 5.5f, 5.f, 19.5f, 20.f, 22.5f},
|
||||
{"Wash 2", 60, 3.5f, 3.f, 15.5f, 20.f, 22.5f},
|
||||
{"Clearing", 120, 5.5f, 5.f, 19.5f, 20.f, 22.5f},
|
||||
{"Wash 3", 60, 3.5f, 3.f, 15.5f, 20.f, 22.5f},
|
||||
{"Reversal", 120, 5.5f, 5.f, 19.5f, 20.f, 22.5f},
|
||||
{"SecondDev", 360, 5.5f, 5.f, 19.5f, 20.f, 22.5f},
|
||||
{"Wash 4", 60, 3.5f, 3.f, 15.5f, 20.f, 22.5f},
|
||||
{"Fix", 300, 5.5f, 5.f, 19.5f, 20.f, 22.5f},
|
||||
{"Wash 5", 60, 3.5f, 3.f, 15.5f, 20.f, 22.5f},
|
||||
{"Fin Rinse", 60, 3.5f, 3.f, 15.5f, 20.f, 22.5f},
|
||||
};
|
||||
|
||||
static const process_def_t s_processes[PROCESS_COUNT] = {
|
||||
{"C41", 7, s_c41_steps},
|
||||
{"E6", 12, s_e6_steps},
|
||||
{"ECN-2", 9, s_ecn2_steps},
|
||||
{"B&W", 7, s_bw_steps},
|
||||
{"Custom", 4, s_custom_steps},
|
||||
{"B&WREV", 12, s_bwrev_steps},
|
||||
};
|
||||
|
||||
static int16_t s_time_delta_s[PROCESS_COUNT][PROCESS_MAX_STEPS];
|
||||
|
||||
void app_process_init(void)
|
||||
{
|
||||
memset(s_time_delta_s, 0, sizeof(s_time_delta_s));
|
||||
}
|
||||
|
||||
const process_def_t *app_process_get(uint8_t id)
|
||||
{
|
||||
if (id >= PROCESS_COUNT) {
|
||||
return NULL;
|
||||
}
|
||||
return &s_processes[id];
|
||||
}
|
||||
|
||||
const process_def_t *app_process_find_name(const char *name)
|
||||
{
|
||||
if (name == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
for (uint8_t i = 0; i < PROCESS_COUNT; i++) {
|
||||
if (strcmp(s_processes[i].name, name) == 0) {
|
||||
return &s_processes[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint16_t app_process_time_s(uint8_t id, uint8_t step)
|
||||
{
|
||||
if (id >= PROCESS_COUNT) {
|
||||
return 0;
|
||||
}
|
||||
const process_def_t *p = &s_processes[id];
|
||||
if (step >= p->step_count) {
|
||||
return 0;
|
||||
}
|
||||
int32_t t = (int32_t)p->steps[step].time_s + (int32_t)s_time_delta_s[id][step];
|
||||
if (t < 0) {
|
||||
t = 0;
|
||||
}
|
||||
if (t > 36000) {
|
||||
t = 36000;
|
||||
}
|
||||
return (uint16_t)t;
|
||||
}
|
||||
|
||||
esp_err_t app_process_adjust_time(uint8_t id, uint8_t step, int8_t delta_s)
|
||||
{
|
||||
if (id >= PROCESS_COUNT) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (step >= s_processes[id].step_count || step >= PROCESS_MAX_STEPS) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
s_time_delta_s[id][step] = (int16_t)(s_time_delta_s[id][step] + delta_s);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void app_process_reset_session(uint8_t id)
|
||||
{
|
||||
if (id >= PROCESS_COUNT) {
|
||||
return;
|
||||
}
|
||||
memset(s_time_delta_s[id], 0, sizeof(s_time_delta_s[id]));
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#ifdef AUTOFILM_HOST
|
||||
#include "esp_host_compat.h"
|
||||
#else
|
||||
#include "esp_err.h"
|
||||
#endif
|
||||
|
||||
#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);
|
||||
@@ -0,0 +1 @@
|
||||
idf_component_register(SRCS "board.c" INCLUDE_DIRS "include" REQUIRES driver)
|
||||
@@ -0,0 +1,16 @@
|
||||
#include "board.h"
|
||||
|
||||
const char *board_name(void)
|
||||
{
|
||||
return "jc4827w543";
|
||||
}
|
||||
|
||||
gpio_num_t board_pin_motor_en(void)
|
||||
{
|
||||
return GPIO_NUM_NC;
|
||||
}
|
||||
|
||||
int board_motor_en_disable_level(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
#include "driver/gpio.h"
|
||||
const char *board_name(void);
|
||||
gpio_num_t board_pin_motor_en(void);
|
||||
int board_motor_en_disable_level(void);
|
||||
@@ -0,0 +1 @@
|
||||
idf_component_register(SRCS "board.c" INCLUDE_DIRS "include" REQUIRES driver)
|
||||
@@ -0,0 +1,16 @@
|
||||
#include "board.h"
|
||||
|
||||
const char *board_name(void)
|
||||
{
|
||||
return "wroom";
|
||||
}
|
||||
|
||||
gpio_num_t board_pin_motor_en(void)
|
||||
{
|
||||
return (gpio_num_t)27;
|
||||
}
|
||||
|
||||
int board_motor_en_disable_level(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
#include "driver/gpio.h"
|
||||
const char *board_name(void);
|
||||
gpio_num_t board_pin_motor_en(void);
|
||||
int board_motor_en_disable_level(void);
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
void hal_audio_init(void);
|
||||
void hal_audio_beep_short(void);
|
||||
void hal_audio_alarm_complete(void);
|
||||
void hal_audio_alarm_cancel(void);
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#ifdef AUTOFILM_HOST
|
||||
#include "esp_host_compat.h"
|
||||
#else
|
||||
#include "esp_err.h"
|
||||
#endif
|
||||
|
||||
void hal_motor_init(void);
|
||||
void hal_motor_enable(bool on); /* on=false → EN disabled (HIGH on wroom) */
|
||||
void hal_motor_request_stop(void); /* must be safe to call anytime; sets flag + enable false */
|
||||
esp_err_t hal_motor_agitate_start(float cw_revs, float ccw_revs, uint32_t rpm);
|
||||
void hal_motor_agitate_stop(void); /* leave disabled */
|
||||
bool hal_motor_is_enabled(void);
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef AUTOFILM_HOST
|
||||
#include "esp_host_compat.h"
|
||||
#else
|
||||
#include "esp_err.h"
|
||||
#endif
|
||||
|
||||
void hal_temp_init(void);
|
||||
esp_err_t hal_temp_read_c(float *out); /* ESP_FAIL → disconnected */
|
||||
@@ -0,0 +1 @@
|
||||
idf_component_register(INCLUDE_DIRS "include")
|
||||
@@ -0,0 +1,63 @@
|
||||
#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;
|
||||
@@ -33,15 +33,19 @@ If blocked: stop, commit nothing broken, write `BLOCKED:` at top of the phase fi
|
||||
| ID | File | Session goal | STATUS |
|
||||
| --- | --- | --- | --- |
|
||||
| P00 | `refactor/P00-docs-lock.md` | Architecture docs already on branch | DONE |
|
||||
| P01 | `refactor/P01-idf-skeleton.md` | Dual-board IDF project compiles | TODO |
|
||||
| P02 | `refactor/P02-ui-cmd-process.md` | `ui_cmd` + `app_process` + golden tests | TODO |
|
||||
| P03 | `refactor/P03-machine-host.md` | `app_machine` + stub HAL + SM tests | TODO |
|
||||
| P04 | `refactor/P04-gitea-ci.md` | `.gitea/workflows/ci.yml` live | TODO |
|
||||
| P05 | `refactor/P05-hal-wroom-motion.md` | WROOM motor/temp/audio HAL | TODO |
|
||||
| P01 | `refactor/P01-idf-skeleton.md` | Dual-board IDF project compiles | BLOCKED |
|
||||
| P02 | `refactor/P02-ui-cmd-process.md` | `ui_cmd` + `app_process` + golden tests | DONE |
|
||||
| P03 | `refactor/P03-machine-host.md` | `app_machine` + stub HAL + SM tests | DONE |
|
||||
| P04 | `refactor/P04-gitea-ci.md` | `.gitea/workflows/ci.yml` live | DONE |
|
||||
| P05 | `refactor/P05-hal-wroom-motion.md` | WROOM motor/temp/audio HAL | BLOCKED |
|
||||
| P06 | `refactor/P06-ui-wroom-cutover.md` | 2004 UI + keypad + Stop/Resume cutover | TODO |
|
||||
| P07 | `refactor/P07-board-s3-ui.md` | JC4827W543 display+GT911+STOP | TODO |
|
||||
| P08 | `refactor/P08-idf-debt.md` | Replace Arduino drivers (RMT etc.) | TODO |
|
||||
|
||||
P04 Notes: merge_bin uses `${{ gitea.sha }}` (not `github.sha`); runner unverified (no Actions run).
|
||||
|
||||
P05 Notes: BLOCKED — no app_machine + HAL headers from P03 (branch @ 415c13a). Not implemented.
|
||||
|
||||
## Dependency
|
||||
|
||||
```
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# P01 — IDF dual-board skeleton
|
||||
|
||||
STATUS: TODO
|
||||
STATUS: BLOCKED
|
||||
BLOCKED: no IDF (IDF_PATH unset; no idf.py; no docker.io/espressif/idf:v5.4). Sources landed; builds not run; no binaries faked.
|
||||
DEPENDS: P00
|
||||
READ: `AGENTS.md`, `docs/megaplans/REFACTOR-MEGAPLAN.md`, this file
|
||||
OUT: recipes, UI, motor, Arduino-as-component, Gitea workflow, touching `src/` behaviour
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# P02 — ui_cmd + app_process + golden tests
|
||||
|
||||
STATUS: TODO
|
||||
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
|
||||
@@ -191,7 +191,7 @@ All golden tests pass.
|
||||
|
||||
## DoD
|
||||
|
||||
- [ ] Headers Arduino-free
|
||||
- [ ] Every cell in golden table asserted
|
||||
- [ ] Overlay does not mutate const steps
|
||||
- [ ] STATUS→DONE
|
||||
- [x] Headers Arduino-free
|
||||
- [x] Every cell in golden table asserted
|
||||
- [x] Overlay does not mutate const steps
|
||||
- [x] STATUS→DONE
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# P03 — app_machine + stub HAL + state tests
|
||||
|
||||
STATUS: TODO
|
||||
STATUS: DONE
|
||||
DEPENDS: P02
|
||||
READ: this file, `docs/megaplans/REFACTOR-MEGAPLAN.md`, `components/ui_cmd/include/ui_cmd.h`
|
||||
OUT: real GPIO, Arduino, LCD, Gitea yaml, deleting `src/menu.cpp`
|
||||
@@ -130,7 +130,7 @@ Required cases in `test_machine.c` names:
|
||||
|
||||
## DoD
|
||||
|
||||
- [ ] STOP path calls `hal_motor_request_stop` before any other work
|
||||
- [ ] default auto_advance false
|
||||
- [ ] host tests cover list above
|
||||
- [ ] STATUS→DONE
|
||||
- [x] STOP path calls `hal_motor_request_stop` before any other work
|
||||
- [x] default auto_advance false
|
||||
- [x] host tests cover list above
|
||||
- [x] STATUS→DONE
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# P04 — Gitea Actions CI
|
||||
|
||||
STATUS: TODO
|
||||
STATUS: DONE
|
||||
Notes: used ${{ gitea.sha }} in merge_bin output name (CICD example); github.sha not substituted. Runner unverified (no Actions run). No required-check flip (P01 BLOCKED; images not confirmed compiling). Release-attach job skipped.
|
||||
DEPENDS: P01 (required), P02/P03 (tests job becomes real when `tests/host/CMakeLists.txt` exists)
|
||||
READ: this file, `docs/CICD.md`
|
||||
OUT: changing firmware sources; enabling branch protection yourself if you lack repo admin — then note BLOCKED for humans
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
BLOCKED: P03 artifacts missing on branch refactor/esp-idf-modular-ui @ 415c13a.
|
||||
|
||||
Required and absent:
|
||||
- components/app_machine/ (include/app_machine.h, app_machine.c)
|
||||
- components/hal_motor/include/hal_motor.h
|
||||
- components/hal_temp/include/hal_temp.h
|
||||
- components/hal_audio/include/hal_audio.h
|
||||
|
||||
Megaplan table still lists P03 STATUS=TODO. Protocol: P05 requires P03 APIs.
|
||||
Do not invent or change public HAL/app_machine APIs. Do not implement P01–P04 in this session.
|
||||
No device HAL, tasks, Arduino-as-component, or main.c cutover landed.
|
||||
|
||||
# P05 — WROOM HAL motor / temp / audio
|
||||
|
||||
STATUS: TODO
|
||||
STATUS: BLOCKED
|
||||
DEPENDS: P03
|
||||
READ: this file, `include/config.h`, `src/motor.cpp`, `src/temperature.cpp`, `src/sound.cpp`, `components/hal_*/include/*.h`
|
||||
OUT: rewriting `app_ui` / deleting menus; S3 motor pins; disabling WDT; `vTaskDelete` motor
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
if(NOT DEFINED AUTOFILM_BOARD)
|
||||
set(AUTOFILM_BOARD wroom CACHE STRING "wroom|jc4827w543")
|
||||
endif()
|
||||
if(AUTOFILM_BOARD STREQUAL "wroom")
|
||||
set(BOARD_COMP board_wroom)
|
||||
elseif(AUTOFILM_BOARD STREQUAL "jc4827w543")
|
||||
set(BOARD_COMP board_jc4827w543)
|
||||
else()
|
||||
message(FATAL_ERROR "AUTOFILM_BOARD=${AUTOFILM_BOARD}")
|
||||
endif()
|
||||
idf_component_register(SRCS "main.c" INCLUDE_DIRS "." REQUIRES ${BOARD_COMP} driver)
|
||||
@@ -0,0 +1 @@
|
||||
dependencies: {}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#include "board.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
static const char *TAG = "app";
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "board=%s", board_name());
|
||||
|
||||
gpio_num_t en_pin = board_pin_motor_en();
|
||||
if (en_pin != GPIO_NUM_NC) {
|
||||
gpio_config_t io = {
|
||||
.pin_bit_mask = 1ULL << en_pin,
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
};
|
||||
gpio_config(&io);
|
||||
gpio_set_level(en_pin, board_motor_en_disable_level());
|
||||
}
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
CONFIG_ESP_TASK_WDT_EN=y
|
||||
CONFIG_ESP_TASK_WDT_TIMEOUT_S=10
|
||||
CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y
|
||||
@@ -0,0 +1,4 @@
|
||||
CONFIG_IDF_TARGET="esp32s3"
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_SPIRAM_MODE_OCT=y
|
||||
CONFIG_SPIRAM_SPEED_80M=y
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
@@ -0,0 +1,46 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(autofilm_host_tests C)
|
||||
|
||||
enable_testing()
|
||||
|
||||
set(HOST_INCLUDES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../components/app_process/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../components/app_machine/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../components/ui_cmd/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../components/hal_motor/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../components/hal_temp/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../components/hal_audio/include
|
||||
)
|
||||
|
||||
add_executable(test_process
|
||||
test_process.c
|
||||
main.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../components/app_process/app_process.c
|
||||
)
|
||||
|
||||
target_include_directories(test_process PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../components/app_process/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../components/ui_cmd/include
|
||||
)
|
||||
|
||||
target_compile_definitions(test_process PRIVATE AUTOFILM_HOST=1)
|
||||
target_compile_features(test_process PRIVATE c_std_11)
|
||||
target_link_libraries(test_process PRIVATE m)
|
||||
add_test(NAME test_process COMMAND test_process)
|
||||
|
||||
add_executable(test_machine
|
||||
test_machine.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../components/app_process/app_process.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../components/app_machine/app_machine.c
|
||||
stubs/hal_motor.c
|
||||
stubs/hal_temp.c
|
||||
stubs/hal_audio.c
|
||||
)
|
||||
|
||||
target_include_directories(test_machine PRIVATE ${HOST_INCLUDES})
|
||||
target_compile_definitions(test_machine PRIVATE AUTOFILM_HOST=1)
|
||||
target_compile_features(test_machine PRIVATE c_std_11)
|
||||
target_link_libraries(test_machine PRIVATE m)
|
||||
add_test(NAME test_machine COMMAND test_machine)
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
typedef int esp_err_t;
|
||||
#define ESP_OK 0
|
||||
#define ESP_FAIL -1
|
||||
#define ESP_ERR_NOT_FOUND 0x105
|
||||
#define ESP_ERR_INVALID_ARG 0x102
|
||||
@@ -0,0 +1,6 @@
|
||||
int test_process_run(void);
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return test_process_run();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "hal_audio.h"
|
||||
|
||||
int stub_beep_count;
|
||||
int stub_alarm_count;
|
||||
int stub_alarm_cancel_count;
|
||||
|
||||
void hal_audio_init(void)
|
||||
{
|
||||
stub_beep_count = 0;
|
||||
stub_alarm_count = 0;
|
||||
stub_alarm_cancel_count = 0;
|
||||
}
|
||||
|
||||
void hal_audio_beep_short(void)
|
||||
{
|
||||
stub_beep_count++;
|
||||
}
|
||||
|
||||
void hal_audio_alarm_complete(void)
|
||||
{
|
||||
stub_alarm_count++;
|
||||
}
|
||||
|
||||
void hal_audio_alarm_cancel(void)
|
||||
{
|
||||
stub_alarm_cancel_count++;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "hal_motor.h"
|
||||
|
||||
bool stub_motor_enabled;
|
||||
int stub_stop_count;
|
||||
int stub_agitate_start_count;
|
||||
int stub_agitate_stop_count;
|
||||
int stub_enable_count;
|
||||
float stub_last_cw;
|
||||
float stub_last_ccw;
|
||||
uint32_t stub_last_rpm;
|
||||
|
||||
void hal_motor_init(void)
|
||||
{
|
||||
stub_motor_enabled = false;
|
||||
stub_stop_count = 0;
|
||||
stub_agitate_start_count = 0;
|
||||
stub_agitate_stop_count = 0;
|
||||
stub_enable_count = 0;
|
||||
stub_last_cw = 0.f;
|
||||
stub_last_ccw = 0.f;
|
||||
stub_last_rpm = 0;
|
||||
}
|
||||
|
||||
void hal_motor_enable(bool on)
|
||||
{
|
||||
stub_enable_count++;
|
||||
stub_motor_enabled = on;
|
||||
}
|
||||
|
||||
void hal_motor_request_stop(void)
|
||||
{
|
||||
stub_stop_count++;
|
||||
stub_motor_enabled = false;
|
||||
}
|
||||
|
||||
esp_err_t hal_motor_agitate_start(float cw_revs, float ccw_revs, uint32_t rpm)
|
||||
{
|
||||
stub_agitate_start_count++;
|
||||
stub_last_cw = cw_revs;
|
||||
stub_last_ccw = ccw_revs;
|
||||
stub_last_rpm = rpm;
|
||||
stub_motor_enabled = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void hal_motor_agitate_stop(void)
|
||||
{
|
||||
stub_agitate_stop_count++;
|
||||
stub_motor_enabled = false;
|
||||
}
|
||||
|
||||
bool hal_motor_is_enabled(void)
|
||||
{
|
||||
return stub_motor_enabled;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "hal_temp.h"
|
||||
#include <stddef.h>
|
||||
|
||||
int stub_temp_fail;
|
||||
float stub_temp_c = 20.0f;
|
||||
|
||||
void hal_temp_init(void)
|
||||
{
|
||||
stub_temp_fail = 0;
|
||||
stub_temp_c = 20.0f;
|
||||
}
|
||||
|
||||
esp_err_t hal_temp_read_c(float *out)
|
||||
{
|
||||
if (stub_temp_fail) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (out != NULL) {
|
||||
*out = stub_temp_c;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
#include "app_machine.h"
|
||||
#include "app_process.h"
|
||||
#include "hal_motor.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
extern bool stub_motor_enabled;
|
||||
extern int stub_stop_count;
|
||||
extern int stub_agitate_start_count;
|
||||
extern int stub_beep_count;
|
||||
extern int stub_alarm_count;
|
||||
extern int stub_alarm_cancel_count;
|
||||
|
||||
static int g_failures;
|
||||
|
||||
static void fail(const char *msg)
|
||||
{
|
||||
fprintf(stderr, "FAIL: %s\n", msg);
|
||||
g_failures++;
|
||||
}
|
||||
|
||||
static void expect_int(int got, int want, const char *msg)
|
||||
{
|
||||
if (got != want) {
|
||||
fprintf(stderr, "FAIL: %s got=%d want=%d\n", msg, got, want);
|
||||
g_failures++;
|
||||
}
|
||||
}
|
||||
|
||||
static void expect_u32(uint32_t got, uint32_t want, const char *msg)
|
||||
{
|
||||
if (got != want) {
|
||||
fprintf(stderr, "FAIL: %s got=%u want=%u\n", msg, got, want);
|
||||
g_failures++;
|
||||
}
|
||||
}
|
||||
|
||||
static void drain_events(void)
|
||||
{
|
||||
ui_evt_t ev;
|
||||
while (app_machine_last_event(&ev) == 1) {
|
||||
}
|
||||
}
|
||||
|
||||
static int pop_ids(ui_evt_id_t *ids, int max)
|
||||
{
|
||||
int n = 0;
|
||||
ui_evt_t ev;
|
||||
while (n < max && app_machine_last_event(&ev) == 1) {
|
||||
ids[n++] = ev.id;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static bool has_id(const ui_evt_id_t *ids, int n, ui_evt_id_t id)
|
||||
{
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (ids[i] == id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static ui_cmd_t cmd_select(uint8_t proc)
|
||||
{
|
||||
ui_cmd_t c;
|
||||
memset(&c, 0, sizeof(c));
|
||||
c.id = CMD_SELECT_PROCESS;
|
||||
c.process_id = proc;
|
||||
return c;
|
||||
}
|
||||
|
||||
static ui_cmd_t cmd_arm(uint8_t step)
|
||||
{
|
||||
ui_cmd_t c;
|
||||
memset(&c, 0, sizeof(c));
|
||||
c.id = CMD_ARM_STEP;
|
||||
c.step_index = step;
|
||||
return c;
|
||||
}
|
||||
|
||||
static ui_cmd_t cmd_start(uint8_t step)
|
||||
{
|
||||
ui_cmd_t c;
|
||||
memset(&c, 0, sizeof(c));
|
||||
c.id = CMD_START_STEP;
|
||||
c.step_index = step;
|
||||
return c;
|
||||
}
|
||||
|
||||
static ui_cmd_t cmd_id(ui_cmd_id_t id)
|
||||
{
|
||||
ui_cmd_t c;
|
||||
memset(&c, 0, sizeof(c));
|
||||
c.id = id;
|
||||
return c;
|
||||
}
|
||||
|
||||
static void select_c41_step_view(void)
|
||||
{
|
||||
app_machine_init();
|
||||
ui_cmd_t c = cmd_select(PROC_C41);
|
||||
app_machine_handle_cmd(&c);
|
||||
expect_int((int)app_machine_state(), ST_STEP_SELECT, "select_c41_step_view state");
|
||||
expect_int((int)app_machine_process_id(), PROC_C41, "select_c41_step_view proc");
|
||||
expect_int((int)app_machine_step_index(), 0, "select_c41_step_view step");
|
||||
ui_evt_id_t ids[8];
|
||||
int n = pop_ids(ids, 8);
|
||||
if (!has_id(ids, n, EVT_PROCESS_SELECTED) || !has_id(ids, n, EVT_STEP_VIEW)) {
|
||||
fail("select_c41_step_view events");
|
||||
}
|
||||
}
|
||||
|
||||
static void adjust_does_not_mutate_const(void)
|
||||
{
|
||||
app_machine_init();
|
||||
ui_cmd_t c = cmd_select(PROC_C41);
|
||||
app_machine_handle_cmd(&c);
|
||||
ui_cmd_t adj;
|
||||
memset(&adj, 0, sizeof(adj));
|
||||
adj.id = CMD_ADJUST_STEP_TIME;
|
||||
adj.time_delta_s = 5;
|
||||
app_machine_handle_cmd(&adj);
|
||||
const process_def_t *p = app_process_get(PROC_C41);
|
||||
expect_int((int)p->steps[0].time_s, 180, "adjust_does_not_mutate_const const time");
|
||||
expect_int((int)app_process_time_s(PROC_C41, 0), 185, "adjust_does_not_mutate_const overlay");
|
||||
}
|
||||
|
||||
static void arm_start_complete_custom_10s(void)
|
||||
{
|
||||
app_machine_init();
|
||||
ui_cmd_t c = cmd_select(PROC_CUSTOM);
|
||||
app_machine_handle_cmd(&c);
|
||||
ui_cmd_t arm = cmd_arm(0);
|
||||
app_machine_handle_cmd(&arm);
|
||||
expect_int((int)app_machine_state(), ST_ARMED, "custom arm state");
|
||||
ui_cmd_t start = cmd_start(0);
|
||||
app_machine_handle_cmd(&start);
|
||||
expect_int((int)app_machine_state(), ST_RUNNING, "custom start state");
|
||||
expect_int(stub_motor_enabled ? 1 : 0, 1, "custom motor on");
|
||||
app_machine_tick(0);
|
||||
expect_int((int)app_machine_state(), ST_RUNNING, "custom tick0");
|
||||
app_machine_tick(9999);
|
||||
expect_int((int)app_machine_state(), ST_RUNNING, "custom tick9999");
|
||||
app_machine_tick(10000);
|
||||
expect_int((int)app_machine_state(), ST_COMPLETE, "custom complete");
|
||||
expect_int(stub_motor_enabled ? 1 : 0, 0, "custom motor off complete");
|
||||
expect_int(stub_alarm_count >= 1 ? 1 : 0, 1, "custom alarm");
|
||||
/* default auto_advance false */
|
||||
app_machine_tick(10001);
|
||||
expect_int((int)app_machine_state(), ST_COMPLETE, "auto_advance stays complete");
|
||||
}
|
||||
|
||||
static void stop_disables_motor_and_resume(void)
|
||||
{
|
||||
app_machine_init();
|
||||
ui_cmd_t c = cmd_select(PROC_CUSTOM);
|
||||
app_machine_handle_cmd(&c);
|
||||
ui_cmd_t arm = cmd_arm(0);
|
||||
app_machine_handle_cmd(&arm);
|
||||
ui_cmd_t start = cmd_start(0);
|
||||
app_machine_handle_cmd(&start);
|
||||
app_machine_tick(0);
|
||||
app_machine_tick(1000);
|
||||
int stops_before = stub_stop_count;
|
||||
ui_cmd_t stop = cmd_id(CMD_STOP);
|
||||
app_machine_handle_cmd(&stop);
|
||||
expect_int((int)app_machine_state(), ST_STOPPED, "stop state");
|
||||
expect_int(stub_motor_enabled ? 1 : 0, 0, "stop motor disabled");
|
||||
expect_int(stub_stop_count >= stops_before + 1 ? 1 : 0, 1, "stop_count");
|
||||
uint32_t rem = app_machine_remaining_ms();
|
||||
if (rem < 8000 || rem > 10000) {
|
||||
fprintf(stderr, "FAIL: remaining after stop got=%u\n", rem);
|
||||
g_failures++;
|
||||
}
|
||||
int beeps = stub_beep_count;
|
||||
ui_cmd_t resume = cmd_id(CMD_RESUME);
|
||||
app_machine_handle_cmd(&resume);
|
||||
expect_int((int)app_machine_state(), ST_RUNNING, "resume state");
|
||||
expect_int(stub_beep_count >= beeps + 1 ? 1 : 0, 1, "resume beep");
|
||||
app_machine_tick(1000);
|
||||
app_machine_tick(1000 + rem);
|
||||
expect_int((int)app_machine_state(), ST_COMPLETE, "resume then complete");
|
||||
}
|
||||
|
||||
static void return_from_stopped(void)
|
||||
{
|
||||
app_machine_init();
|
||||
ui_cmd_t c = cmd_select(PROC_CUSTOM);
|
||||
app_machine_handle_cmd(&c);
|
||||
ui_cmd_t arm = cmd_arm(0);
|
||||
app_machine_handle_cmd(&arm);
|
||||
ui_cmd_t start = cmd_start(0);
|
||||
app_machine_handle_cmd(&start);
|
||||
app_machine_tick(0);
|
||||
app_machine_tick(1000);
|
||||
ui_cmd_t stop = cmd_id(CMD_STOP);
|
||||
app_machine_handle_cmd(&stop);
|
||||
uint32_t rem = app_machine_remaining_ms();
|
||||
ui_cmd_t ret = cmd_id(CMD_RETURN_TO_STEP_SELECT);
|
||||
app_machine_handle_cmd(&ret);
|
||||
expect_int((int)app_machine_state(), ST_STEP_SELECT, "return_from_stopped state");
|
||||
uint16_t overlay = app_process_time_s(PROC_CUSTOM, 0);
|
||||
uint16_t expect_s = (uint16_t)((rem + 999u) / 1000u);
|
||||
expect_int((int)overlay, (int)expect_s, "return_from_stopped overlay");
|
||||
expect_int((int)app_process_get(PROC_CUSTOM)->steps[0].time_s, 10, "const still 10");
|
||||
}
|
||||
|
||||
static void stop_during_complete_cancels_alarm(void)
|
||||
{
|
||||
app_machine_init();
|
||||
ui_cmd_t c = cmd_select(PROC_CUSTOM);
|
||||
app_machine_handle_cmd(&c);
|
||||
ui_cmd_t arm = cmd_arm(0);
|
||||
app_machine_handle_cmd(&arm);
|
||||
ui_cmd_t start = cmd_start(0);
|
||||
app_machine_handle_cmd(&start);
|
||||
app_machine_tick(0);
|
||||
app_machine_tick(10000);
|
||||
expect_int((int)app_machine_state(), ST_COMPLETE, "complete before stop");
|
||||
int cancels = stub_alarm_cancel_count;
|
||||
ui_cmd_t stop = cmd_id(CMD_STOP);
|
||||
app_machine_handle_cmd(&stop);
|
||||
expect_int((int)app_machine_state(), ST_STEP_SELECT, "stop from complete");
|
||||
expect_int(stub_alarm_cancel_count >= cancels + 1 ? 1 : 0, 1, "alarm cancel");
|
||||
}
|
||||
|
||||
static void ecn2_remjet_zero_time(void)
|
||||
{
|
||||
app_machine_init();
|
||||
ui_cmd_t c = cmd_select(PROC_ECN2);
|
||||
app_machine_handle_cmd(&c);
|
||||
ui_cmd_t browse;
|
||||
memset(&browse, 0, sizeof(browse));
|
||||
browse.id = CMD_BROWSE_STEP;
|
||||
browse.step_delta = 1;
|
||||
app_machine_handle_cmd(&browse);
|
||||
expect_int((int)app_machine_step_index(), 1, "remjet index");
|
||||
ui_cmd_t arm = cmd_arm(1);
|
||||
app_machine_handle_cmd(&arm);
|
||||
int starts = stub_agitate_start_count;
|
||||
ui_cmd_t start = cmd_start(1);
|
||||
app_machine_handle_cmd(&start);
|
||||
expect_int(stub_motor_enabled ? 1 : 0, 0, "remjet no enable");
|
||||
expect_int(stub_agitate_start_count, starts, "remjet no agitate");
|
||||
app_machine_tick(0);
|
||||
expect_int((int)app_machine_state(), ST_COMPLETE, "remjet complete next tick");
|
||||
}
|
||||
|
||||
static void stop_ignored_meaningless_in_idle(void)
|
||||
{
|
||||
app_machine_init();
|
||||
int stops = stub_stop_count;
|
||||
int enables = stub_motor_enabled ? 1 : 0;
|
||||
ui_cmd_t stop = cmd_id(CMD_STOP);
|
||||
app_machine_handle_cmd(&stop);
|
||||
expect_int((int)app_machine_state(), ST_IDLE, "idle stays idle");
|
||||
expect_int(stub_stop_count, stops, "idle no request_stop required");
|
||||
(void)enables;
|
||||
}
|
||||
|
||||
static void c41_clock(void)
|
||||
{
|
||||
app_machine_init();
|
||||
ui_cmd_t c = cmd_select(PROC_C41);
|
||||
app_machine_handle_cmd(&c);
|
||||
ui_cmd_t arm = cmd_arm(0);
|
||||
app_machine_handle_cmd(&arm);
|
||||
ui_cmd_t start = cmd_start(0);
|
||||
app_machine_handle_cmd(&start);
|
||||
app_machine_tick(0);
|
||||
expect_int((int)app_machine_state(), ST_RUNNING, "c41 tick0");
|
||||
app_machine_tick(179999);
|
||||
expect_int((int)app_machine_state(), ST_RUNNING, "c41 tick179999");
|
||||
app_machine_tick(180000);
|
||||
expect_int((int)app_machine_state(), ST_COMPLETE, "c41 tick180000");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
select_c41_step_view();
|
||||
adjust_does_not_mutate_const();
|
||||
arm_start_complete_custom_10s();
|
||||
stop_disables_motor_and_resume();
|
||||
return_from_stopped();
|
||||
stop_during_complete_cancels_alarm();
|
||||
ecn2_remjet_zero_time();
|
||||
stop_ignored_meaningless_in_idle();
|
||||
c41_clock();
|
||||
|
||||
if (g_failures == 0) {
|
||||
printf("test_machine: all assertions passed\n");
|
||||
return 0;
|
||||
}
|
||||
fprintf(stderr, "test_machine: %d failure(s)\n", g_failures);
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
#include "app_process.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static int g_failures;
|
||||
|
||||
static void fail(const char *msg)
|
||||
{
|
||||
fprintf(stderr, "FAIL: %s\n", msg);
|
||||
g_failures++;
|
||||
}
|
||||
|
||||
static void expect_ptr_null(const void *p, const char *msg)
|
||||
{
|
||||
if (p != NULL) {
|
||||
fail(msg);
|
||||
}
|
||||
}
|
||||
|
||||
static void expect_ptr_nonnull(const void *p, const char *msg)
|
||||
{
|
||||
if (p == NULL) {
|
||||
fail(msg);
|
||||
}
|
||||
}
|
||||
|
||||
static void expect_str(const char *got, const char *want, const char *msg)
|
||||
{
|
||||
if (got == NULL || want == NULL || strcmp(got, want) != 0) {
|
||||
fprintf(stderr, "FAIL: %s got=\"%s\" want=\"%s\"\n",
|
||||
msg, got ? got : "(null)", want ? want : "(null)");
|
||||
g_failures++;
|
||||
}
|
||||
}
|
||||
|
||||
static void expect_u8(uint8_t got, uint8_t want, const char *msg)
|
||||
{
|
||||
if (got != want) {
|
||||
fprintf(stderr, "FAIL: %s got=%u want=%u\n", msg, got, want);
|
||||
g_failures++;
|
||||
}
|
||||
}
|
||||
|
||||
static void expect_u16(uint16_t got, uint16_t want, const char *msg)
|
||||
{
|
||||
if (got != want) {
|
||||
fprintf(stderr, "FAIL: %s got=%u want=%u\n", msg, got, want);
|
||||
g_failures++;
|
||||
}
|
||||
}
|
||||
|
||||
static void expect_f(float got, float want, const char *msg)
|
||||
{
|
||||
if (fabsf(got - want) >= 1e-4f) {
|
||||
fprintf(stderr, "FAIL: %s got=%f want=%f\n", msg, (double)got, (double)want);
|
||||
g_failures++;
|
||||
}
|
||||
}
|
||||
|
||||
static void expect_err(esp_err_t got, esp_err_t want, const char *msg)
|
||||
{
|
||||
if (got != want) {
|
||||
fprintf(stderr, "FAIL: %s got=%d want=%d\n", msg, got, want);
|
||||
g_failures++;
|
||||
}
|
||||
}
|
||||
|
||||
static void check_step(const process_def_t *p, uint8_t i,
|
||||
const char *name, uint16_t t,
|
||||
float cw, float ccw,
|
||||
float tmin, float tpref, float tmax)
|
||||
{
|
||||
char buf[96];
|
||||
snprintf(buf, sizeof(buf), "%s[%u].name", p->name, i);
|
||||
expect_str(p->steps[i].name, name, buf);
|
||||
snprintf(buf, sizeof(buf), "%s[%u].time_s", p->name, i);
|
||||
expect_u16(p->steps[i].time_s, t, buf);
|
||||
snprintf(buf, sizeof(buf), "%s[%u].cw", p->name, i);
|
||||
expect_f(p->steps[i].cw_revs, cw, buf);
|
||||
snprintf(buf, sizeof(buf), "%s[%u].ccw", p->name, i);
|
||||
expect_f(p->steps[i].ccw_revs, ccw, buf);
|
||||
snprintf(buf, sizeof(buf), "%s[%u].min", p->name, i);
|
||||
expect_f(p->steps[i].temp_min_c, tmin, buf);
|
||||
snprintf(buf, sizeof(buf), "%s[%u].pref", p->name, i);
|
||||
expect_f(p->steps[i].temp_pref_c, tpref, buf);
|
||||
snprintf(buf, sizeof(buf), "%s[%u].max", p->name, i);
|
||||
expect_f(p->steps[i].temp_max_c, tmax, buf);
|
||||
}
|
||||
|
||||
int test_process_run(void)
|
||||
{
|
||||
app_process_init();
|
||||
|
||||
expect_ptr_null(app_process_get(6), "get(6)");
|
||||
expect_ptr_null(app_process_find_name("nope"), "find_name nope");
|
||||
expect_ptr_null(app_process_find_name(NULL), "find_name NULL");
|
||||
|
||||
const process_def_t *c41 = app_process_get(PROC_C41);
|
||||
const process_def_t *e6 = app_process_get(PROC_E6);
|
||||
const process_def_t *ecn = app_process_get(PROC_ECN2);
|
||||
const process_def_t *bw = app_process_get(PROC_BW);
|
||||
const process_def_t *custom = app_process_get(PROC_CUSTOM);
|
||||
const process_def_t *bwrev = app_process_get(PROC_BWREV);
|
||||
|
||||
expect_ptr_nonnull(c41, "C41 get");
|
||||
expect_ptr_nonnull(e6, "E6 get");
|
||||
expect_ptr_nonnull(ecn, "ECN-2 get");
|
||||
expect_ptr_nonnull(bw, "B&W get");
|
||||
expect_ptr_nonnull(custom, "Custom get");
|
||||
expect_ptr_nonnull(bwrev, "B&WREV get");
|
||||
|
||||
expect_str(c41->name, "C41", "C41 name");
|
||||
expect_u8(c41->step_count, 7, "C41 count");
|
||||
check_step(c41, 0, "Prewarm", 180, 1.f, 1.f, 37.8f, 38.f, 38.2f);
|
||||
check_step(c41, 1, "Developer", 195, 5.5f, 5.f, 37.8f, 38.f, 38.2f);
|
||||
check_step(c41, 2, "Bleach", 45, 5.5f, 5.f, 32.f, 38.f, 38.2f);
|
||||
check_step(c41, 3, "Fix", 180, 5.5f, 5.f, 32.f, 38.f, 38.2f);
|
||||
check_step(c41, 4, "Rinse 1", 60, 3.5f, 3.f, 32.f, 38.f, 38.2f);
|
||||
check_step(c41, 5, "Rinse 2", 60, 3.5f, 3.f, 32.f, 38.f, 38.2f);
|
||||
check_step(c41, 6, "Fin Rinse", 30, 1.f, 1.f, 32.f, 38.f, 38.2f);
|
||||
|
||||
expect_str(e6->name, "E6", "E6 name");
|
||||
expect_u8(e6->step_count, 12, "E6 count");
|
||||
check_step(e6, 0, "Preheat", 180, 1.f, 1.f, 37.5f, 38.f, 38.5f);
|
||||
check_step(e6, 1, "FirstDev", 360, 5.5f, 5.f, 37.7f, 38.f, 38.3f);
|
||||
check_step(e6, 2, "Wash 1", 120, 3.5f, 3.f, 33.0f, 38.f, 38.0f);
|
||||
check_step(e6, 3, "Reversal", 120, 5.f, 5.5f, 37.7f, 38.f, 38.3f);
|
||||
check_step(e6, 4, "ColorDev", 360, 5.5f, 5.f, 37.0f, 38.f, 39.0f);
|
||||
check_step(e6, 5, "PreBleach", 120, 5.5f, 5.5f, 37.f, 38.f, 38.f);
|
||||
check_step(e6, 6, "Bleach", 360, 5.5f, 5.5f, 37.5f, 38.f, 38.5f);
|
||||
check_step(e6, 7, "Fixer", 240, 5.5f, 5.5f, 37.5f, 38.f, 38.5f);
|
||||
check_step(e6, 8, "Wash 2", 120, 3.5f, 3.f, 33.0f, 38.f, 38.5f);
|
||||
check_step(e6, 9, "Wash 3", 120, 3.5f, 3.f, 33.0f, 38.f, 38.5f);
|
||||
check_step(e6, 10, "Wash 4", 120, 3.5f, 3.f, 33.0f, 38.f, 38.5f);
|
||||
check_step(e6, 11, "Fin Rinse", 30, 3.5f, 3.f, 19.0f, 20.f, 21.0f);
|
||||
|
||||
expect_str(ecn->name, "ECN-2", "ECN-2 name");
|
||||
expect_u8(ecn->step_count, 9, "ECN-2 count");
|
||||
check_step(ecn, 0, "Prebath", 180, 1.f, 1.f, 27.f, 38.f, 38.f);
|
||||
check_step(ecn, 1, "RemJet", 0, 0.f, 0.f, 0.f, 0.f, 0.f);
|
||||
check_step(ecn, 2, "Developer", 210, 5.5f, 5.f, 40.8f, 41.f, 41.2f);
|
||||
check_step(ecn, 3, "Stop Bath", 60, 3.5f, 3.f, 27.f, 38.f, 38.f);
|
||||
check_step(ecn, 4, "Wash", 180, 3.5f, 3.f, 27.f, 38.f, 38.f);
|
||||
check_step(ecn, 5, "Bleach", 150, 5.5f, 5.f, 27.f, 38.f, 38.f);
|
||||
check_step(ecn, 6, "Fixer", 120, 5.5f, 5.f, 27.f, 38.f, 38.f);
|
||||
check_step(ecn, 7, "Wash 2", 300, 3.5f, 3.f, 27.f, 38.f, 38.f);
|
||||
check_step(ecn, 8, "Fin Rinse", 120, 1.f, 1.f, 27.f, 38.f, 38.f);
|
||||
|
||||
expect_str(bw->name, "B&W", "B&W name");
|
||||
expect_u8(bw->step_count, 7, "B&W count");
|
||||
check_step(bw, 0, "Developer", 510, 5.5f, 5.f, 19.f, 20.f, 21.f);
|
||||
check_step(bw, 1, "Stop", 30, 3.5f, 3.f, 19.f, 20.f, 21.f);
|
||||
check_step(bw, 2, "Fix", 300, 3.5f, 3.f, 19.f, 20.f, 21.f);
|
||||
check_step(bw, 3, "Rinse 1", 60, 3.5f, 3.f, 19.f, 20.f, 21.f);
|
||||
check_step(bw, 4, "Rinse 2", 90, 3.5f, 3.f, 19.f, 20.f, 21.f);
|
||||
check_step(bw, 5, "Rinse 3", 120, 3.5f, 3.f, 19.f, 20.f, 21.f);
|
||||
check_step(bw, 6, "Fin Rinse", 30, 1.f, 1.f, 19.f, 20.f, 21.f);
|
||||
|
||||
expect_str(custom->name, "Custom", "Custom name");
|
||||
expect_u8(custom->step_count, 4, "Custom count");
|
||||
check_step(custom, 0, "Developer", 10, 5.5f, 5.f, 19.f, 20.f, 21.f);
|
||||
check_step(custom, 1, "Stop", 10, 3.5f, 3.f, 19.f, 20.f, 21.f);
|
||||
check_step(custom, 2, "Fix", 10, 3.5f, 3.f, 19.f, 20.f, 21.f);
|
||||
check_step(custom, 3, "Rinse", 10, 3.5f, 3.f, 19.f, 20.f, 21.f);
|
||||
|
||||
expect_str(bwrev->name, "B&WREV", "B&WREV name");
|
||||
expect_u8(bwrev->step_count, 12, "B&WREV count");
|
||||
check_step(bwrev, 0, "FirstDev", 720, 5.5f, 5.f, 19.5f, 20.f, 20.5f);
|
||||
check_step(bwrev, 1, "Wash 1", 300, 3.5f, 3.f, 15.5f, 20.f, 22.5f);
|
||||
check_step(bwrev, 2, "Bleach", 300, 5.5f, 5.f, 19.5f, 20.f, 22.5f);
|
||||
check_step(bwrev, 3, "Wash 2", 60, 3.5f, 3.f, 15.5f, 20.f, 22.5f);
|
||||
check_step(bwrev, 4, "Clearing", 120, 5.5f, 5.f, 19.5f, 20.f, 22.5f);
|
||||
check_step(bwrev, 5, "Wash 3", 60, 3.5f, 3.f, 15.5f, 20.f, 22.5f);
|
||||
check_step(bwrev, 6, "Reversal", 120, 5.5f, 5.f, 19.5f, 20.f, 22.5f);
|
||||
check_step(bwrev, 7, "SecondDev", 360, 5.5f, 5.f, 19.5f, 20.f, 22.5f);
|
||||
check_step(bwrev, 8, "Wash 4", 60, 3.5f, 3.f, 15.5f, 20.f, 22.5f);
|
||||
check_step(bwrev, 9, "Fix", 300, 5.5f, 5.f, 19.5f, 20.f, 22.5f);
|
||||
check_step(bwrev, 10, "Wash 5", 60, 3.5f, 3.f, 15.5f, 20.f, 22.5f);
|
||||
check_step(bwrev, 11, "Fin Rinse", 60, 3.5f, 3.f, 15.5f, 20.f, 22.5f);
|
||||
|
||||
/* Fix broken overlay default check: compare via id, not pointer arithmetic */
|
||||
expect_u16(app_process_time_s(PROC_C41, 0), 180, "C41[0] time default");
|
||||
expect_u16(app_process_time_s(PROC_C41, 1), 195, "C41[1] time default");
|
||||
|
||||
static const uint8_t ids[PROCESS_COUNT] = {
|
||||
PROC_C41, PROC_E6, PROC_ECN2, PROC_BW, PROC_CUSTOM, PROC_BWREV
|
||||
};
|
||||
for (int i = 0; i < PROCESS_COUNT; i++) {
|
||||
const process_def_t *p = app_process_get(ids[i]);
|
||||
const process_def_t *by_name = app_process_find_name(p->name);
|
||||
if (by_name != p) {
|
||||
fprintf(stderr, "FAIL: find_name(%s) != get(%u)\n", p->name, ids[i]);
|
||||
g_failures++;
|
||||
}
|
||||
}
|
||||
|
||||
expect_err(app_process_adjust_time(PROC_C41, 1, 5), ESP_OK, "adjust C41 step1 +5");
|
||||
expect_u16(app_process_time_s(PROC_C41, 1), 200, "C41[1] after +5");
|
||||
expect_u16(c41->steps[1].time_s, 195, "const C41[1].time_s unchanged");
|
||||
app_process_reset_session(PROC_C41);
|
||||
expect_u16(app_process_time_s(PROC_C41, 1), 195, "C41[1] after reset");
|
||||
|
||||
expect_err(app_process_adjust_time(6, 0, 1), ESP_ERR_INVALID_ARG, "bad id");
|
||||
expect_err(app_process_adjust_time(PROC_C41, 7, 1), ESP_ERR_INVALID_ARG, "bad step");
|
||||
|
||||
if (g_failures == 0) {
|
||||
printf("test_process: all assertions passed\n");
|
||||
return 0;
|
||||
}
|
||||
fprintf(stderr, "test_process: %d failure(s)\n", g_failures);
|
||||
return 1;
|
||||
}
|
||||
Reference in New Issue
Block a user