firmware/update_fw.sh: исправления по ревью
- can_has_uuid: без pipefail-ловушки grep -q (flashtool -q падал по EPIPE) - убраны пред-проверки -q перед -r: Klipper с назначенным node id не отвечает на запрос UUID - tee игнорирует SIGINT, trap INT — по Ctrl-C klipper всё равно запускается - set -E: trap ERR срабатывает внутри функций; die печатает этап - preflight различает «Moonraker недоступен» и «klippy не ready» - host: проверка смены PID klipper-mcu и наличия /tmp/klipper_host_mcu - wait_for по реальному времени; klippy.log проверяется только после последнего старта Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+29
-16
@@ -24,7 +24,7 @@
|
|||||||
# rp_sb2209 = 350f10596216
|
# rp_sb2209 = 350f10596216
|
||||||
# sht36_v2 = 8ce295109ef0
|
# sht36_v2 = 8ce295109ef0
|
||||||
|
|
||||||
set -euo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
# ---------- Константы ----------
|
# ---------- Константы ----------
|
||||||
MOONRAKER="http://127.0.0.1:7125"
|
MOONRAKER="http://127.0.0.1:7125"
|
||||||
@@ -54,7 +54,7 @@ FORCE=0
|
|||||||
TARGETS=()
|
TARGETS=()
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
-h|--help) sed -n '2,15p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
-h|--help) sed -n '2,15p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
||||||
--build-only) BUILD_ONLY=1 ;;
|
--build-only) BUILD_ONLY=1 ;;
|
||||||
--force) FORCE=1 ;;
|
--force) FORCE=1 ;;
|
||||||
host|mcu|toolhead) TARGETS+=("$arg") ;;
|
host|mcu|toolhead) TARGETS+=("$arg") ;;
|
||||||
@@ -67,7 +67,7 @@ want() { local t; for t in "${TARGETS[@]}"; do [ "$t" = "$1" ] && return 0; done
|
|||||||
# ---------- Лог и трапы ----------
|
# ---------- Лог и трапы ----------
|
||||||
mkdir -p "$OUT_DIR"
|
mkdir -p "$OUT_DIR"
|
||||||
LOG="$OUT_DIR/update_fw-$(date +%Y%m%d-%H%M).log"
|
LOG="$OUT_DIR/update_fw-$(date +%Y%m%d-%H%M).log"
|
||||||
exec > >(tee -a "$LOG") 2>&1
|
exec > >(trap '' INT; exec tee -a "$LOG") 2>&1
|
||||||
START_TS=$(date +%s)
|
START_TS=$(date +%s)
|
||||||
STAGE="инициализация"
|
STAGE="инициализация"
|
||||||
KLIPPER_STOPPED=0
|
KLIPPER_STOPPED=0
|
||||||
@@ -88,22 +88,25 @@ on_exit() {
|
|||||||
}
|
}
|
||||||
trap on_err ERR
|
trap on_err ERR
|
||||||
trap on_exit EXIT
|
trap on_exit EXIT
|
||||||
|
trap 'exit 130' INT
|
||||||
|
|
||||||
section() { echo; echo "=== $* ==="; }
|
section() { echo; echo "=== $* ==="; }
|
||||||
die() { echo "!!! $*" >&2; exit 1; }
|
die() { echo "!!! [$STAGE] $*" >&2; exit 1; }
|
||||||
|
|
||||||
# Ждать, пока команда-условие не вернёт 0 (таймаут в секундах)
|
# Ждать, пока команда-условие не вернёт 0 (таймаут в секундах)
|
||||||
wait_for() {
|
wait_for() {
|
||||||
local timeout=$1 msg=$2; shift 2
|
local timeout=$1 msg=$2; shift 2
|
||||||
local i
|
local deadline=$((SECONDS + timeout))
|
||||||
for ((i = 0; i < timeout; i++)); do
|
while [ "$SECONDS" -lt "$deadline" ]; do
|
||||||
if "$@" >/dev/null 2>&1; then return 0; fi
|
if "$@" >/dev/null 2>&1; then return 0; fi
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
echo "!!! Таймаут ($timeout с): $msg" >&2
|
echo "!!! Таймаут ($timeout с): $msg" >&2
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
can_has_uuid() { python3 "$KATAPULT" -i "$CAN_IF" -q 2>/dev/null | grep -q "$1"; }
|
# Klipper отвечает на запрос UUID только пока ему не назначен node id (canserial.c),
|
||||||
|
# поэтому -q годится лишь для узлов, только что перезагруженных после прошивки.
|
||||||
|
can_has_uuid() { local out; out=$(python3 "$KATAPULT" -i "$CAN_IF" -q 2>/dev/null) || return 1; grep -q "$1" <<< "$out"; }
|
||||||
katapult_serial() { local d; for d in $KATAPULT_SERIAL_GLOB; do [ -e "$d" ] && { echo "$d"; return 0; }; done; return 1; }
|
katapult_serial() { local d; for d in $KATAPULT_SERIAL_GLOB; do [ -e "$d" ] && { echo "$d"; return 0; }; done; return 1; }
|
||||||
klippy_state() { curl -s --max-time 5 "$MOONRAKER/printer/info" | jq -r '.result.state // empty' 2>/dev/null; }
|
klippy_state() { curl -s --max-time 5 "$MOONRAKER/printer/info" | jq -r '.result.state // empty' 2>/dev/null; }
|
||||||
klippy_ready() { [ "$(klippy_state)" = "ready" ]; }
|
klippy_ready() { [ "$(klippy_state)" = "ready" ]; }
|
||||||
@@ -134,14 +137,17 @@ if [ "$BUILD_ONLY" = 0 ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Печать не должна идти
|
# Печать не должна идти
|
||||||
|
KSTATE=$(klippy_state || true)
|
||||||
PRINT_STATE=$(curl -s --max-time 5 "$MOONRAKER/printer/objects/query?print_stats" \
|
PRINT_STATE=$(curl -s --max-time 5 "$MOONRAKER/printer/objects/query?print_stats" \
|
||||||
| jq -r '.result.status.print_stats.state // empty' 2>/dev/null || true)
|
| jq -r '.result.status.print_stats.state // empty' 2>/dev/null || true)
|
||||||
if [ -z "$PRINT_STATE" ]; then
|
if [ -z "$KSTATE" ]; then
|
||||||
if [ "$FORCE" = 1 ]; then
|
if [ "$FORCE" = 1 ]; then
|
||||||
echo "Moonraker недоступен, проверка печати пропущена (--force)"
|
echo "Moonraker недоступен, проверка печати пропущена (--force)"
|
||||||
else
|
else
|
||||||
die "Moonraker недоступен — не могу проверить, идёт ли печать (--force чтобы пропустить)"
|
die "Moonraker недоступен — не могу проверить, идёт ли печать (--force чтобы пропустить)"
|
||||||
fi
|
fi
|
||||||
|
elif [ -z "$PRINT_STATE" ]; then
|
||||||
|
echo "klippy в состоянии '$KSTATE' (не ready) — печать не идёт, продолжаю"
|
||||||
else
|
else
|
||||||
echo "print_stats.state = $PRINT_STATE"
|
echo "print_stats.state = $PRINT_STATE"
|
||||||
case "$PRINT_STATE" in
|
case "$PRINT_STATE" in
|
||||||
@@ -198,20 +204,22 @@ if want host; then
|
|||||||
STAGE="прошивка host MCU"
|
STAGE="прошивка host MCU"
|
||||||
section "Прошивка host (klipper-mcu)"
|
section "Прошивка host (klipper-mcu)"
|
||||||
# flash-linux.sh копирует out/klipper.elf в /usr/local/bin/klipper_mcu и перезапускает klipper-mcu
|
# flash-linux.sh копирует out/klipper.elf в /usr/local/bin/klipper_mcu и перезапускает klipper-mcu
|
||||||
|
HOST_PID_OLD=$(systemctl show -p ExecMainPID --value klipper-mcu)
|
||||||
make -C "$KLIPPER" flash KCONFIG_CONFIG="$BUILD_TMP/$HOST_CFG"
|
make -C "$KLIPPER" flash KCONFIG_CONFIG="$BUILD_TMP/$HOST_CFG"
|
||||||
[ "$(stat -c %Y "$HOST_BIN")" -ge "$START_TS" ] || die "$HOST_BIN не обновился"
|
[ "$(stat -c %Y "$HOST_BIN")" -ge "$START_TS" ] || die "$HOST_BIN не обновился"
|
||||||
wait_for 10 "klipper-mcu не запустился" systemctl is-active --quiet klipper-mcu
|
host_restarted() { [ "$(systemctl show -p ExecMainPID --value klipper-mcu)" != "$HOST_PID_OLD" ] && [ -e /tmp/klipper_host_mcu ]; }
|
||||||
echo "klipper-mcu: $(systemctl is-active klipper-mcu), $HOST_BIN обновлён"
|
wait_for 10 "klipper-mcu не перезапустился (PID $HOST_PID_OLD)" host_restarted
|
||||||
|
echo "klipper-mcu перезапущен (PID $HOST_PID_OLD -> $(systemctl show -p ExecMainPID --value klipper-mcu)), $HOST_BIN обновлён"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if want mcu; then
|
if want mcu; then
|
||||||
STAGE="прошивка Octopus Pro"
|
STAGE="прошивка Octopus Pro"
|
||||||
section "Прошивка mcu (Octopus Pro, uuid $MCU_UUID)"
|
section "Прошивка mcu (Octopus Pro, uuid $MCU_UUID)"
|
||||||
can_has_uuid "$MCU_UUID" || die "Octopus ($MCU_UUID) не виден на $CAN_IF"
|
|
||||||
python3 "$KATAPULT" -i "$CAN_IF" -u "$MCU_UUID" -r
|
python3 "$KATAPULT" -i "$CAN_IF" -u "$MCU_UUID" -r
|
||||||
wait_for 20 "не появился katapult serial ($KATAPULT_SERIAL_GLOB). Проверьте USB или перезагрузите плату" katapult_serial
|
wait_for 20 "не появился katapult serial ($KATAPULT_SERIAL_GLOB). Проверьте USB или перезагрузите плату" katapult_serial
|
||||||
DEV=$(katapult_serial)
|
DEV=$(katapult_serial)
|
||||||
echo "katapult serial: $DEV"
|
echo "katapult serial: $DEV"
|
||||||
|
sleep 1 # порт может быть ещё не готов сразу после появления симлинка
|
||||||
python3 "$KATAPULT" -d "$DEV" -f "$OUT_DIR/mcu_klipper.bin"
|
python3 "$KATAPULT" -d "$DEV" -f "$OUT_DIR/mcu_klipper.bin"
|
||||||
# После прошивки плата перезагружается в Klipper и снова поднимает USB-CAN мост
|
# После прошивки плата перезагружается в Klipper и снова поднимает USB-CAN мост
|
||||||
wait_for 30 "Octopus не вернулся на $CAN_IF после прошивки" can_has_uuid "$MCU_UUID"
|
wait_for 30 "Octopus не вернулся на $CAN_IF после прошивки" can_has_uuid "$MCU_UUID"
|
||||||
@@ -221,7 +229,7 @@ fi
|
|||||||
if want toolhead; then
|
if want toolhead; then
|
||||||
STAGE="прошивка тулхеда"
|
STAGE="прошивка тулхеда"
|
||||||
section "Прошивка toolhead (EBB SB2209, uuid $TOOLHEAD_UUID)"
|
section "Прошивка toolhead (EBB SB2209, uuid $TOOLHEAD_UUID)"
|
||||||
can_has_uuid "$TOOLHEAD_UUID" || die "Тулхед ($TOOLHEAD_UUID) не виден на $CAN_IF"
|
# flashtool сам отправляет запрос бутлоадера по UUID и заливает прошивку
|
||||||
python3 "$KATAPULT" -i "$CAN_IF" -u "$TOOLHEAD_UUID" -f "$OUT_DIR/toolhead_klipper.bin"
|
python3 "$KATAPULT" -i "$CAN_IF" -u "$TOOLHEAD_UUID" -f "$OUT_DIR/toolhead_klipper.bin"
|
||||||
wait_for 30 "тулхед не вернулся на $CAN_IF после прошивки" can_has_uuid "$TOOLHEAD_UUID"
|
wait_for 30 "тулхед не вернулся на $CAN_IF после прошивки" can_has_uuid "$TOOLHEAD_UUID"
|
||||||
echo "Тулхед снова на $CAN_IF"
|
echo "Тулхед снова на $CAN_IF"
|
||||||
@@ -250,15 +258,20 @@ section "Версии"
|
|||||||
KLIPPY_VER=$(curl -s --max-time 5 "$MOONRAKER/printer/info" | jq -r '.result.software_version // "?"' 2>/dev/null || echo "?")
|
KLIPPY_VER=$(curl -s --max-time 5 "$MOONRAKER/printer/info" | jq -r '.result.software_version // "?"' 2>/dev/null || echo "?")
|
||||||
echo "klippy (хост): $KLIPPY_VER"
|
echo "klippy (хост): $KLIPPY_VER"
|
||||||
AFTER=$(mcu_versions)
|
AFTER=$(mcu_versions)
|
||||||
printf '%-12s %-32s %s\n' "MCU" "было" "стало"
|
|
||||||
MISMATCH=0
|
MISMATCH=0
|
||||||
while IFS=$'\t' read -r name ver _; do
|
if [ -z "$AFTER" ] || [ "$AFTER" = "недоступно" ]; then
|
||||||
|
echo "!!! Версии MCU недоступны (klippy не ready)"; MISMATCH=1
|
||||||
|
else
|
||||||
|
printf '%-12s %-32s %s\n' "MCU" "было" "стало"
|
||||||
|
while IFS=$'\t' read -r name ver _; do
|
||||||
old=$(printf '%s\n' "$BEFORE" | awk -F'\t' -v n="$name" '$1==n{print $2}')
|
old=$(printf '%s\n' "$BEFORE" | awk -F'\t' -v n="$name" '$1==n{print $2}')
|
||||||
printf '%-12s %-32s %s\n' "$name" "${old:-?}" "$ver"
|
printf '%-12s %-32s %s\n' "$name" "${old:-?}" "$ver"
|
||||||
[ "$ver" = "$KLIPPY_VER" ] || { echo "!!! $name: версия $ver != klippy $KLIPPY_VER"; MISMATCH=1; }
|
[ "$ver" = "$KLIPPY_VER" ] || { echo "!!! $name: версия $ver != klippy $KLIPPY_VER"; MISMATCH=1; }
|
||||||
done <<< "$AFTER"
|
done <<< "$AFTER"
|
||||||
|
fi
|
||||||
|
|
||||||
if tail -n 200 "$KLIPPY_LOG" 2>/dev/null | grep -E "Command format mismatch|Protocol error|Lost communication" | tail -5 | grep .; then
|
# Только строки после последнего старта klippy, чтобы не ловить старые ошибки
|
||||||
|
if tac "$KLIPPY_LOG" 2>/dev/null | sed '/^Start printer at/q' | grep -E "Command format mismatch|Protocol error|Lost communication" | tail -5 | grep .; then
|
||||||
echo "!!! В klippy.log есть ошибки протокола (см. выше)"; MISMATCH=1
|
echo "!!! В klippy.log есть ошибки протокола (см. выше)"; MISMATCH=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user