186 lines
5.9 KiB
Python
186 lines
5.9 KiB
Python
"""IDS 数据共享模块。
|
||
|
||
提供 JSON 加载和 HTML 表格渲染函数,
|
||
供 render_ids_acq_map / render_ids_exc_map / render_ids_daq_reg / render_ids_awg_reg 复用。
|
||
"""
|
||
|
||
import json
|
||
from pathlib import Path
|
||
from collections import OrderedDict
|
||
|
||
# 输出顺序(先列出的模块排在前面)
|
||
ACQ_MODULE_ORDER = ["MCU_INS", "MCU_DAT", "DAQ_REG", "DAQ_PAR", "DAQ_FLT"]
|
||
EXC_MODULE_ORDER = ["MCU_INS", "MCU_DAT", "AWG_REG", "AWG_IDX", "AWG_WVE"]
|
||
|
||
|
||
# ---------- 数据加载 ----------
|
||
|
||
def _load_ids(filepath: str) -> dict:
|
||
"""加载 IDS JSON 文件并返回解析后的 dict。"""
|
||
path = Path(filepath)
|
||
if not path.exists():
|
||
raise FileNotFoundError(f"IDS 文件不存在: {path}")
|
||
with open(path, "r", encoding="utf-8") as f:
|
||
return json.load(f)
|
||
|
||
|
||
# ---------- HTML 工具 ----------
|
||
|
||
def _td(text: str, **kwargs) -> str:
|
||
"""生成 <td> 单元格,支持 rowspan / style 等属性。"""
|
||
attrs = " ".join(f'{k}="{v}"' for k, v in kwargs.items() if v is not None)
|
||
return f"<td {attrs}>{text}</td>" if attrs else f"<td>{text}</td>"
|
||
|
||
|
||
def _th(text: str) -> str:
|
||
return f"<th>{text}</th>"
|
||
|
||
|
||
def _bit_range_str(bits: list) -> str:
|
||
"""将 [15, 8] 或 [7] 转为位段字符串。"""
|
||
if len(bits) == 1:
|
||
return str(bits[0])
|
||
return f"{bits[0]}:{bits[-1]}"
|
||
|
||
|
||
# ---------- 映射表渲染 ----------
|
||
|
||
def render_mapping_acq_table(data: dict) -> str:
|
||
"""渲染 msmt_acq 通道模块基地址表。"""
|
||
mi = data.get("MappingInfo", {}).get("msmt_acq", {})
|
||
return _render_mapping_table(mi, ACQ_MODULE_ORDER, "ACQ")
|
||
|
||
|
||
def render_mapping_exc_table(data: dict) -> str:
|
||
"""渲染 msmt_exc 通道模块基地址表。"""
|
||
mi = data.get("MappingInfo", {}).get("msmt_exc", {})
|
||
return _render_mapping_table(mi, EXC_MODULE_ORDER, "EXC")
|
||
|
||
|
||
def _render_mapping_table(mapping: dict, module_order: list, label: str) -> str:
|
||
"""通用的通道模块基地址渲染。"""
|
||
rows = []
|
||
# 收集所有模块名
|
||
all_modules = set()
|
||
for ch_info in mapping.values():
|
||
all_modules.update(ch_info.get("Modules", {}).keys())
|
||
|
||
# 按 module_order 排序,未在列表中的放最后
|
||
ordered = [m for m in module_order if m in all_modules]
|
||
ordered += sorted(all_modules - set(module_order))
|
||
|
||
for mod_name in ordered:
|
||
for ch in sorted(mapping.keys(), key=int):
|
||
info = mapping[ch]
|
||
modules = info.get("Modules", {})
|
||
if mod_name not in modules:
|
||
continue
|
||
m = modules[mod_name]
|
||
exaddr = info.get("Exaddr", "")
|
||
cid = info.get("Cid", "")
|
||
rows.append(
|
||
"<tr>"
|
||
f"<td>ch{ch}</td>"
|
||
f"<td>{exaddr}</td>"
|
||
f"<td>{cid}</td>"
|
||
f"<td><code>{mod_name}</code></td>"
|
||
f"<td><code>{m.get('StartAddress', '')}</code></td>"
|
||
f"<td>{m.get('Size', '')}</td>"
|
||
f"<td>{m.get('Info', '')}</td>"
|
||
"</tr>"
|
||
)
|
||
|
||
if not rows:
|
||
return "<p>(无映射数据)</p>"
|
||
|
||
header = (
|
||
"<tr>"
|
||
"<th>通道</th><th>Exaddr</th><th>Cid</th>"
|
||
"<th>模块</th><th>起始地址</th><th>大小</th><th>说明</th>"
|
||
"</tr>"
|
||
)
|
||
return f"<table>{header}{''.join(rows)}</table>"
|
||
|
||
|
||
# ---------- 寄存器表渲染 ----------
|
||
|
||
def render_daq_reg_table(data: dict) -> str:
|
||
"""渲染 DAQ_REG 寄存器汇总表 + 位域明细。"""
|
||
regs = data.get("Modules", {}).get("DAQ_REG", {})
|
||
return _render_reg_table(regs, "DAQ_REG")
|
||
|
||
|
||
def render_awg_reg_table(data: dict) -> str:
|
||
"""渲染 AWG_REG 寄存器汇总表 + 位域明细。"""
|
||
regs = data.get("Modules", {}).get("AWG_REG", {})
|
||
return _render_reg_table(regs, "AWG_REG")
|
||
|
||
|
||
def _render_reg_table(regs: dict, label: str) -> str:
|
||
"""通用寄存器表渲染(汇总表 + 每位域明细)。"""
|
||
if not regs:
|
||
return "<p>(无寄存器数据)</p>"
|
||
|
||
# 按偏移地址排序
|
||
sorted_regs = sorted(regs.values(), key=lambda r: int(r.get("OffsetAddress", "0x0"), 16))
|
||
|
||
# ---- 汇总表 ----
|
||
summary_rows = []
|
||
for r in sorted_regs:
|
||
name = list(regs.keys())[list(regs.values()).index(r)]
|
||
offset = r.get("OffsetAddress", "")
|
||
perm = r.get("Permission", "")
|
||
desc = r.get("SegDescription", "")
|
||
summary_rows.append(
|
||
"<tr>"
|
||
f"<td><strong>{name}</strong></td>"
|
||
f"<td><code>{offset}</code></td>"
|
||
f"<td>{perm}</td>"
|
||
f"<td>{desc}</td>"
|
||
"</tr>"
|
||
)
|
||
|
||
summary = (
|
||
"<table>"
|
||
"<tr><th>寄存器名</th><th>偏移地址</th><th>权限</th><th>描述</th></tr>"
|
||
f"{''.join(summary_rows)}"
|
||
"</table>"
|
||
)
|
||
|
||
# ---- 位域明细表(仅含字段数 > 0 的寄存器) ----
|
||
detail_parts = []
|
||
for r in sorted_regs:
|
||
fields = r.get("Fields", [])
|
||
if not fields:
|
||
continue
|
||
name = list(regs.keys())[list(regs.values()).index(r)]
|
||
offset = r.get("OffsetAddress", "")
|
||
|
||
field_rows = []
|
||
for f in fields:
|
||
bits = f.get("Bits", [])
|
||
field_name = f.get("FieldName", "")
|
||
fdesc = (f.get("FieldDescription", "") or "").replace("\n", "<br>")
|
||
reset = f.get("ResetValue", "")
|
||
field_rows.append(
|
||
"<tr>"
|
||
f"<td><code>[{_bit_range_str(bits)}]</code></td>"
|
||
f"<td><strong>{field_name}</strong></td>"
|
||
f"<td>{reset}</td>"
|
||
f"<td>{fdesc}</td>"
|
||
"</tr>"
|
||
)
|
||
|
||
if field_rows:
|
||
detail_parts.append(
|
||
f"<h4>{name}({offset})</h4>"
|
||
"<table>"
|
||
"<tr><th>位段</th><th>字段名</th><th>复位值</th><th>描述</th></tr>"
|
||
f"{''.join(field_rows)}"
|
||
"</table>"
|
||
)
|
||
|
||
if detail_parts:
|
||
return f"{summary}<br>{''.join(detail_parts)}"
|
||
return summary
|