From 0cae95c9cc64b94eec74a671f382b3e1ee975013 Mon Sep 17 00:00:00 2001 From: MeowOS Date: Tue, 1 Sep 2026 22:13:42 +0800 Subject: [PATCH] =?UTF-8?q?powermgr=20=E6=9C=9F2:=20=E5=8E=86=E5=8F=B2?= =?UTF-8?q?=E9=87=87=E6=A0=B7=E7=8E=AF/=E5=8A=9F=E8=80=97=E5=88=86?= =?UTF-8?q?=E8=A7=A3top/=E7=94=B5=E6=BA=90=E4=BA=8B=E4=BB=B6=E6=B5=81=20+?= =?UTF-8?q?=20mp2731=E7=94=B5=E6=B5=81=E6=8E=A5=E5=85=A5=20+=20BT=C2=B7WiF?= =?UTF-8?q?i=E5=88=86=E6=A1=A3=E7=BB=86=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - history: 30s 节拍内存环形(RING_N=480≈4h)存 pct/est_mw/充电态/屏幕态/亮度, 不落盘重启清零(板载写磨损不考虑); IPC MSG_PM_HISTORY(0xD4)/D5, CLI history[N] - top: estimate breakdown 分量实时占比排序视图(2s 刷新, Ctrl-C 退, MEOW_PM_TOP_ONCE=1 一次输出供测试); CLI top - events: 插拔充电器/低电阈值穿越(15%/5%/回20%)/亮灭屏/背光跳变(≥30%); App 前后台无可轮询导出点→诚实标注不支持(语言, 期3接 launcher) IPC MSG_PM_EVENTS(0xD6)/D7, CLI events[N] - 模型: 系数路径迁 /data/meow/powermgr/power_model.conf(=/usr/data symlink, 组件持久化目录定案); WiFi 增未关联(carrier=0→unassoc)分档; BT 细化 rfkill+hciconfig ACL(up/connected, 有界子进程 900ms 超时, 不可用回退 up) - mp2731 current_now 接入: 充电态实测 charge_mw=cc_voltage×current/1e9, charge_confidence=measured(消费侧无实测节点仍 estimated, 条件写清) - IPC 0xD4-D7 号段本文件局部定(不动 meow_launcherd/meow_ipc.h, 严守只动本目录) 验证: 宿主 aarch64 54 PASS + qemu-mipsel 54 PASS; 交叉 -Wall -Wextra -Werror 0 warning; 固件阈值 <200KB 达标(133KB); BSS 37.6KB 起见 RSS<1MB 达标 --- patch_v2/meow_powermgr/meow_powermgr | Bin 117628 -> 132812 bytes patch_v2/meow_powermgr/meow_powermgr.c | 587 ++++++++++++++++++++-- patch_v2/meow_powermgr/meow_powermgr_host | Bin 76176 -> 77488 bytes patch_v2/meow_powermgr/test_powermgr.sh | 72 ++- 4 files changed, 605 insertions(+), 54 deletions(-) diff --git a/patch_v2/meow_powermgr/meow_powermgr b/patch_v2/meow_powermgr/meow_powermgr index 696459b427c969a749046e8178daee49b799c2ab..6912ce93678c370929a35fe2bad541e4f76d9951 100755 GIT binary patch delta 45196 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 L{+0jKPJ{jzY&E)2 delta 31196 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 K%HVL(>;C~L3mOIh diff --git a/patch_v2/meow_powermgr/meow_powermgr.c b/patch_v2/meow_powermgr/meow_powermgr.c index 663e598..470aa0d 100644 --- a/patch_v2/meow_powermgr/meow_powermgr.c +++ b/patch_v2/meow_powermgr/meow_powermgr.c @@ -1,11 +1,17 @@ #define _GNU_SOURCE /* struct ucred / SO_PEERCRED、asprintf 等 GNU/musl 扩展 */ -/* meow_powermgr.c — MeowOS 功耗管理专项服务(devdocs/plan/POWER_MANAGER_DESIGN.md,期 1 骨架)。 +/* meow_powermgr.c — MeowOS 功耗管理专项服务(devdocs/plan/POWER_MANAGER_DESIGN.md,期 2)。 * * 定位(三服务职责划分,§1):svc_power=一次性系统动作;svc_perf=性能只读聚合; * **powermgr=功耗**——电池深加工(state/cap/mV/续航估算)、经验功耗模型估算与分解。 * 期 1 范围(§9):svcd 托管骨架 + IPC(MSG_PM_QUERY/RESULT) + CLI status/estimate/model + - * 权威充电链复用 + 经验模型;history/top/events/policy 为期 2/3 桩。 + * 权威充电链复用 + 经验模型。 + * 期 2 新增(本文件):历史采样环(30s 节拍,pct/est_mw/充电态/屏幕态/亮度,内存环形不落盘)、 + * 功耗分解 top 排序视图、电源事件流(插拔/低电阈值/背光变化;App 前后台无口径可采→诚实标注不支持)、 + * mp2731 current_now 充电功率接入(充电态下 charge_mw 为实测,consume est_mw 仍为模型 estimated)、 + * 模型系数路径迁移到 /data/meow/powermgr/power_model.conf(=/usr/data symlink,组件持久化目录定案)。 + * IPC 新增 MSG_PM_HISTORY/D5 + MSG_PM_EVENTS/D7(号段 0x00D0-0x00D7 剩余位;数字本文件局部定, + * meow_ipc.h 仅注释保留——只动本目录,不碰 meow_launcherd/meow_ipc.h)。 * * 单一二进制双形态(同 svc_power): * - svcd 空参拉起(板载 services.d/meow_powermgr.conf,trigger=boot/restart=on-fail)→ @@ -48,9 +54,19 @@ #include #include #include +#include #include "meow_ipc.h" /* 帧格式/消息号 + MSG_PM 系列 + PM_SUBSET_* + m_pm_*(号段 0x00D0-0x00D7) */ +/* 期 2 消息号:meow_ipc.h 仅以注释保留(0x00D4-0x00D7),本文件局部定义,避免动 meow_launcherd/。 + * 响应帧复用 m_pm_result_t(code+subset+json_len+json),type 区分 HISTORY_RESULT/EVENTS_RESULT。 */ +#ifndef MSG_PM_HISTORY +#define MSG_PM_HISTORY 0x00D4 /* ↑ 历史采样查询(req: u32 count=N,0=默认) */ +#define MSG_PM_HISTORY_RESULT 0x00D5 /* ↓ 历史 JSON(采样点清单 + 窗口汇总) */ +#define MSG_PM_EVENTS 0x00D6 /* ↑ 事件流查询(req: u32 count=N,0=默认) */ +#define MSG_PM_EVENTS_RESULT 0x00D7 /* ↓ 事件 JSON 清单 */ +#endif + /* ---------------- 路径(全部可经 env 覆盖,宿主/qemu 桩测试用) ---------------- */ static char g_ps_dir[128] = "/sys/class/power_supply"; /* MEOW_PS_DIR */ static char g_bl_dir[128] = "/sys/class/backlight/backlight_pwm0"; /* MEOW_BL_DIR */ @@ -60,13 +76,17 @@ static char g_bt_dir[128] = "/sys/class/bluetooth"; /* MEOW_ static char g_netdev[160] = "/proc/net/dev"; /* MEOW_NETDEV */ static char g_proc_stat[160] = "/proc/stat"; /* MEOW_PROC_STAT */ static char g_boot_mode[160] = "/tmp/meow_boot_mode"; /* MEOW_BOOT_MODE */ -static char g_model_conf[160] = "/usr/data/meow/power_model.conf"; /* MEOW_PM_MODEL */ +static char g_model_conf[160] = "/usr/data/meow/powermgr/power_model.conf"; /* MEOW_PM_MODEL + (= /data/meow/powermgr/power_model.conf,/data=/usr/data symlink, + 组件持久化目录定案 SPECIAL_REF §1.1;存量平铺件不动) */ static char g_audio_sock[128] = "/usr/data/meow_audio.sock"; /* MEOW_AUDIO_SOCK */ static const char *g_sock = "/usr/data/meow_pm.sock"; /* MEOW_PM_SOCK/--sock */ #define SAMPLE_SEC_DEF 30 /* 电池采样/状态刷新节拍(秒;MEOW_PM_SAMPLE_SEC 覆盖,测试用) */ #define RATE_MIN_SPAN_DEF 120 /* 容量差分速率的最小时间窗(秒;MEOW_PM_MIN_SPAN_SEC 覆盖) */ -#define RING_N 240 /* 采样环容量(30s×240=2h) */ +#define RING_N 480 /* 采样环容量(30s×480=4h;内存环形不落盘,重启清零——板载 flash 写磨损不考虑) */ +#define HIST_MAX_REPLY 80 /* history IPC 单帧最多回采样点(MEOW_IPC_MAX=4096 下单帧可承载上限) */ +#define EVENT_RING_N 64 /* 事件环容量(内存环形,重启清零) */ static int g_sample_sec = SAMPLE_SEC_DEF; static int g_rate_min_span = RATE_MIN_SPAN_DEF; @@ -145,6 +165,8 @@ typedef struct { int temp_c10; /* battery/temp(本机无 → -1 → null) */ int charger_temp_c10; /* mp2731-charger/temp(-1=无);÷10=℃ */ int cycle; /* battery/cycle_count(本机无 → -1 → null) */ + int chg_cur_ua; /* mp2731-charger/current_now(µA;放电恒 0,充电=充电电流;-1=无) */ + int chg_vol_uv; /* mp2731-charger/constant_charge_voltage(µV;-1=无)→ 充电功率实测 */ } batt_t; static void batt_read(batt_t *b) @@ -152,6 +174,7 @@ static void batt_read(batt_t *b) char p[192]; memset(b, 0, sizeof *b); b->pct = -1; b->mv = -1; b->temp_c10 = -1; b->charger_temp_c10 = -1; b->cycle = -1; + b->chg_cur_ua = -1; b->chg_vol_uv = -1; ps_path(p, sizeof p, "battery/capacity"); b->pct = read_file_int_def(p, -1); ps_path(p, sizeof p, "battery/voltage_now"); @@ -169,6 +192,10 @@ static void batt_read(batt_t *b) b->charger_temp_c10 = read_file_int_def(p, -1); ps_path(p, sizeof p, "battery/cycle_count"); b->cycle = read_file_int_def(p, -1); + ps_path(p, sizeof p, "mp2731-charger/current_now"); + b->chg_cur_ua = read_file_int_def(p, -1); + ps_path(p, sizeof p, "mp2731-charger/constant_charge_voltage"); + b->chg_vol_uv = read_file_int_def(p, -1); } /* ---------------- 经验功耗模型(§3.2;系数全部经验初值,待真机标定) ---------------- */ @@ -250,10 +277,18 @@ static int model_load(pm_model_t *m) return 0; } -/* ---------------- 采样环(守护进程;容量差分速率 → 续航估算,§3.1/§3.3 derived) ---------------- */ -typedef struct { long mono; int pct; int charging; } sample_t; -static sample_t g_ring[RING_N]; -static int g_rcount = 0; /* 已入环条数(≤RING_N;覆盖写最旧) */ +/* ---------------- 采样环(守护进程;容量差分速率 → 续航估算 §3.1/§3.3 derived; + * 期 2 同时服务 history 采样点:pct/est_mw/充电态/屏幕态/亮度,内存环形不落盘,重启清零) ---------------- */ +typedef struct { + long mono; /* 采样时刻(monotonic 秒) */ + int pct; /* 电量 % */ + int est_mw; /* est_mw(-1=只读/未知) */ + int charging; /* 0/1 */ + int screen; /* 0=off 1=on(-1=未知) */ + int brightness_pct; /* -1=未知 */ +} hist_sample_t; +static hist_sample_t g_ring[RING_N]; +static int g_rcount = 0; /* 已入环条数(≤RING_N;覆盖写最旧) */ static long mono_now(void) { @@ -262,13 +297,14 @@ static long mono_now(void) return tp.tv_sec; } -static void ring_push(const batt_t *b) +static void ring_push(const batt_t *b, int est_mw, int screen, int bri) { if (b->pct < 0) return; - sample_t s; + hist_sample_t s; s.mono = mono_now(); s.pct = b->pct; s.charging = b->charging; + s.est_mw = est_mw; s.screen = screen; s.brightness_pct = bri; if (g_rcount < RING_N) g_ring[g_rcount++] = s; else { memmove(g_ring, g_ring + 1, sizeof g_ring - sizeof g_ring[0]); g_ring[RING_N - 1] = s; } } @@ -279,8 +315,8 @@ static void ring_push(const batt_t *b) static int ring_rate(int *rate_x100) { if (g_rcount < 2) return 0; - const sample_t *nw = &g_ring[g_rcount - 1]; - const sample_t *ol = NULL; + const hist_sample_t *nw = &g_ring[g_rcount - 1]; + const hist_sample_t *ol = NULL; for (int i = 0; i < g_rcount - 1; i++) if (g_ring[i].charging == nw->charging) { ol = &g_ring[i]; break; } if (!ol) return 0; @@ -313,16 +349,59 @@ static int bl_read_pct(int *on) return v * 100 / mx; } -/* WiFi:0=off(接口不存在/operstate 非 up)1=idle 2=tx(netdev 增量>阈值)。 - * 关联/未关联档 v1 无判据(要 wpa_cli,期 2 再细化)——up 即按 idle 起算,诚实标注。 */ +/* 有界子进程命令(fork+pipe+poll+超时 SIGKILL,绝不阻塞采样拍;netgr 同源性)。 + * 返回读到字节数(含尾 NUL 截断);-1=失败/超时。out 在 cap 内 NUL 结尾。 + * ⚠ 仅 meow 全功能模式、每拍至多一两次(wpa_cli/hciconfig);只读模式绝不调用(门控红线)。 */ +static int run_cmd_bounded(const char *cmd, char *out, size_t cap, int tmo_ms) +{ + if (cap < 2) return -1; + int pfd[2]; + if (pipe(pfd) != 0) return -1; + pid_t pid = fork(); + if (pid < 0) { close(pfd[0]); close(pfd[1]); return -1; } + if (pid == 0) { + close(pfd[0]); + dup2(pfd[1], STDOUT_FILENO); + close(pfd[1]); + int nul = open("/dev/null", O_WRONLY); + if (nul >= 0) { dup2(nul, STDERR_FILENO); if (nul > 2) close(nul); } + execl("/bin/sh", "sh", "-c", cmd, (char *)NULL); + _exit(127); + } + close(pfd[1]); + size_t off = 0; + struct pollfd pf = { pfd[0], POLLIN, 0 }; + for (;;) { + int pr = poll(&pf, 1, tmo_ms); + if (pr < 0) { if (errno == EINTR) continue; break; } + if (pr == 0) break; /* 超时 */ + ssize_t r = read(pfd[0], out + off, cap - 1 - off); + if (r > 0) { off += (size_t)r; if (off >= cap - 1) break; } + else if (r == 0) break; /* EOF */ + else if (errno == EINTR) continue; + else break; + } + close(pfd[0]); + kill(pid, SIGKILL); /* 已退出则 0,未退出强杀 */ + waitpid(pid, NULL, 0); /* 收尸彻底 */ + out[off] = 0; + return off > 0 ? (int)off : -1; +} + +/* WiFi:0=off(接口不存在/operstate 非 up)1=idle 2=tx(netdev 增量>阈值)3=unassoc(up 但未关联)。 + * 关联判定:/sys/class/net/wlan0/carrier(0=未关联,1=已关联,不可读则默认已关联——同 wpa 常态, + * 保留期 1 语义:up 即 idle 起算)。tx 由 netdev 计数器增量判(期 1 惯例)。 */ static int wifi_read_state(long *rx_out, long *tx_out) { char p[192]; snprintf(p, sizeof p, "%s/wlan0/operstate", g_net_dir); char os[32] = ""; if (read_file_txt(p, os, sizeof os) <= 0 || strcmp(os, "up") != 0) return 0; + /* carrier 关联判定(0=未关联→unassoc;1 或不可读→已关联向下走 idle/tx 档) */ + snprintf(p, sizeof p, "%s/wlan0/carrier", g_net_dir); + int carrier = read_file_int_def(p, 1); /* 缺省 1=已关联(期 1 语义) */ FILE *f = fopen(g_netdev, "r"); - if (!f) return 1; + if (!f) { if (rx_out) *rx_out = -1; if (tx_out) *tx_out = -1; return carrier == 0 ? 3 : 1; } char line[256]; long rx = -1, tx = -1; while (fgets(line, sizeof line, f)) { @@ -333,9 +412,6 @@ static int wifi_read_state(long *rx_out, long *tx_out) while (*n == ' ' || *n == '\t') n++; if (strcmp(n, "wlan0") != 0) continue; rx = strtol(c + 1, NULL, 10); - char *tk = c + 1; - for (int i = 0; i < 8; i++) { tk = strchr(tk, ' '); if (!tk) break; while (*tk == ' ') tk++; } - /* 第 9 列 = tx bytes */ char *save = NULL; char *w = strtok_r(c + 1, " \t", &save); for (int i = 0; w && i < 8; i++) w = strtok_r(NULL, " \t", &save); @@ -345,10 +421,11 @@ static int wifi_read_state(long *rx_out, long *tx_out) fclose(f); if (rx_out) *rx_out = rx; if (tx_out) *tx_out = tx; - return 1; + if (carrier == 0) return 3; + return 1; /* 传送档由调用方按增量判 2 */ } -/* BT:rfkill soft 块→off;hci0 存在且未被块→up。连接数判据期 2。 */ +/* BT:rfkill soft 块→off;hci0 存在且未被块→up;连接数档(期 2 细化见下)。 */ static int bt_read_state(void) { char p[448]; /* rfkill_dir + "/" + d_name(≤255) + "/type|/soft" 充裕 */ @@ -372,7 +449,22 @@ static int bt_read_state(void) if (blocked) return 0; if (!hci0 && !seen) return 0; /* 无任何 BT 踪迹 → off */ if (!hci0) return 0; - return 1; /* up 空闲档(连接判定期 2) */ + /* hciconfig 实测 UP/RUNNING + ACL 连接数(期 1 只据 sysfs 存在→up 偏粗,期 2 细化)。 + * hciconfig 不可用(宿主/qemu 桩无该命令)或 MEOW_PM_NO_HCICONFIG=1(宿主测试桩,防宿主机 + * 自带 hciconfig/蓝牙控制器干扰断言)→ 回退 up(1),连接数无法判(诚实标注,期 3 接 hcitool)。 */ + char out[320]; + int r = -1; + if (!getenv("MEOW_PM_NO_HCICONFIG")) + r = run_cmd_bounded("PATH=/bin:/sbin:/usr/bin:/usr/sbin hciconfig -a hci0 2>/dev/null", + out, sizeof out, 900); + if (r > 0) { + if (strstr(out, "DOWN") && !strstr(out, "UP RUNNING")) return 0; /* 电源开但接口未 up */ + const char *ah = strcasestr(out, "acl:"); /* 连接数 "ACL: N" / "acl: N" */ + if (!ah) ah = strcasestr(out, "acl "); + if (ah) { int n = atoi(ah + 4); if (n > 0) return 2; } /* 有 ACL 连接 → connected */ + return 1; /* up 空闲 */ + } + return 1; /* hciconfig 不可用 → sysfs 存在=up(1) */ } /* 音频链:meow 模式下经 audiod IPC(MSG_AUDIO_QUERY→MSG_AUDIO_STATE 文本)判 active。 @@ -443,8 +535,9 @@ typedef struct { int baseline_mw, cpu_mw, bl_mw, panel_mw, wifi_mw, bt_mw, audio_mw; int cpu_util; /* -1=未知 */ int brightness_pct; /* -1=未知 */ - int wifi_state; /* 0/1/2 */ - int bt_state; /* 0/1 */ + int screen_on; /* 0=off 1=on -1=未知(actual_brightness>0 代理,期 2 历史用) */ + int wifi_state; /* 0=off 1=idle 2=tx 3=unassoc */ + int bt_state; /* 0=off 1=up 2=connected */ int audio_state; /* -1/0/1 */ int degraded; /* 1=Hiby 只读模式:不碰 wlan0/bt/声卡,分量全 0 */ } est_t; @@ -456,30 +549,34 @@ static void est_compute(const pm_model_t *m, int full, int cpu_util, long *prev_rx, long *prev_tx, est_t *e) { memset(e, 0, sizeof *e); - e->cpu_util = -1; e->brightness_pct = -1; e->audio_state = -1; + e->cpu_util = -1; e->brightness_pct = -1; e->audio_state = -1; e->screen_on = -1; if (!full) { e->degraded = 1; return; } e->baseline_mw = m->baseline_mw; /* 屏幕 */ int on = -1; int bp = bl_read_pct(&on); e->brightness_pct = bp; + e->screen_on = on; if (on == 1) { e->panel_mw = m->panel_on_mw; e->bl_mw = bp <= 33 ? m->bl_low_mw : (bp <= 66 ? m->bl_mid_mw : m->bl_high_mw); } - /* WiFi */ + /* WiFi(0=off 1=idle 2=tx 3=unassoc;tx 仅对已关联档判) */ long rx = -1, tx = -1; e->wifi_state = wifi_read_state(&rx, &tx); - if (e->wifi_state > 0) { + if (e->wifi_state == 1) { if (prev_rx && *prev_rx >= 0 && rx >= 0 && (rx - *prev_rx) + (tx - *prev_tx) > 4096) e->wifi_state = 2; - e->wifi_mw = e->wifi_state == 2 ? m->wifi_tx_mw : m->wifi_idle_mw; } + e->wifi_mw = e->wifi_state == 2 ? m->wifi_tx_mw + : (e->wifi_state == 3 ? m->wifi_unassoc_mw + : (e->wifi_state == 1 ? m->wifi_idle_mw : 0)); if (prev_rx) { *prev_rx = rx; *prev_tx = tx; } - /* BT */ + /* BT(0=off 1=up 2=connected) */ e->bt_state = bt_read_state(); - e->bt_mw = e->bt_state ? m->bt_up_mw : 0; + e->bt_mw = e->bt_state == 2 ? m->bt_conn_mw + : (e->bt_state == 1 ? m->bt_up_mw : 0); /* 音频链 */ e->audio_state = audio_query_active(); e->audio_mw = e->audio_state == 1 ? m->audio_play_mw @@ -537,6 +634,18 @@ static void jadd_rate(jw_t *w, const batt_t *b, int have_rate, int rate_x100) jadd(w, "\"rate_confidence\":\"derived\","); } +/* 充电功率(mp2731 current_now 实测轨;POWER_HW_FACTS §0/项 2,期 2 接入 §3.3): + * charge_mw = constant_charge_voltage(µV) × current_now(µA) / 1e9。 + * 仅当「权威链判定 charging && current_now>0 && constant_charge_voltage>0」时返回实测 mW, + * 否则返回 -1(≠ 容量差分/经验模型,诚实标注——充电功率可测,消费功耗不可测)。 */ +static int pm_charge_mw(const batt_t *b) +{ + if (!b->charging) return -1; + if (b->chg_cur_ua <= 0 || b->chg_vol_uv <= 0) return -1; + long long v = (long long)b->chg_vol_uv * b->chg_cur_ua; + return (int)(v / 1000000000LL); +} + /* 聚合 JSON(subset 位选段;0=全部)。守护/CLI 降级直读共用。 * have_rate/cpu_util/prev_rx/prev_tx 由调用方提供(守护=环/差分态;直读=瞬测)。 */ static void build_json(uint32_t subset, int full, const batt_t *b, @@ -558,15 +667,23 @@ static void build_json(uint32_t subset, int full, const batt_t *b, jadd_int_or_null(&w, "temp_c10", b->temp_c10); jadd_int_or_null(&w, "charger_temp_c10", b->charger_temp_c10); jadd_int_or_null(&w, "cycle_count", b->cycle); + jadd_int_or_null(&w, "chg_current_ua", b->chg_cur_ua); + jadd_int_or_null(&w, "chg_voltage_uv", b->chg_vol_uv); jadd_rate(&w, b, have_rate, rate_x100); jadd(&w, "\"nominal_mah_note\":\"nominal_mah 为模型系数(默认1600,待标定)\"}"); } if (subset & PM_SUBSET_ESTIMATE) { jadd(&w, ",\"estimate\":{"); if (e->degraded) { - jadd(&w, "\"est_mw\":null,\"confidence\":\"none\",\"degraded\":true}"); + jadd(&w, "\"est_mw\":null,\"confidence\":\"none\",\"degraded\":true," + "\"charge_mw\":null,\"charge_confidence\":null}"); } else { - jadd(&w, "\"est_mw\":%d,\"confidence\":\"estimated\",\"calibrated\":false}", e->total_mw); + int cmw = pm_charge_mw(b); + jadd(&w, "\"est_mw\":%d,\"confidence\":\"estimated\",\"calibrated\":false,", e->total_mw); + if (cmw >= 0) + jadd(&w, "\"charge_mw\":%d,\"charge_confidence\":\"measured\"}", cmw); + else + jadd(&w, "\"charge_mw\":null,\"charge_confidence\":null}"); } } if (subset & PM_SUBSET_BREAKDOWN) { @@ -580,8 +697,11 @@ static void build_json(uint32_t subset, int full, const batt_t *b, jadd(&w, "\"backlight_mw\":%d,\"panel_mw\":%d,", e->bl_mw, e->panel_mw); jadd_int_or_null(&w, "brightness_pct", e->brightness_pct); jadd(&w, "\"wifi_mw\":%d,\"wifi_state\":\"%s\",", e->wifi_mw, - e->wifi_state == 2 ? "tx" : (e->wifi_state == 1 ? "idle" : "off")); - jadd(&w, "\"bt_mw\":%d,\"bt_state\":\"%s\",", e->bt_mw, e->bt_state ? "up" : "off"); + e->wifi_state == 2 ? "tx" + : (e->wifi_state == 3 ? "unassoc" + : (e->wifi_state == 1 ? "idle" : "off"))); + jadd(&w, "\"bt_mw\":%d,\"bt_state\":\"%s\",", e->bt_mw, + e->bt_state == 2 ? "connected" : (e->bt_state == 1 ? "up" : "off")); jadd(&w, "\"audio_mw\":%d,\"audio_state\":\"%s\"", e->audio_mw, e->audio_state == 1 ? "playing" : (e->audio_state == 0 ? "idle" : "unknown")); jadd(&w, "}"); @@ -649,7 +769,7 @@ static int send_err(int fd, uint32_t req_id, uint32_t code, const char *msg) return send_frame(fd, MSG_ERR, req_id, &e, sizeof e); } -static int send_pm_result(int fd, uint32_t req_id, uint32_t subset, const char *json) +static int send_pm_result(int fd, uint16_t rtype, uint32_t req_id, uint32_t subset, const char *json) { m_pm_result_t r; memset(&r, 0, sizeof r); @@ -660,15 +780,19 @@ static int send_pm_result(int fd, uint32_t req_id, uint32_t subset, const char * r.json_len = (uint16_t)jl; memcpy(r.json, json, jl); r.json[jl] = 0; - return send_frame(fd, MSG_PM_RESULT, req_id, &r, (uint32_t)(12 + jl + 1)); + return send_frame(fd, rtype, req_id, &r, (uint32_t)(12 + jl + 1)); } /* ---------------- 守护进程态 ---------------- */ static int g_full_mode = 0; /* meow=1 全功能;0=只读降级(hiby/缺失) */ static long g_cpu_prev_total = -1, g_cpu_prev_idle = 0; -static long g_wifi_prev_rx = -1, g_wifi_prev_tx = 0; +static long g_wifi_prev_rx = -1, g_wifi_prev_tx = 0; /* PM_QUERY 实时估计算的 WiFi 基线(查询驱动) */ +static long g_hist_wifi_prev_rx = -1, g_hist_wifi_prev_tx = 0; /* 守护节拍估计算的 WiFi 基线(history 采样) */ static pm_model_t g_model; +/* 上一拍状态(事件检测用;-1=首拍未初始化,跳过跃迁判定) */ +static int g_prev_charging = -1, g_prev_pct = -1, g_prev_screen = -1, g_prev_bri_pct = -1; + static int boot_mode_is_meow(void) { char buf[16] = ""; @@ -676,7 +800,76 @@ static int boot_mode_is_meow(void) return strcmp(buf, "meow") == 0; } -/* 采样拍:模式热切换 + 电池入环 + CPU 差分基准推进。(WiFi 计数基准由 est_compute 回存。) */ +/* ---------------- 电源事件流(守护进程;内存环形,重启清零,不落盘) ---------------- + * 可检测:① 充电器插拔(charging 位翻转)② 低电阈值穿越(低 15%/极低 5%/回到 20% 恢复,§5.2 默认) + * ③ 屏幕亮灭 ④ 背光跳变(≥30% 点)。⚠ App 前后台切换:期 2 无可轮询的 fg/bg 导出点(launcher 对外 + * 无 App 前台态;/tmp/meow_*_active 是 watchdog 让位的行为标记而非 App 前后台),**诚实标注不支持**, + * 不编造事件——留期 3 接 launcher MSG_ACT_FOCUS 后再扩。 */ +#define EV_LOW_PCT 15 /* 低电阈值(§5.2 power.low_pct 默认 15,期 3 接 settings) */ +#define EV_CRIT_PCT 5 /* 极低电阈值(§5.2 power.critical_pct 默认 5) */ +#define EV_LOW_REC 20 /* 恢复阈值(low+5;电量回到 ≥20% 触发恢复事件) */ +#define EV_BRI_JUMP 30 /* 背光跳变阈值(百分点) */ + +typedef struct { + long mono; /* 事件时刻(monotonic 秒) */ + int type; /* 0=charger 1=lowbat 2=screen 3=brightness */ + int arg; /* charger: new charging(0/1);lowbat: 0低 1极低 2恢复;screen: on(0/1);bri: new pct */ + int pct; /* 事件时电量 % */ + char txt[112]; /* 可读描述 */ +} pm_event_t; +static pm_event_t g_events[EVENT_RING_N]; +static int g_ev_count = 0; /* 累计事件数含滚出;展示取 min(count, EVENT_RING_N) */ + +static void ev_push(int type, int arg, int pct, const char *fmt, ...) +{ + pm_event_t *dst; + if (g_ev_count < EVENT_RING_N) dst = &g_events[g_ev_count]; + else { memmove(g_events, g_events + 1, sizeof g_events - sizeof g_events[0]); dst = &g_events[EVENT_RING_N - 1]; } + g_ev_count++; + memset(dst, 0, sizeof *dst); + dst->mono = mono_now(); dst->type = type; dst->arg = arg; dst->pct = pct; + va_list ap; + va_start(ap, fmt); + vsnprintf(dst->txt, sizeof dst->txt, fmt, ap); + va_end(ap); +} + +static void detect_events(const batt_t *b, const est_t *e) +{ + /* ① 充电器插拔 */ + if (g_prev_charging >= 0 && g_prev_charging != b->charging) { + if (b->charging) ev_push(0, 1, b->pct, "插上充电器,开始充电 (state=%s)", b->state); + else ev_push(0, 0, b->pct, "拔出充电器,停止充电 (state=%s)", b->state); + } + /* ② 低电阈值(放电下降沿触发;充电回阈值恢复) */ + if (b->pct >= 0 && g_prev_pct >= 0) { + if (!b->charging) { + if (g_prev_pct >= EV_LOW_PCT && b->pct < EV_LOW_PCT) + ev_push(1, 0, b->pct, "电量低 %d%%(阈值 %d%%)", b->pct, EV_LOW_PCT); + if (g_prev_pct >= EV_CRIT_PCT && b->pct < EV_CRIT_PCT) + ev_push(1, 1, b->pct, "电量极低 %d%%(阈值 %d%%)", b->pct, EV_CRIT_PCT); + } + if (g_prev_pct < EV_LOW_REC && b->pct >= EV_LOW_REC) + ev_push(1, 2, b->pct, "电量回到 %d%%(≥%d%% 恢复)", b->pct, EV_LOW_REC); + } + /* ③ 屏幕亮灭 / ④ 背光跳变(只读模式 screen=-1 → 跳过;息屏时不算背光跳变) */ + if (e->screen_on >= 0) { + if (g_prev_screen >= 0 && g_prev_screen != e->screen_on) + ev_push(2, e->screen_on, b->pct, e->screen_on ? "亮屏" : "息屏"); + if (e->screen_on == 1 && e->brightness_pct >= 0 && g_prev_bri_pct >= 0 && + ((e->brightness_pct > g_prev_bri_pct ? e->brightness_pct - g_prev_bri_pct + : g_prev_bri_pct - e->brightness_pct)) >= EV_BRI_JUMP) + ev_push(3, e->brightness_pct, b->pct, "背光跳变 %d%%→%d%%(≥%d%%)", + g_prev_bri_pct, e->brightness_pct, EV_BRI_JUMP); + } + g_prev_charging = b->charging; + if (b->pct >= 0) g_prev_pct = b->pct; + g_prev_screen = e->screen_on; + g_prev_bri_pct = e->brightness_pct; +} + +/* 采样拍:模式热切换 + 电池入环 + CPU 差分基准推进 + 守护节拍估算(history/events 用)。 + * (PM_QUERY 的实时估算在 handle_conn 独立重算,基线 g_wifi_prev_* 查询驱动——保持期 1 已测语义。) */ static void daemon_tick(void) { int fm = boot_mode_is_meow(); @@ -687,10 +880,80 @@ static void daemon_tick(void) } batt_t b; batt_read(&b); - ring_push(&b); long t = -1, i = 0; - cpu_util_pct(g_cpu_prev_total, g_cpu_prev_idle, &t, &i); + int cu = cpu_util_pct(g_cpu_prev_total, g_cpu_prev_idle, &t, &i); g_cpu_prev_total = t; g_cpu_prev_idle = i; + est_t e; + est_compute(&g_model, g_full_mode, cu, &g_hist_wifi_prev_rx, &g_hist_wifi_prev_tx, &e); + ring_push(&b, e.degraded ? -1 : e.total_mw, + e.degraded ? -1 : e.screen_on, + e.degraded ? -1 : e.brightness_pct); + detect_events(&b, &e); +} + +/* 期 2 · history:从采样环出最近最多 count 条 + 全环窗口汇总(内存环形,重启清零)。 + * JSON: {kind:history,count,n_samples,window_sec,start_pct,end_pct,min_pct,max_pct,avg_mw,samples:[[ts,pct,est_mw,chg,scr,bri],...]} */ +static void build_history_json(uint32_t count, char *out, size_t cap) +{ + if (count == 0 || count > HIST_MAX_REPLY) count = HIST_MAX_REPLY; + int n = g_rcount < (int)count ? g_rcount : (int)count; + if (n < 0) n = 0; + int start_pct = -1, end_pct = -1, min_pct = 101, max_pct = -1, sum_mw = 0, mwden = 0; + long first_mono = 0, last_mono = 0; + if (g_rcount > 0) { + start_pct = g_ring[0].pct; + end_pct = g_ring[g_rcount - 1].pct; + first_mono = g_ring[0].mono; + last_mono = g_ring[g_rcount - 1].mono; + for (int i = 0; i < g_rcount; i++) { + if (g_ring[i].pct >= 0) { + if (g_ring[i].pct < min_pct) min_pct = g_ring[i].pct; + if (g_ring[i].pct > max_pct) max_pct = g_ring[i].pct; + } + if (g_ring[i].est_mw >= 0) { sum_mw += g_ring[i].est_mw; mwden++; } + } + if (min_pct == 101) min_pct = -1; + } + jw_t w = { out, cap, 0 }; + out[0] = 0; + jadd(&w, "{\"kind\":\"history\",\"count\":%d,\"n_samples\":%d,\"window_sec\":%ld," + "\"start_pct\":%d,\"end_pct\":%d,\"min_pct\":%d,\"max_pct\":%d,\"avg_mw\":%d,\"samples\":[", + n, g_rcount, g_rcount > 0 ? last_mono - first_mono : 0, + start_pct, end_pct, min_pct, max_pct, mwden ? sum_mw / mwden : -1); + int start = g_rcount - n; + for (int i = start; i < g_rcount; i++) { + const hist_sample_t *s = &g_ring[i]; + jadd(&w, "%s[%ld,%d,%d,%d,%d,%d]", i > start ? "," : "", + s->mono, s->pct, s->est_mw, s->charging, s->screen, s->brightness_pct); + } + jadd(&w, "]}"); +} + +/* 期 2 · events:从事件环出最近最多 count 条(内存环形,重启清零)。 + * JSON: {kind:events,count,events:[[ts,type,pct,txt],...]} + * 单事件最坏 ~160B(txt 长 ≤112)→ 回帧上限收紧到 EV_REPLY_MAX,防 JSON 撑爆 MEOW_IPC_MAX。 */ +#define EV_REPLY_MAX ((int)((MEOW_IPC_MAX - 12 - 64) / 160)) +static void build_events_json(uint32_t count, char *out, size_t cap) +{ + if (count == 0) count = 20; + if ((int)count > EVENT_RING_N) count = EVENT_RING_N; + if ((int)count > EV_REPLY_MAX) count = (uint32_t)EV_REPLY_MAX; + int tot = g_ev_count < EVENT_RING_N ? g_ev_count : EVENT_RING_N; + int n = tot < (int)count ? tot : (int)count; + if (n < 0) n = 0; + int start = tot - n; + jw_t w = { out, cap, 0 }; + out[0] = 0; + jadd(&w, "{\"kind\":\"events\",\"count\":%d,\"events\":[", n); + for (int i = start; i < tot; i++) { + const pm_event_t *ev = &g_events[i]; + const char *tn = ev->type == 0 ? "charger" + : (ev->type == 1 ? "lowbat" + : (ev->type == 2 ? "screen" : "brightness")); + jadd(&w, "%s[%ld,\"%s\",%d,\"%s\"]", i > start ? "," : "", + ev->mono, tn, ev->pct, ev->txt); + } + jadd(&w, "]}"); } static void handle_conn(int c) @@ -736,10 +999,31 @@ static void handle_conn(int c) char json[MEOW_IPC_MAX]; build_json(q.subset, g_full_mode, &b, have_rate, rate_x100, &e, json, sizeof json); - send_pm_result(c, req_id, q.subset, json); + send_pm_result(c, MSG_PM_RESULT, req_id, q.subset, json); continue; } - send_err(c, req_id, MEOW_ERR_INVAL, "unknown type (期1仅 PM_QUERY)"); + + if (type == MSG_PM_HISTORY) { + /* 期 2:历史采样查询(读级任意调用者)。req: u32 count */ + uint32_t count = 0; + if (rlen >= 4) memcpy(&count, payload, 4); + char json[MEOW_IPC_MAX]; + build_history_json(count, json, sizeof json); + send_pm_result(c, MSG_PM_HISTORY_RESULT, req_id, count, json); + continue; + } + + if (type == MSG_PM_EVENTS) { + /* 期 2:电源事件查询(读级任意调用者)。req: u32 count */ + uint32_t count = 0; + if (rlen >= 4) memcpy(&count, payload, 4); + char json[MEOW_IPC_MAX]; + build_events_json(count, json, sizeof json); + send_pm_result(c, MSG_PM_EVENTS_RESULT, req_id, count, json); + continue; + } + + send_err(c, req_id, MEOW_ERR_INVAL, "unknown type (期1 PM_QUERY / 期2 HISTORY/EVENTS)"); break; } } @@ -890,6 +1174,59 @@ direct: } } +/* 期 2:history/events 通用 IPC 客户端(HELLO→ACK→req→RESULT 提取 JSON)。 + * 返回 0=守护返回;-1=守护不可达/协议错。history/events 无直读降级——采样环/事件环在守护内存。 */ +static int pm_ipc_json(uint16_t reqtype, const void *req, uint32_t reqlen, + uint16_t wanttype, char *out, size_t cap) +{ + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) return -1; + struct timeval tv = { 1, 500000 }; + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv); + struct sockaddr_un a; + memset(&a, 0, sizeof a); + a.sun_family = AF_UNIX; + strncpy(a.sun_path, g_sock, sizeof a.sun_path - 1); + int rc = -1; + if (connect(fd, (struct sockaddr *)&a, sizeof a) != 0) { close(fd); return -1; } + uint8_t hdr[MEOW_IPC_HDR_LEN]; + uint8_t pl[sizeof(m_pm_result_t)]; + if (send_frame(fd, MSG_HELLO, 1, NULL, 0) != 0) goto out; + if (x_read_full(fd, hdr, sizeof hdr) != 0) goto out; /* MSG_ACK 头 */ + { uint32_t al; memcpy(&al, hdr + 12, 4); if (al <= sizeof pl && al && x_read_full(fd, pl, al) != 0) goto out; } /* 丢 ACK 载荷 */ + if (send_frame(fd, reqtype, 2, req, reqlen) != 0) goto out; + if (x_read_full(fd, hdr, sizeof hdr) != 0) goto out; + uint16_t rt; uint32_t rl; + memcpy(&rt, hdr + 6, 2); + memcpy(&rl, hdr + 12, 4); + if (rt != wanttype || rl < 12 || rl > sizeof pl) goto out; + if (x_read_full(fd, pl, rl) != 0) goto out; + { + m_pm_result_t r; + memset(&r, 0, sizeof r); + memcpy(&r, pl, rl); + size_t jl = r.json_len; + if (jl >= cap) jl = cap - 1; + memcpy(out, r.json, jl); + out[jl] = 0; + rc = 0; + } +out: + close(fd); + return rc; +} + +/* 极简数组解析:跳跃分隔符后取一个整数,推进指针(自产 JSON 固定形状 [a,b,c])。 */ +static long next_int2(const char **pp) +{ + const char *p = *pp; + while (*p == ',' || *p == ' ' || *p == ']') p++; + long v = strtol(p, (char **)&p, 10); + *pp = p; + return v; +} + /* ---------------- CLI 子命令 ---------------- */ static void fmt_min(char *out, size_t cap, int min) { @@ -970,6 +1307,17 @@ static int cli_estimate(void) jstr(js, "audio_state", as, sizeof as); printf("est_mw=%d confidence=%s calibrated=false (系数全部经验初值,待真机标定)\n", total, conf[0] ? conf : "estimated"); + /* 期 2:mp2731 current_now 实测充电功率(仅充电态且 current_now>0 → measured;否则 null) */ + { + int cmw = -1; char cc[16]; + jint(js, "charge_mw", &cmw); + jstr(js, "charge_confidence", cc, sizeof cc); + if (cmw >= 0) + printf("charge=+%dmW confidence=%s (mp2731 current_now×constant_charge_voltage 实测;系统消费侧无实测节点仍为 estimated)\n", + cmw, cc[0] ? cc : "measured"); + else + printf("charge=null confidence=null (非充电态或无 current_now——充电功率无实测轨)\n"); + } char cu_s[16], bp_s[16]; if (cu >= 0) snprintf(cu_s, sizeof cu_s, "%d%%", cu); else snprintf(cu_s, sizeof cu_s, "null"); if (bp >= 0) snprintf(bp_s, sizeof bp_s, "%d%%", bp); else snprintf(bp_s, sizeof bp_s, "null"); @@ -986,7 +1334,8 @@ static int cli_model(void) pm_model_t m; int def = model_load(&m); printf("# meow_powermgr 经验功耗模型(POWER_MANAGER_DESIGN §3.2)\n"); - printf("# 系数来源: %s\n", def ? "内置默认(conf 缺失)" : g_model_conf); + printf("# 系数来源: %s\n", g_model_conf); + printf("# %s\n", def ? "⚠ conf 缺失 → 以上为内置默认(待标定)" : "conf 读取成功"); printf("# ⚠ 全部系数为经验初值,待真机标定(POWER_HW_FACTS §1 项 7/8 标定实验未做)\n"); printf("nominal_mah=%d\n", m.nominal_mah); printf("baseline_mw=%d\n", m.baseline_mw); @@ -1005,6 +1354,151 @@ static int cli_model(void) return 0; } +/* 期 2 · history:最近 N 个采样点 + 全环窗口汇总(不落盘,重启清零) */ +static int cli_history(int n, char **v) +{ + uint32_t count = 0; + if (n >= 1) { int c = atoi(v[0]); if (c > 0) count = (uint32_t)c; } + char js[MEOW_IPC_MAX]; + if (pm_ipc_json(MSG_PM_HISTORY, &count, sizeof count, MSG_PM_HISTORY_RESULT, js, sizeof js) != 0) { + fprintf(stderr, "powermgr 守护不可达:history 采样环在守护内存,需 meow_powermgr 常驻\n"); + return 1; + } + int cnt = 0, ns = 0, sp = -1, ep = -1, mnp = -1, mxp = -1, avg = -1; + long win = 0; + jint(js, "count", &cnt); jint(js, "n_samples", &ns); + jint(js, "start_pct", &sp); jint(js, "end_pct", &ep); + jint(js, "min_pct", &mnp); jint(js, "max_pct", &mxp); jint(js, "avg_mw", &avg); + { int wi = 0; jint(js, "window_sec", &wi); win = wi; } + printf("history: %d 条采样点(环共 %d,窗口 %lds=%.2fh)\n", cnt, ns, win, win / 3600.0); + if (sp >= 0) + printf(" 电量: 起 %d%% 止 %d%% 低 %d%% 高 %d%% 平均功耗 %dmW(estimated/derived,系数未标定)\n", + sp, ep, mnp, mxp, avg); + long ts[80]; int pc[80], mw[80], cg[80], sc[80], br[80]; + int m = 0; + const char *p = strstr(js, "\"samples\":["); + if (p) { + p += 11; /* "\"samples\":[" 长 11 */ + while (m < 80 && *p == '[') { + p++; /* '[' */ + ts[m] = next_int2(&p); + pc[m] = (int)next_int2(&p); + mw[m] = (int)next_int2(&p); + cg[m] = (int)next_int2(&p); + sc[m] = (int)next_int2(&p); + br[m] = (int)next_int2(&p); + m++; + if (*p == ']') { p++; if (*p == ',') p++; } + } + } + printf("\n%-10s %5s %6s %5s %5s %6s\n", "time", "cap%", "est_mW", "chg", "scr", "bri"); + long newest = m > 0 ? ts[m - 1] : 0; + for (int i = 0; i < m; i++) { + char tb[24], bs[16], bri_s[16]; + long off = newest - ts[i]; /* 距最新采样(秒,≥0) */ + if (off >= 7200) snprintf(tb, sizeof tb, "-%ldh%02ldm", off / 3600, (off % 3600) / 60); + else snprintf(tb, sizeof tb, "-%ldmin", off / 60); + snprintf(bs, sizeof bs, "%s", cg[i] ? "Chg" : "Dis"); + if (br[i] < 0) snprintf(bri_s, sizeof bri_s, "?"); else snprintf(bri_s, sizeof bri_s, "%d%%", br[i]); + printf("%-10s %5d %6d %5s %5s %6s\n", tb, pc[i], mw[i], bs, + sc[i] == 1 ? "On" : (sc[i] == 0 ? "Off" : "?"), bri_s); + } + fprintf(stderr, "note: est_mW/scr/bri 为守护 %ds 节拍估算;只读模式下为 -1/off/未知(只采样电池)\n", + g_sample_sec); + return 0; +} + +/* 期 2 · events:电源事件流(内存环形,重启清零) */ +static int cli_events(int n, char **v) +{ + uint32_t count = 0; + if (n >= 1) { int c = atoi(v[0]); if (c > 0) count = (uint32_t)c; } + char js[MEOW_IPC_MAX]; + if (pm_ipc_json(MSG_PM_EVENTS, &count, sizeof count, MSG_PM_EVENTS_RESULT, js, sizeof js) != 0) { + fprintf(stderr, "powermgr 守护不可达:events 事件环在守护内存,需 meow_powermgr 常驻\n"); + return 1; + } + int cnt = 0; jint(js, "count", &cnt); + printf("events: %d 条(内存环形,重启清零;App 前后台切换无可轮询导出点——期2诚实标注不支持)\n", cnt); + if (cnt == 0) { printf(" (无事件)\n"); return 0; } + long ts[64]; char tn[64][16]; int pct[64]; char txt[64][128]; + int m = 0; + const char *p = strstr(js, "\"events\":["); + if (p) { + p += 10; /* "\"events\":[" 长 10 */ + while (m < 64 && *p == '[') { + p++; /* '[' */ + ts[m] = next_int2(&p); + while (*p && *p != '"') p++; + if (*p == '"') p++; + int k = 0; + while (*p && *p != '"' && k < 15) tn[m][k++] = *p++; + tn[m][k] = 0; if (*p == '"') p++; + if (*p == ',') p++; + pct[m] = (int)next_int2(&p); + while (*p && *p != '"') p++; + if (*p == '"') p++; + k = 0; + while (*p && *p != '"' && k < 127) txt[m][k++] = *p++; + txt[m][k] = 0; if (*p == '"') p++; + m++; + if (*p == ']') { p++; if (*p == ',') p++; } + } + } + long newest = m > 0 ? ts[m - 1] : 0; + for (int i = m - 1; i >= 0; i--) { /* 最新在前 */ + char tb[24]; + long off = newest - ts[i]; + if (off >= 7200) snprintf(tb, sizeof tb, "-%ldh%02ldm", off / 3600, (off % 3600) / 60); + else snprintf(tb, sizeof tb, "-%ldmin", off / 60); + printf("%-8s %-10s %3d%% %s\n", tb, tn[i], pct[i], txt[i]); + } + return 0; +} + +/* 期 2 · top:功耗分解实时排序视图(2s 刷新,Ctrl-C 退;MEOW_PM_TOP_ONCE=1 一次输出用于测试)。 */ +static int cli_top(void) +{ + static const char *name[6] = { "baseline", "screen", "wifi", "bt", "audio", "cpu" }; + signal(SIGINT, on_sig); + signal(SIGTERM, on_sig); + int once = getenv("MEOW_PM_TOP_ONCE") != NULL; + do { + char js[MEOW_IPC_MAX]; + pm_query_json(PM_SUBSET_BREAKDOWN, js, sizeof js); + if (strstr(js, "\"degraded\":true")) { + printf("degraded: HibyOS 只读模式——不施功耗分解(只采样电池)\n"); + return 0; + } + int b = 0, cpu = 0, w = 0, bt = 0, au = 0, bl = 0, panel = 0; + jint(js, "baseline_mw", &b); jint(js, "cpu_mw", &cpu); + jint(js, "wifi_mw", &w); jint(js, "bt_mw", &bt); + jint(js, "audio_mw", &au); jint(js, "backlight_mw", &bl); jint(js, "panel_mw", &panel); + int mw[6] = { b, bl + panel, w, bt, au, cpu, }; + int total = 0; + for (int i = 0; i < 6; i++) total += mw[i]; + int idx[6] = { 0, 1, 2, 3, 4, 5 }; + for (int i = 0; i < 5; i++) + for (int j = i + 1; j < 6; j++) + if (mw[idx[j]] > mw[idx[i]]) { int t = idx[i]; idx[i] = idx[j]; idx[j] = t; } + if (!once) printf("\033[2J\033[H"); /* 清屏(仅终端) */ + printf("=== meow_power top 功耗分解 总 %dmW (confidence=estimated, 系数待真机标定) ===\n", total); + for (int i = 0; i < 6; i++) { + int k = idx[i]; + int pctv = total > 0 ? mw[k] * 100 / total : 0; + int nb = pctv * 40 / 100; + char bar[41]; + for (int x = 0; x < nb; x++) bar[x] = '#'; + bar[nb] = 0; + printf("%-9s %6dmW %3d%% |%-40s|\n", name[k], mw[k], pctv, bar); + } + fflush(stdout); + if (once) break; + for (int sl = 0; sl < 20 && !g_stop; sl++) usleep(100 * 1000); /* 2s,可中断 */ + } while (!g_stop); + return 0; +} + static int cli_stub(const char *what, const char *phase) { printf("meow_power %s: 期 %s 落地,当前未实现(POWER_MANAGER_DESIGN §9 分期)\n", what, phase); @@ -1013,19 +1507,18 @@ static int cli_stub(const char *what, const char *phase) static const char *cli_usage(void) { - return "status|estimate|model|history[小时]|top|events|policy(后四者期2/3桩)"; + return "status|estimate|model|history[N]|top|events[N]|policy(history/top/events 期2已落地;policy 期3桩)"; } static int cli_run(int n, char **v) { const char *cmd = v[0]; - (void)n; /* 子命令参数期 2/3 才用(history[小时] 等) */ if (!strcmp(cmd, "status")) return cli_status(); if (!strcmp(cmd, "estimate")) return cli_estimate(); if (!strcmp(cmd, "model")) return cli_model(); - if (!strcmp(cmd, "history")) return cli_stub("history", "2"); - if (!strcmp(cmd, "top")) return cli_stub("top", "2"); - if (!strcmp(cmd, "events")) return cli_stub("events", "2"); + if (!strcmp(cmd, "history")) return cli_history(n - 1, v + 1); + if (!strcmp(cmd, "top")) return cli_top(); + if (!strcmp(cmd, "events")) return cli_events(n - 1, v + 1); if (!strcmp(cmd, "policy")) return cli_stub("policy", "3"); fprintf(stderr, "usage: meow_power <%s>\n", cli_usage()); return 1; diff --git a/patch_v2/meow_powermgr/meow_powermgr_host b/patch_v2/meow_powermgr/meow_powermgr_host index c7e923ae16166335ff61e9d290d4f0cfd1b736c8..d9054e21241ab6b739ef441596521f778a36e8f8 100755 GIT binary patch literal 77488 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 <期望子串> <实际> # ---- 搭假节点 ---- 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" + "$BL" "$NET/wlan0" "$RF/rfkill0" "$BT/hci0" echo 94 > "$PS/battery/capacity" echo Discharging > "$PS/battery/status" echo 4275000 > "$PS/battery/voltage_now" @@ -31,6 +31,7 @@ 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 1 > "$NET/wlan0/carrier" # 已关联(carrier 0=未关联) echo bluetooth > "$RF/rfkill0/type" echo 1 > "$RF/rfkill0/soft" # BT 被 soft 块 → off cat > "$T/netdev" <<'EOF' @@ -43,7 +44,8 @@ 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" + MEOW_PM_MODEL=$T/model.conf MEOW_AUDIO_SOCK=$T/no_audio.sock MEOW_PM_SOCK=$T/pm.sock + MEOW_PM_NO_HCICONFIG=1" ln -sf "$(readlink -f "$BIN")" "$T/meow_power" # argv[0] 双形态 # ---- T1: 守护直读(无守护 → 降级直读) ---- @@ -92,6 +94,20 @@ sleep 3 OUT=$(env $ENVS $Q "$T/meow_power" estimate 2>/dev/null) ck T5a "wifi=600(tx)" "$OUT" +# ---- T5b: wifi 未关联(carrier=0)→ unassoc 档 ---- +echo 0 > "$NET/wlan0/carrier" +sleep 2 +OUT=$(env $ENVS $Q "$T/meow_power" estimate 2>/dev/null) +ck T5b "wifi=80(unassoc)" "$OUT" +echo 1 > "$NET/wlan0/carrier" + +# ---- T5c: BT up(rfkill 未软块 + hci0 存在;host 无 hciconfig → 回退 up) ---- +echo 0 > "$RF/rfkill0/soft" +sleep 2 +OUT=$(env $ENVS $Q "$T/meow_power" estimate 2>/dev/null) +ck T5c "bt=60(up)" "$OUT" +echo 1 > "$RF/rfkill0/soft" + # ---- T6: 息屏(actual_brightness=0)→ 背光/面板归零 ---- echo 0 > "$BL/actual_brightness" OUT=$(env $ENVS $Q "$T/meow_power" estimate 2>/dev/null) @@ -120,17 +136,59 @@ 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"; } +# ---- T9: 期 2 CLI — history(守护采样环)/policy(期3桩) ---- +sleep 1 # 再采几拍,history 有样本 +OUT=$(env $ENVS $Q "$T/meow_power" history 2>&1) +ck T9a "history:" "$OUT" +ck T9b "采样点" "$OUT" +ck T9c "est_mW" "$OUT" +echo "$OUT" | grep -q "守护不可达" && { FAIL=$((FAIL+1)); echo "FAIL T9d 应走守护"; } || { PASS=$((PASS+1)); echo "PASS T9d via-daemon"; } OUT=$(env $ENVS $Q "$T/meow_power" policy 2>&1) -ck T9c "期 3" "$OUT" +ck T9e "期 3" "$OUT" # ---- T10: meow_powermgr 名直调子命令也走 CLI ---- OUT=$(env $ENVS $Q "$BIN" model 2>/dev/null) ck T10a "nominal_mah=1600" "$OUT" +# ---- T13: mp2731 current_now 充电功率实测接入(期 2 项 5)---- +echo Charging > "$PS/mp2731-charger/status" +echo 1500000 > "$PS/mp2731-charger/current_now" +echo 4200000 > "$PS/mp2731-charger/constant_charge_voltage" +sleep 2 +OUT=$(env $ENVS $Q "$T/meow_power" estimate 2>/dev/null) +ck T13a "charge=+6300mW" "$OUT" # 4.2V×1.5A=6.3W +ck T13b "confidence=measured" "$OUT" # 充电功率升 measured +OUT=$(env $ENVS $Q "$T/meow_power" status 2>/dev/null) +ck T13c "state=Charging" "$OUT" # 权威链 mp2731=Charging +echo Discharging > "$PS/mp2731-charger/status" +echo 0 > "$PS/mp2731-charger/current_now" +sleep 2 +OUT=$(env $ENVS $Q "$T/meow_power" estimate 2>/dev/null) +ck T13d "charge=null" "$OUT" # 放电 → 无实测轨 + +# ---- T14: 电源事件流(充电器插拔 + 息屏,App 前后台诚实标注不支持)---- +OUT=$(env $ENVS $Q "$T/meow_power" events 2>&1) +ck T14a "events:" "$OUT" +ck T14b "charger" "$OUT" # 插/拔充电器事件 tag +echo 0 > "$BL/actual_brightness" # 息屏 → screen 事件 +sleep 2 +OUT=$(env $ENVS $Q "$T/meow_power" events 2>&1) +ck T14c "screen" "$OUT" +ck T14d "息屏" "$OUT" +echo 50 > "$BL/actual_brightness" # 恢复亮屏(供后测) + +# ---- T15: top 功耗分解实时排序(一次输出)---- +OUT=$(env $ENVS MEOW_PM_TOP_ONCE=1 $Q "$T/meow_power" top 2>/dev/null) +ck T15a "功耗分解" "$OUT" +ck T15b "screen" "$OUT" +ck T15c "baseline" "$OUT" +echo "$OUT" | grep -q "cpu" && { PASS=$((PASS+1)); echo "PASS T15d cpu行"; } || { FAIL=$((FAIL+1)); echo "FAIL T15d"; } + +# ---- T16: 模型系数路径(新定案 /data/meow/powermgr/power_model.conf)---- +ENVS_NM=$(echo "$ENVS" | tr ' ' '\n' | grep -v '^MEOW_PM_MODEL=' | tr '\n' ' ') +OUT=$(env $ENVS_NM $Q "$T/meow_power" model 2>/dev/null) +ck T16a "powermgr/power_model.conf" "$OUT" + # ---- T11: 守护停 → CLI 自动降级直读不崩 ---- kill $DPID 2>/dev/null; wait $DPID 2>/dev/null; DPID="" sleep 1