rack:主板 / 底板功率与功率曲线;底板曲线加电压电流、开机重按事件
- 每路主板功率(V×I):总览槽位、单板卡片、单路曲线新增「功率」一张 - 底板:单板页新增功率仪表(底板输入 / 五路主板合计 / 损耗),底板曲线加 功率图(输入 vs 主板合计);board_history 每点多回一列 rails_mw - 底板曲线改为纵向 N 张(温度 / 风扇 / 输入电压 / 输入电流 / 功率),断档断线 - LOG_EV_BOOT_CHECK(19)事件命名,只有「没开起来」标红 Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
@@ -35,6 +35,7 @@
|
||||
<div class="bl" :title="phone(b, i).label">{{ phone(b, i).label || ' ' }}</div>
|
||||
<template v-if="rail(b, i)?.pr">
|
||||
<div class="bv readout">{{ mA(rail(b, i)?.ua) }}<small>mA</small></div>
|
||||
<div class="bw readout">{{ W(rail(b, i)?.mv, rail(b, i)?.ua) }}<small>W</small></div>
|
||||
<Spark :board="b.id" :rail="i" :color="loud(b, i) ? LEVEL_COLOR[health(b, i).level] : 'var(--ink-2)'" />
|
||||
</template>
|
||||
<div class="bs" :style="{ color: loud(b, i) ? LEVEL_COLOR[health(b, i).level] : 'var(--muted)' }">{{ health(b, i).text }}</div>
|
||||
@@ -44,6 +45,8 @@
|
||||
<footer class="pf" v-if="b.status">
|
||||
<div><span class="k">输入</span><span class="readout">{{ V(b.status.in?.mv) }}</span> V
|
||||
<span class="readout">{{ A(b.status.in?.ua) }}</span> A</div>
|
||||
<div><span class="k">底板功率</span><span class="readout">{{ W(b.status.in?.mv, b.status.in?.ua) }}</span> W
|
||||
<span class="k sub">主板</span><span class="readout">{{ railsW(b) }}</span> W</div>
|
||||
<div><span class="k">温度</span>
|
||||
<span v-for="(t, k) in b.status.temp || []" :key="k" class="readout t">{{ t == null ? '—' : C(t) }}</span> °C</div>
|
||||
<div><span class="k">风扇</span><span class="readout">{{ (b.status.fan?.duty ?? 0) / 10 }}</span> %
|
||||
@@ -62,7 +65,7 @@
|
||||
<time class="muted readout">{{ fmtTime(f.ts) }}</time>
|
||||
<span class="who">{{ boardName(f.board) }}</span>
|
||||
<template v-if="f.kind === 'event'">
|
||||
<span :class="{ bad: SEVERE.has(f.ev[3]) }">{{ f.ev[2] ? `#${f.ev[2]} ` : '' }}{{ evName(f.ev[3]) }}</span>
|
||||
<span :class="{ bad: isSevere(f.ev) }">{{ f.ev[2] ? `#${f.ev[2]} ` : '' }}{{ evName(f.ev[3]) }}</span>
|
||||
</template>
|
||||
<template v-else-if="f.kind === 'audit'">
|
||||
<span>{{ f.row.user }} 对 #{{ f.row.rail ?? '' }} 执行 {{ f.row.request.cmd }}:{{ rspText(f.row.response) }}</span>
|
||||
@@ -82,16 +85,19 @@ import { Refresh } from '@element-plus/icons-vue'
|
||||
import Spark from '../components/Spark.vue'
|
||||
import { state } from '../store'
|
||||
import { rspText } from '../api'
|
||||
import { mA, V, C, ago, fmtTime, railHealth, LEVEL_COLOR, RECOVERY_ZH, REASON_ZH } from '../util'
|
||||
import { mA, V, W, C, ago, fmtTime, railsMw, railHealth, LEVEL_COLOR, RECOVERY_ZH, REASON_ZH } from '../util'
|
||||
|
||||
// 与 backend/prrack/protocol.py 的 EVENT_TYPES / EVENT_SEVERE 同一张表(数字来自固件 evlog.h)
|
||||
const EV = ['', '板子上电', '事件丢失', '过压关路', '欠压关路', '反灌断 VBUS', '顶高断 VBUS', 'eFuse 故障',
|
||||
'PG 变化', 'I²C 失联', '过温降载', '风扇停转', '重启序列', '命令被拒', 'mux 失联全断', '刀片不在位', 'PD 限功率',
|
||||
'上电等不到 PG', '固件升级']
|
||||
'上电等不到 PG', '固件升级', '开机重按']
|
||||
const SEVERE = new Set([3, 4, 5, 6, 7, 9, 11, 14, 17])
|
||||
// 开机重按(19)只有 arg bit7「最终没开起来」才算严重
|
||||
const isSevere = ev => SEVERE.has(ev[3]) || (ev[3] === 19 && (ev[4] & 0x80) !== 0)
|
||||
const evName = t => EV[t] || `事件 ${t}`
|
||||
|
||||
const A = ua => (ua == null ? '—' : (ua / 1e6).toFixed(2))
|
||||
const railsW = b => { const s = railsMw(b.status?.rails); return s == null ? '—' : (s / 1000).toFixed(2) }
|
||||
const boards = computed(() => Object.values(state.boards).sort((a, b) =>
|
||||
(a.location + a.name + a.id).localeCompare(b.location + b.name + b.id)))
|
||||
const rail = (b, i) => b.status?.rails?.[i - 1]
|
||||
@@ -152,12 +158,15 @@ const tally = computed(() => {
|
||||
.bl { font-size: 12px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.bv { font-size: 24px; line-height: 1.1; color: var(--ink); margin-top: 2px; }
|
||||
.bv small { font-size: 11px; color: var(--muted); margin-left: 2px; font-family: var(--ui); }
|
||||
.bw { font-size: 13px; color: var(--ink-2); line-height: 1.2; }
|
||||
.bw small { font-size: 10px; color: var(--muted); margin-left: 2px; font-family: var(--ui); }
|
||||
.bs { font-size: 12px; font-weight: 600; margin-top: auto; }
|
||||
.bay.off .bv, .bay.lost .bv { color: var(--muted); }
|
||||
.bay.off .bv, .bay.lost .bv, .bay.off .bw, .bay.lost .bw { color: var(--muted); }
|
||||
|
||||
.pf { display: flex; flex-wrap: wrap; gap: 4px 18px; font-size: 13px; color: var(--ink-2);
|
||||
border-top: 1px solid var(--line-soft); padding-top: 10px; }
|
||||
.pf .k { color: var(--muted); margin-right: 6px; }
|
||||
.pf .k.sub { margin-left: 6px; }
|
||||
.pf .readout { font-size: 15px; color: var(--ink); }
|
||||
.pf .t + .t::before { content: '/'; margin: 0 4px; color: var(--muted); }
|
||||
|
||||
|
||||
在新工单中引用
屏蔽一个用户