feat(powermgr): meow_powermgr 期0调研+期1骨架——功耗管理专项服务(POWER_MANAGER_DESIGN)

期0: POWER_HW_FACTS.md 真机只读取证——battery/无current_now(实时功率=derived+estimated
双轨)、无cpufreq(省电档无降频点)、背光量程0-101(非255)、rfkill可用、axp_battery为
幻影设备、无thermal zones、audiod STATE字段完备。

期1: patch_v2/meow_powermgr/ 单二进制双形态(meow_powermgr守护+meow_power CLI):
- svcd托管 trigger=boot/restart=on-fail;HibyOS只读降级(/tmp/meow_boot_mode!=meow
  只采样电池,不碰wlan0/bt/声卡,热切换)
- 权威充电链0-diff照抄svc_power(mp2731→fusb302→battery兜底)
- 电池深加工 state/cap/mV/health/续航估算(容量差分速率环 30s×240)
- 经验功耗模型(/usr/data/meow/power_model.conf 可调,内置默认全部标注待标定)
- IPC /usr/data/meow_pm.sock MSG_PM_QUERY(0x00D0)→MSG_PM_RESULT(0x00D1) JSON,
  subset位;meow_ipc.h 0x00D0-0x00D7 号段回填
- CLI status/estimate/model 落地(IPC优先+直读降级),history/top/events/policy 期2/3桩
- 宿主桩测试36+1例全过(MEOW_PS_DIR等假节点)+qemu-mipsel全过+真机对照svc_power
  battery口径一致(93%/4270mV);mipsel静态117KB(<200KB);-Wall -Wextra 0 warning
This commit is contained in:
MeowOS committed 2026-09-01 16:21:12 +08:00
1 parent 6a4d330a26
commit 250c9ae8b1
9 files changed
+1413

No files matched your search

+162
View File
@@ -0,0 +1,162 @@
#!/bin/bash
# test_powermgr.sh — meow_powermgr 宿主桩测试(假 sysfs/procfs 节点,MEOW_*_DIR env 覆盖)。
# 用法:./test_powermgr.sh [host二进制路径](默认 ./meow_powermgr_host;可传 mipsel 二进制 + QEMU 前缀)
# QEMU="qemu-mipsel" ./test_powermgr.sh ./meow_powermgr # qemu 验证
set -u
cd "$(dirname "$0")"
BIN="${1:-./meow_powermgr_host}"
Q="${QEMU:-}"
T=$(mktemp -d /tmp/pmtest.XXXXXX)
trap 'kill $DPID 2>/dev/null; rm -rf "$T"' EXIT
PASS=0; FAIL=0
ck() { # ck <名> <期望子串> <实际>
if echo "$3" | grep -qF "$2"; then PASS=$((PASS+1)); echo "PASS $1";
else FAIL=$((FAIL+1)); echo "FAIL $1: 期望含 [$2] 实际=[$3]"; fi
}
# ---- 搭假节点 ----
PS="$T/ps"; BL="$T/bl"; NET="$T/net"; RF="$T/rfkill"; BT="$T/bt"
mkdir -p "$PS/battery" "$PS/mp2731-charger" "$PS/fusb302-typec-source" "$PS/ac" \
"$BL" "$NET/wlan0" "$RF/rfkill0" "$BT"
echo 94 > "$PS/battery/capacity"
echo Discharging > "$PS/battery/status"
echo 4275000 > "$PS/battery/voltage_now"
echo Good > "$PS/battery/health"
echo Li-ion > "$PS/battery/technology"
echo Discharging > "$PS/mp2731-charger/status" # 权威链
echo 300 > "$PS/mp2731-charger/temp"
echo 0 > "$PS/fusb302-typec-source/online"
echo 0 > "$PS/ac/online"
echo 101 > "$BL/max_brightness" # R3 Pro II 实测量程 0-101
echo 50 > "$BL/actual_brightness" # 中档(49%)
echo 50 > "$BL/brightness"
echo up > "$NET/wlan0/operstate"
echo bluetooth > "$RF/rfkill0/type"
echo 1 > "$RF/rfkill0/soft" # BT 被 soft 块 → off
cat > "$T/netdev" <<'EOF'
Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
wlan0: 1000000 1000 0 0 0 0 0 0 2000000 1000 0 0 0 0 0 0
EOF
echo "cpu 100 0 100 800 0 0 0 0 0 0" > "$T/proc_stat"
echo meow > "$T/boot_mode"
ENVS="MEOW_PS_DIR=$PS MEOW_BL_DIR=$BL MEOW_NET_DIR=$NET MEOW_RFKILL_DIR=$RF MEOW_BT_DIR=$BT
MEOW_NETDEV=$T/netdev MEOW_PROC_STAT=$T/proc_stat MEOW_BOOT_MODE=$T/boot_mode
MEOW_PM_MODEL=$T/model.conf MEOW_AUDIO_SOCK=$T/no_audio.sock MEOW_PM_SOCK=$T/pm.sock"
ln -sf "$(readlink -f "$BIN")" "$T/meow_power" # argv[0] 双形态
# ---- T1: 守护直读(无守护 → 降级直读) ----
OUT=$(env $ENVS $Q "$T/meow_power" status 2>/dev/null)
ck T1a "state=Discharging cap=94%" "$OUT"
echo "$OUT" | grep -q "mv=4275" && { PASS=$((PASS+1)); echo "PASS T1b mv"; } || { FAIL=$((FAIL+1)); echo "FAIL T1b: $OUT"; }
ck T1c "health=Good" "$OUT"
ck T1d "charger_temp=30.0C" "$OUT"
ck T1e "rate=null" "$OUT"
ck T1f "src=direct" "$OUT"
# ---- T2: 起守护,IPC 路径 ----
env $ENVS MEOW_PM_SAMPLE_SEC=1 MEOW_PM_MIN_SPAN_SEC=2 $Q "$BIN" --sock "$T/pm.sock" >"$T/daemon.log" 2>&1 &
DPID=$!
for i in $(seq 1 30); do [ -S "$T/pm.sock" ] && break; sleep 0.2; done
[ -S "$T/pm.sock" ] || { echo "FAIL T0: daemon socket 未出现"; cat "$T/daemon.log"; exit 1; }
PASS=$((PASS+1)); echo "PASS T0 daemon up"
OUT=$(env $ENVS $Q "$T/meow_power" status 2>/dev/null)
ck T2a "src=daemon" "$OUT"
ck T2b "cap=94%" "$OUT"
ck T2c "mode=meow" "$OUT"
# ---- T3: 容量差分速率(94→93,span≥2s)→ rate/est_left=derived ----
echo 93 > "$PS/battery/capacity"
sleep 4
OUT=$(env $ENVS $Q "$T/meow_power" status 2>/dev/null)
ck T3a "conf=derived" "$OUT"
ck T3b "est_left=" "$OUT"
echo "$OUT" | grep -q "rate=-" && { PASS=$((PASS+1)); echo "PASS T3c 放电速率为负"; } || { FAIL=$((FAIL+1)); echo "FAIL T3c: $OUT"; }
# ---- T4: estimate(模型合成;中档背光+面板+wifi idle+bt off+audio unknown) ----
OUT=$(env $ENVS $Q "$T/meow_power" estimate 2>/dev/null)
ck T4a "confidence=estimated" "$OUT"
ck T4b "待真机标定" "$OUT"
ck T4c "backlight=450" "$OUT" # 49% → 中档 450
ck T4d "panel=80" "$OUT"
ck T4e "bt=0(off)" "$OUT" # rfkill soft=1 → off
ck T4f "audio=0(unknown)" "$OUT" # 无 audiod → unknown 0mW
# est_mw = 300(base)+450+80+200(wifi idle)+0+0+cpu(0,静态proc_stat)=1030
ck T4g "est_mw=1030" "$OUT"
# ---- T5: wifi 传输档(netdev 增量 >4096/拍 → tx=600) ----
sed -i 's/wlan0: 1000000/wlan0: 1999999/; s/ 2000000 / 2999999 /' "$T/netdev"
sleep 3
OUT=$(env $ENVS $Q "$T/meow_power" estimate 2>/dev/null)
ck T5a "wifi=600(tx)" "$OUT"
# ---- T6: 息屏(actual_brightness=0)→ 背光/面板归零 ----
echo 0 > "$BL/actual_brightness"
OUT=$(env $ENVS $Q "$T/meow_power" estimate 2>/dev/null)
ck T6a "backlight=0" "$OUT"
ck T6b "panel=0" "$OUT"
echo 50 > "$BL/actual_brightness"
# ---- T7: 模型 conf 覆盖 ----
printf '# test\nbaseline_mw=999\nwifi_idle_mw=111\n' > "$T/model.conf"
OUT=$(env $ENVS $Q "$T/meow_power" model 2>/dev/null)
ck T7a "baseline_mw=999" "$OUT"
ck T7b "wifi_idle_mw=111" "$OUT"
ck T7c "bl_high_mw=900" "$OUT" # 未覆盖键留默认
rm -f "$T/model.conf"
# ---- T8: HibyOS 只读降级(不施估算;电池仍可读) ----
echo hiby > "$T/boot_mode"
sleep 2 # 等守护下一拍热切换
OUT=$(env $ENVS $Q "$T/meow_power" estimate 2>/dev/null)
ck T8a "HibyOS 只读模式" "$OUT"
OUT=$(env $ENVS $Q "$T/meow_power" status 2>/dev/null)
ck T8b "cap=93%" "$OUT" # 只读模式电池采样不受影响
ck T8c "mode=readonly" "$OUT"
echo meow > "$T/boot_mode"
sleep 2
OUT=$(env $ENVS $Q "$T/meow_power" estimate 2>/dev/null)
ck T8d "est_mw=" "$OUT" # 热切换回全功能
# ---- T9: CLI 桩(期 2/3) ----
OUT=$(env $ENVS $Q "$T/meow_power" history 2>&1); RC=$?
ck T9a "期 2" "$OUT"
[ $RC -eq 1 ] && { PASS=$((PASS+1)); echo "PASS T9b rc=1"; } || { FAIL=$((FAIL+1)); echo "FAIL T9b rc=$RC"; }
OUT=$(env $ENVS $Q "$T/meow_power" policy 2>&1)
ck T9c "期 3" "$OUT"
# ---- T10: meow_powermgr 名直调子命令也走 CLI ----
OUT=$(env $ENVS $Q "$BIN" model 2>/dev/null)
ck T10a "nominal_mah=1600" "$OUT"
# ---- T11: 守护停 → CLI 自动降级直读不崩 ----
kill $DPID 2>/dev/null; wait $DPID 2>/dev/null; DPID=""
sleep 1
OUT=$(env $ENVS $Q "$T/meow_power" status 2>/dev/null)
ck T11a "src=direct" "$OUT"
ck T11b "cap=93%" "$OUT"
# ---- T12: audiod 在场时音频链分量(真 audiod 二进制当桩:无 HELLO 直接 QUERY 协议) ----
AUDIOD_BIN=../meow_audiod/meow_audiod_host
[ -n "$Q" ] && AUDIOD_BIN=../meow_audiod/meow_audiod
if [ -x "$AUDIOD_BIN" ]; then
env MEOW_AUDIO_FORCE=1 MEOW_AUDIO_SOCK="$T/audio.sock" MEOW_AUDIO_OUT="$T/a.wav" \
MEOW_AUDIO_SINK=null $Q "$AUDIOD_BIN" --sock "$T/audio.sock" >"$T/audiod.log" 2>&1 &
APID=$!
for i in $(seq 1 30); do [ -S "$T/audio.sock" ] && break; sleep 0.2; done
env $ENVS MEOW_AUDIO_SOCK="$T/audio.sock" MEOW_PM_SAMPLE_SEC=1 MEOW_PM_MIN_SPAN_SEC=2 \
MEOW_PM_SOCK="$T/pm2.sock" $Q "$BIN" --sock "$T/pm2.sock" >"$T/daemon2.log" 2>&1 &
DPID=$!
for i in $(seq 1 30); do [ -S "$T/pm2.sock" ] && break; sleep 0.2; done
OUT=$(env $ENVS MEOW_AUDIO_SOCK="$T/audio.sock" MEOW_PM_SOCK="$T/pm2.sock" $Q "$T/meow_power" estimate 2>/dev/null)
ck T12a "audio=50(idle)" "$OUT" # audiod 在场无流 → 静态档 50
kill $DPID $APID 2>/dev/null; wait 2>/dev/null; DPID=""
else
echo "SKIP T12 (无 audiod 二进制 $AUDIOD_BIN)"
fi
echo "=============================="
echo "PASS=$PASS FAIL=$FAIL"
[ $FAIL -eq 0 ]