内容
# psu-mcp
用于可编程台式电源控制的MCP服务器,具有自主的启动加载程序入口探测功能。
MVP:Korad KA3005P / RND 320-KA3005P。通过`vendors.py`策略注册表可扩展供应商。
## 为什么
- 硬件安全参与通常依赖于通过精确计时电源循环将目标进入启动加载程序模式。手动执行此操作容易出错且不可重复。
- 驱动PSU的代理可以通过MCP自主扫描时间,观察恢复后的电流曲线,并收敛到有效模式。
- 从BK7231/ESP类提取工作流中删除了人为干预的步骤。
## 工具
| 工具 | 级别 | 目的 |
|------|------|---------|
| `connect` | 只读 | 探测PSU,验证声明的配置文件与实时VSET |
| `list_profiles` | 只读 | 返回操作员声明的配置文件槽标签 |
| `get_status` | 只读 | 实时VSET/ISET/VOUT/IOUT/output_on和声明的配置文件;如果VSET与任何声明的配置文件不匹配,则发出警告 |
| `recall_profile` | 允许写入 | 加载操作员声明的配置文件槽N。拒绝配置文件中未配置的槽。如果配置文件不匹配,则强制输出关闭 |
| `output_on` | 允许写入 | 使能输出。如果实时VSET与任何声明的配置文件不匹配,则拒绝 |
| `output_off` | 允许写入 | 禁能输出 |
| `yank_restore` | 允许写入 | 原子电源循环(可选`repeat`);如果VSET不在声明的配置文件中,则拒绝 |
| `pulse_off_observe` | 允许写入 | 电源循环 + 恢复曲线遥测采样;相同的VSET预飞行 |
## 安全模型
使用中的两个级别:
- **只读**:完全自主
- **允许写入**:自主,记录
`APPROVAL_WRITE`存在于级别枚举中,但没有MVP工具生活在那里。 MCP侧没有电压或电流设置器 - 代理不能在目标上触发一个电压,而操作员没有故意预加载到内存槽中。
### 配置文件即保护(唯一合同)
操作员在台式机上预加载PSU的内存槽M1-M5(慢速,谨慎,物理方式)。代理选择一个配置文件槽 - 它永远不会选择一个电压。电压权限是一只手放在旋钮上,而不是软件配置值。
影响输出的工具(`output_on`,`yank_restore`,`pulse_off_observe`)在入口处验证实时VSET是否等于操作员声明的配置文件毫伏值。 游离VSET(面板旋钮,电源循环后默认,前一会话)不满足此检查; 工具在代理调用`recall_profile`之前拒绝。
`recall_profile`仅接受配置文件中声明的槽。 调用后VSET与声明的毫伏值进行验证; 不匹配强制`output_off`如果它处于开启状态。 `SAV{n}`根本不暴露 - 配置文件内容是操作员的权限。
预飞行检查在工具入口处进行。 `yank_restore`和`pulse_off_observe`的时间循环对代理是原子的 - 窗口期间没有检查。 操作员纪律承担其余部分: 在序列期间不要触摸面板。
## 安装
```bash
pip install -e ".[dev]"
```
系统要求:用户必须位于`dialout`组中以进行串行访问:
```bash
sudo usermod -aG dialout $USER
# 注销并重新登录以使组membership生效
```
## 配置
首次设置:复制`config.example.json`到您想要的位置,并编辑以匹配您在PSU面板上物理加载的M1-M5槽标签:
```bash
mkdir -p ~/.config/psu-mcp
cp config.example.json ~/.config/psu-mcp/config.json
$EDITOR ~/.config/psu-mcp/config.json
```
MCP在启动时读取`PSU_CONFIG_PATH`。 在会话期间更改文件不会立即生效,直到MCP重新启动。
架构:
```json
{
"port": "/dev/ttyACM0",
"vendor": "korad_ka3005p",
"profiles": {
"1": {"mv": 3300, "label": "BK7231"},
"2": {"mv": 3300, "label": "ESP_logic"},
"3": {"mv": 5000, "label": "ESP_via_USB"},
"4": {"mv": 1800, "label": "core_logic"},
"5": {"mv": 2500, "label": "spare"}
}
}
```
`profiles`必须声明至少一个槽。 没有单独的`max_voltage_mv` / `max_current_ma`设置 - 声明的配置文件毫伏值集是隐含的允许列表,操作员的物理槽负载是权威来源。
### MCP客户端(.mcp.json)
```json
{
"mcpServers": {
"psu": {
"command": "/path/to/.venv/bin/python",
"args": ["-m", "psu_mcp"],
"env": {
"PSU_CONFIG_PATH": "/home/you/.config/psu-mcp/config.json"
}
}
}
}
```
## 参与日志记录(可选)
`yank_restore`和`pulse_off_observe`接受可选的`engagement_name`和`project_path`参数。 当提供其中任何一个时,工具在调用结束时将一个JSONL行追加到`<engagement>/uart/logs/psu.jsonl`。 该行包括完整的有效载荷 - yank的循环日志,脉冲观测的遥测数组 - 因此参与文件夹累积原始数据以供以后分析(芯片族签名库,事后证据,漂移检测)。
解析规则:
- `project_path`(绝对)-> `<project_path>/uart/logs/psu.jsonl`
- `engagement_name` -> `$PIDEV_ENGAGEMENTS_DIR/<engagement_name>/uart/logs/psu.jsonl`
- 两个都提供 -> `project_path`获胜
- 都不提供 -> 没有日志; 工具仅返回有效载荷
在MCP客户端配置文件中设置`PIDEV_ENGAGEMENTS_DIR`:
```json
{
"psu": {
"command": "/path/to/.venv/bin/python",
"args": ["-m", "psu_mcp"],
"env": {
"PSU_CONFIG_PATH": "/home/you/.config/psu-mcp/config.json",
"PIDEV_ENGAGEMENTS_DIR": "/home/you/engagements"
}
}
}
```
如果请求`engagement_name`但环境变量未设置,则工具成功返回并在`result["warnings"]`中添加警告(探测已经发生; 日志记录失败不会中止它)。
解析:`pulse_off_observe`上的10 Hz最大采样 - 足够用于稳态分类和曲线形状存档,但不适用于毫秒级波形分析。 有关权衡讨论,请参见规范。
## 操作员说明
- 在 yank 或 pulse 序列期间不要触摸面板。 循环对代理是原子的,但对 M 按钮的手不是。
- KA3005P固件没有远程锁定。 面板和串行之间最后一个命令生效。
- `connect`如果输出开启,则跳过配置文件验证 - 调用每个槽将使实时电压波动。 首先调用`output_off`。
- 如果您在台式机上更改配置文件毫伏值(例如,从5000重新加载M3为3300),请在下一次参与之前更新配置文件以匹配。 MCP在启动时加载配置文件,因此更改需要重启。
- **`yank_restore`的时间偏差:** 每个循环在输出状态转换(写入+写入后约30毫秒延迟)期间添加约31毫秒的Korad串行结算开销。 请求`off_ms=1500`产生`off_ms_actual`约1531毫秒; 偏差一致且确定。 如果您针对HITL重试循环调整时间窗口,请考虑它 - 芯片看到的实际无电源时间是芯片看到的,不是请求的。
- **通过电流读回的启动加载程序入口检查:** yank后,调用`get_status`并读取`iout_ma`。 <15 mA = 芯片在ROM启动加载程序中(低静态,监听UART)。 40-70 mA = 应用程序固件仍在运行(yank失败 - 尝试更大的`off_ms`; 在带有外部3.3V电源的FT232/CH340 UART桥接器上,已测得1500毫秒作为BK7231N的工作底限,而500毫秒可重复失败)。 约0 mA + 低`vout_mv` = 芯片没有重新上电(yank太长)。 这是一个免费的侧通道,不需要UART访问。
## 测试
```bash
pytest # 129个单元测试,不需要硬件
pytest tests/ -m hardware # 6个集成测试,需要实际的KA3005P
```
硬件套件期望PSU面板槽1加载3300 mV; 测试夹具声明一个配置文件来匹配。 使用`PSU_TEST_PORT=/dev/ttyACMx`覆盖端口。
## 许可证
MIT