内容
# FDC+ 串行驱动服务器
<img src="images/mits-logo.svg" alt="MITS Altair 8800" width="180" align="right" />
适用于 **MITS Altair 8800** 上的 **FDC+ 增强型软盘控制器** 的 TypeScript 串行磁盘服务器。通过串行提供虚拟软盘、 cassette 音频和 VT102 终端,并具有用于控制的 Svelte 5 网页界面。
**版本:** 2.0.1-rc4 · **许可证:** GPL-3.0

---
## 快速入门
```bash
# 克隆,安装两个树,运行后端 + 前端开发服务器
git clone <repo-url> fdcplus-web && cd fdcplus-web
pnpm install # pnpm 工作区:提供根目录 + frontend/
pnpm dev:all # 后端(ts-node)和 Vite 开发服务器并发
open http://localhost:3000 # 挂载示例磁盘镜像;无需硬件
```
有关连接的 Altair(Pi 目标),请参阅 [安装](#installation) 和下面的 GPIO 部分。
## 支持的平台
| 平台 | 状态 | 备注 |
|---|---|---|
| Linux(树莓派和 x86) | ✅ 已测试 | 主要生产目标;GPIO LED 支持 |
| macOS | ✅ 已测试 | 开发/探索;串行通过 `/dev/cu.usbserial-*` 工作 |
| Windows | ⚠️ 未测试 | 理论上应该工作(Node.js + SerialPort),但没有贡献者运行。欢迎 PR。 |
| Docker | ✅ | 包含 `docker-compose.yml` |
## 贡献和架构
- [`CONTRIBUTING.md`](CONTRIBUTING.md) — 如何发送 PR,PR 前的检查流程
- [`_bmad-output/project-context.md`](_bmad-output/project-context.md)(gitignored)— AI 代理上下文,包含负载信息:令牌、基元、堆栈陷阱、旧硬件不变性
---
## 概述
FDC+ 串行磁盘服务器的 TypeScript 实现。它通过串行使用 FDC+ 线路协议,因此它与未经修改的 Altair 8800 上的现有 FDC+ 增强型软盘控制器硬件一起工作。功能:
- 现代 async/await 架构,模块化 Express 后端
- 类型安全的协议实现
- 结构化日志(pino),具有详细/调试模式
- 跨平台支持(Linux + macOS 已测试;Windows 未测试)
- **Svelte 5 + Tailwind 4 网页界面**,具有实时 Socket.IO 状态更新
- **VT102 终端模拟器**,用于第二个串行端口(可选 CRT 荧光模式)
- **MCP 服务器**,具有 38 个工具,用于 AI 助手集成 — 本地(stdio)和远程(HTTP with bearer auth);参见 [AI 助手集成](#ai-assistant-integration-mcp)
- **GPIO LED 状态指示器**,用于树莓派
- **OpenAPI/Swagger 文档**,位于 `/api/docs`(也作为 [`openapi.json`](openapi.json) 提交)
- SQLite 数据库,具有 WAL 模式,用于持久状态
- Docker 和 Debian 软件包部署支持
---
## 架构
代码库组织成模块化后端和 Svelte 单页应用程序前端:
```
src/
├── index.ts # 入口点,带有 CLI 解析
├── server.ts # 主服务器循环和命令处理
├── drive.ts # 磁盘管理 和 磁盘 I/O
├── serial.ts # FDC+ 串行端口通信
├── terminal-serial.ts # 终端串行端口管理器
├── web-server.ts # Express 调谐器(组合下面的模块)
├── protocol.ts # FDC+ 协议定义 和 类型
├── config.ts # 配置文件加载 和 验证
├── database.ts # SQLite 数据库管理
├── mcp-server.ts # MCP 服务器(32 个 AI 可访问工具)
├── openapi-def.ts # OpenAPI/Swagger 规范
├── types.ts # 共享 TypeScript 类型
├── routes/ # Express 路由处理程序
│ ├── cassettes.ts # 磁带管理
│ ├── config.ts # 运行时配置
│ ├── cpm.ts # CP/M 文件系统实用程序
│ ├── disk-serving.ts # 磁盘镜像服务控制
│ ├── drives.ts # 驱动器安装/卸载/状态
│ ├── health.ts # 健康检查端点
│ ├── images.ts # 磁盘镜像库
│ ├── replay.ts # 脚本重放 和 XMODEM 传输
│ ├── scripts.ts # 脚本管理
│ ├── serial.ts # 串行端口管理
│ └── terminal.ts # 终端串行管理
├── services/ # 业务逻辑
│ ├── audio.ts # 音频/磁带处理
│ ├── disk-serving.ts # 磁盘服务逻辑
│ ├── file-listing.ts # 文件/目录列表
│ ├── status.ts # 状态广播
│ └── transfer.ts # 文件传输逻辑
├── middleware/ # Express 中间件
│ ├── auth.ts # API 密钥身份验证
│ ├── security.ts # CORS、CSP、速率限制
│ └── static.ts # 静态文件服务 和 Swagger UI
├── websocket/
│ └── handlers.ts # Socket.IO 事件处理程序
├── utils/
│ └── safe-path.ts # 路径遍历预防
└── gpio/ # GPIO LED 状态指示器
├── gpio-manager.ts
└── gpio-controller.ts
frontend/ # Svelte 5 + Vite + Tailwind 4 SPA
├── src/
│ ├── App.svelte # 根应用程序组件
│ ├── lib/
│ │ ├── components/ # 可重用 UI 组件
│ │ │ ├── chat/ # AI 助手面板
│ │ │ └── shared/ # 状态LED、LedPanel、Toast
│ │ ├── pages/ # 页面组件(终端、磁盘、磁带、脚本、配置)
│ │ ├── services/ # API 客户端 和 Socket.IO
│ │ ├── stores/ # Svelte 存储(状态管理)
│ │ └── types/ # TypeScript 类型定义
│ └── main.ts
└── vite.config.ts
```
### 关键模块
- **server.ts / protocol.ts**:FDC+ 命令处理(STAT、READ、WRIT)和协议定义
- **drive.ts**:使用 Node.js fs/promises 的异步磁盘镜像 I/O
- **serial.ts / terminal-serial.ts**:FDC+ 控制器和 VT102 终端的串行端口通信
- **web-server.ts**:Express 调谐器,组合路由、服务和中间件模块
- **routes/**:11 个 Express 路由模块,涵盖驱动器、镜像、磁带、脚本、终端等
- **services/**:与 HTTP 处理分离的业务逻辑(状态、传输、音频、文件列表)
- **middleware/**:安全(Helmet、CORS、速率限制)、API 密钥身份验证和静态文件服务
- **mcp-server.ts** / **mcp-http.ts**:MCP 服务器,公开 38 个工具用于 AI 助手集成。`mcp-server.ts` 构建工具注册表;`mcp-http.ts` 在 Web 服务器上安装 Streamable HTTP 传输(需选择加入)。Stdio 传输直接通过 `--mcp` 从 `mcp-server.ts` 提供。参见 [AI 助手集成](#ai-assistant-integration-mcp)。
- **frontend/**:现代 Svelte 5 SPA,具有实时 Socket.IO 更新、xterm.js 终端和复古 CRT 模式
---
## 安装
### 选项 1:Debian 软件包(推荐用于树莓派/Raspbian)
在树莓派或 Debian 基础系统上安装的最简单方法是使用 Debian 软件包:
```bash
# 构建软件包
make deb
# 安装软件包
sudo dpkg -i ../fdcsds_2.0.0-1_all.deb
sudo apt-get install -f
# 配置
sudo nano /etc/fdcsds/fdcsds.config.json
# 启动服务
sudo systemctl start fdcsds
sudo systemctl enable fdcsds
```
参见 [DEBIAN-PACKAGE.md](DEBIAN-PACKAGE.md),获取完整的 Debian 软件包文档。
### 选项 2:从源代码
#### 先决条件
- **Node.js** 22+
- **npm** 或 **yarn**
- 串行端口访问权限
#### 安装依赖项
```bash
npm install
```
**macOS/Windows 开发人员注意:** 如果 `npm install` 由于可选的 `onoff` GPIO 包而失败,请使用:
```bash
npm install --no-optional
```
这将安装:
- `serialport` - 串行端口通信
- `commander` - CLI 参数解析
- `express` - Web 服务器框架
- `socket.io` - WebSocket 通信
- `cors` - 跨源资源共享
- `onoff` - GPIO 控制(可选,仅限 Linux)
- `typescript` - TypeScript 编译器
### 构建
```bash
npm run build
```
这会将 TypeScript 源代码编译为 `dist/` 目录中的 JavaScript。
### 全局安装(可选)
```bash
npm install -g .
```
这将全局安装 `fdcsds` 命令。
---
## 用法
### 基本用法
```bash
# 开发模式(ts-node)
npm run dev -- -p /dev/ttyUSB0 -0 disks/cpm22.dsk
# 生产模式(编译)
npm start -- -p /dev/ttyUSB0 -0 disks/cpm22.dsk
# 或如果已全局安装
fdcsds -p /dev/ttyUSB0 -0 disks/cpm22.dsk
```
### 配置文件
服务器支持配置文件以简化启动并避免冗长的命令行。
#### 默认位置
服务器自动在以下位置搜索配置文件(按顺序):
1. `.fdcsds.config`
2. `.config/fdcsds.json`
3. `fdcsds.config.json`
#### 创建配置文件
生成示例配置文件:
```bash
fdcsds --example-config > .fdcsds.config
```
或复制示例:
```bash
cp .fdcsds.config.example .fdcsds.config
```
#### 配置文件格式
配置文件使用 JSON 格式:
```json
{
"port": "/dev/ttyUSB0",
"baud": 230400,
"drive0": "disks/cpm22.dsk",
"drive1": "disks/games.dsk",
"drive2": null,
"drive3": null,
"readonly": [0],
"verbose": false,
"debug": false,
"logFile": null,
"web": true,
"webPort": 3000,
"webHost": "localhost",
"apiKey": null,
"enableMcpHttp": false,
"terminalPort": "/dev/ttyUSB1",
"terminalBaud": 9600,
"terminalAutoconnect": false,
"gpioLeds": {
"enabled": false
}
}
```
#### 使用配置文件
**使用默认位置:**
```bash
# 创建配置文件
fdcsds --example-config > .fdcsds.config
# 编辑它,然后运行:
fdcsds
```
**使用自定义位置:**
```bash
fdcsds --config /path/to/myconfig.json
# 或
fdcsds -c myconfig.json
```
**使用 CLI 参数覆盖配置:**
```bash
# 使用配置文件但覆盖端口
fdcsds -c production.config -p /dev/ttyUSB1
```
**注意:** 命令行参数始终优先于配置文件设置。
---
### 命令行选项
```
用法:fdcsds [选项] -p <端口>
选项:
-p, --port <设备> FDC+ 的串行端口(如果配置文件中未指定,则必填)
-b, --baud <速率> 设置 FDC+ 串行端口速度(默认:230400)
-0, --drive0 <文件> 将磁盘镜像挂载到驱动器 0
-1, --drive1 <文件> 将磁盘镜像挂载到驱动器 1
-2, --drive2 <文件> 将磁盘镜像挂载到驱动器 2
-3, --drive3 <文件> 将磁盘镜像挂载到驱动器 3
-r, --readonly <n> 使驱动器 0-3 只读
-v, --verbose 详细显示
-d, --debug 调试模式
--log-file <路径> 日志文件路径(启用基于文件的日志记录)
-w, --web 启用 Web 界面(默认:禁用)
--web-port <端口> Web 界面端口(默认:3000)
--web-host <主机> Web 界面主机(默认:localhost)
--terminal-port <设备> 终端的第二个串行端口
--terminal-baud <速率> 终端端口波特率(默认:9600)
--terminal-autoconnect 启动时自动连接终端端口
--gpio-leds 启用 GPIO LED 状态指示器(树莓派)
--no-gpio-leds 禁用 GPIO LED 状态指示器
--gpio-active-low 使用 LED 的低电平逻辑
-c, --config <文件> 配置文件路径
--example-config 打印示例配置文件并退出
--mcp 以 stdio 模式启动为 MCP 服务器(阻塞;不是 Web 服务器)
--mcp-http 在 Web 服务器上通过 /mcp 提供 MCP(需要 apiKey;参见 AI 助手集成)
-h, --help 显示帮助信息
```
### 支持的波特率
- 9600
- 19200
- 38400
- 57600
- 76800
- 230400(默认)
- 403200(仅限 macOS)
- 460800
### 示例
**挂载多个驱动器:**
```bash
fdcsds -p /dev/ttyUSB0 \
-0 disks/cpm22.dsk \
-1 disks/altdos.dsk \
-2 disks/basic.dsk
```
**只读驱动器:**
```bash
fdcsds -p /dev/ttyUSB0 -0 disks/cpm22.dsk -r 0
```
**自定义波特率:**
```bash
fdcsds -p /dev/ttyUSB0 -b 460800 -0 disks/cpm22.dsk
```
**详细模式:**
```bash
fdcsds -p /dev/ttyUSB0 -v -0 disks/cpm22.dsk
```
**启用 Web 界面:**
```bash
fdcsds -p /dev/ttyUSB0 -0 disks/cpm22.dsk -w
# 访问:http://localhost:3000
```
**自定义 Web 界面主机和端口:**
```bash
fdcsds -p /dev/ttyUSB0 -0 disks/cpm22.dsk -w --web-port 8080 --web-host 0.0.0.0
# 访问:http://0.0.0.0:8080
```
**两个串行端口(FDC+ 和终端):**
```bash
fdcsds -p /dev/ttyUSB0 -0 disks/cpm22.dsk \
--terminal-port /dev/ttyUSB1 --terminal-baud 9600 -w
# 终端可通过 Web 界面访问
```
**启动时自动连接终端:**
```bash
fdcsds -p /dev/ttyUSB0 -0 disks/cpm22.dsk \
--terminal-port /dev/ttyUSB1 --terminal-autoconnect -w
```
**使用配置文件:```
# 生成并编辑配置文件
fdcsds --example-config > .fdcsds.config
nano .fdcsds.config
# 运行配置(不需要其他参数!)
fdcsds
# 或使用自定义配置文件位置
fdcsds --config production.config
```
**GPIO LED 状态指示器(树莓派):```
# 启用 GPIO LED 默认情况下启用
fdcsds -p /dev/ttyUSB0 -0 disks/cpm22.dsk --gpio-leds -w
# GPIO LED 显示驱动器和终端的实时状态
# 有关布线和配置详细信息,请参阅文档/GPIO-LEDS.md
```
---
## Web 界面
服务器包含一个可选的 Web 界面,用于远程监控和控制。
### 功能
- **实时状态更新**:通过 WebSocket 实现实时驱动器状态
- **驱动器管理**:远程挂载/卸载磁盘镜像
- **VT102 终端模拟器**:浏览器中的交互式串行控制台
- **多个串行端口**:独立的 FDC+ 和终端连接
- **REST API**:用于自动化的完整 HTTP API
### 启用 Web 界面
使用 `-w` 标志启动服务器:
```bash
fdcsds -p /dev/ttyUSB0 -0 disks/cpm22.dsk -w
```
然后打开浏览器访问:**http://localhost:3000**
### Web 界面部分
#### 1. 驱动器管理
- 查看已挂载的驱动器及其状态
- 通过下拉菜单挂载/卸载磁盘镜像
- 切换只读保护
- 实时跟踪位置和负载状态
#### 2. VT102 终端模拟器
- 完整的 xterm.js 终端模拟器
- 可配置的串行端口设置:
- 波特率:9600 - 115200
- 数据位:5、6、7、8
- 停止位:1、2
- 奇偶校验:无、偶数、奇数、标记、空格
- 流控:无、硬件、软件
- 串行端口选择来自可用设备
- 连接/断开控制
- 清屏功能
- 完整的 VT102 转义序列支持
### REST API 端点
#### 驱动器管理
```
GET /api/status 获取完整服务器状态
GET /api/drives 获取所有驱动器状态
GET /api/images 列出可用磁盘镜像
POST /api/drives/:id/mount 挂载磁盘镜像
POST /api/drives/:id/unmount 卸载驱动器
PUT /api/drives/:id/readonly 设置写保护
```
#### 终端管理
```
GET /api/terminal/status 获取终端连接状态
GET /api/terminal/ports 列出可用串行端口
POST /api/terminal/open 打开终端串行端口
POST /api/terminal/close 关闭终端串行端口
PUT /api/terminal/config 更新终端配置
```
#### 示例 API 用法
```bash
# 获取服务器状态
curl http://localhost:3000/api/status
# 挂载磁盘镜像
curl -X POST http://localhost:3000/api/drives/0/mount \
-H "Content-Type: application/json" \
-d '{"filename":"cpm22.dsk"}'
# 列出可用串行端口
curl http://localhost:3000/api/terminal/ports
# 打开终端端口
curl -X POST http://localhost:3000/api/terminal/open \
-H "Content-Type: application/json" \
-d '{"device":"/dev/ttyUSB1","config":{"baudRate":9600}}'
```
### WebSocket 事件
Web 界面使用 Socket.IO 进行实时更新。
#### 驱动器事件
```javascript
// 服务器 → 客户端
'status' - 驱动器和串行状态更新
// 客户端 → 服务器
'request-status' - 请求立即状态更新
```
#### 终端事件
```javascript
// 服务器 → 客户端
'terminal:data' - 设备来的原始串行数据
'terminal:status' - 终端连接状态
'terminal:error' - 终端错误消息
// 客户端 → 服务器
'terminal:write' - 发送数据到串行设备
'terminal:control' - 控制信号(DTR,RTS)
```
---
## AI 助手集成(MCP)
服务器配备了一个 [模型上下文协议](https://modelcontextprotocol.io/) 服务器,该服务器向 MCP 兼容的助手(Claude Desktop、Claude Code 等)公开了 **38 个工具**。支持两种传输方式,两种方式均需选择加入:
| 传输方式 | 何时使用 | 认证 | 启用方式 |
|---|---|---|---|
| **stdio** | 助手在运行 `fdcsds` 的同一台机器上运行 | 无(父进程信任) | `fdcsds --mcp`(阻塞;不是 Web 服务器) |
| **HTTP(Streamable)** | 助手在 LAN 上其他地方运行,通过网络连接 | Bearer token(必需 - 拒绝启用没有 API 密钥) | `--mcp-http`,配置文件中的 `enableMcpHttp: true` 或配置页面的切换 |
### 工具表面(38种工具,按类别划分)
| 类别 | 功能(示例) |
|---|---|
| **状态** | `获取状态`, `获取驱动器状态` |
| **驱动器** | `列出驱动器`, `挂载磁盘`, `卸载磁盘`, `设置驱动器只读` |
| **磁盘镜像** | `列出磁盘镜像`, `创建磁盘镜像`, `克隆磁盘镜像`, `删除磁盘镜像`, `上传磁盘镜像` |
| **CP/M文件系统** | `列出CP/M文件`, `读取CP/M文件`, `写入CP/M文件`, `从上传写入CP/M文件`, `列出上传`, `删除CP/M文件`, `格式化CP/M磁盘` |
| **终端串行** | `列出终端端口`, `打开终端`, `关闭终端`, `发送到终端`, `读取终端输出`, `运行终端命令`, `清除终端缓冲区` |
| **重放/传输** | `开始重放`, `取消重放`, `列出脚本` |
| **磁带** | `列出磁带`, `播放磁带`, `停止磁带` |
| **配置** | `配置串行`, `启用磁盘服务`, `禁用磁盘服务` |
准确的当前列表以 [`src/mcp-server.ts`](src/mcp-server.ts) 为准。
### 在CP/M磁盘上写入大文件(避免内联base64)
`write_cpm_file` 在其 `data` 参数中将文件字节作为base64字符串。这有效,但 **助手必须逐个生成整个base64 blob令牌** - 一个16 KB文件扩展到约22,000个base64字符(约8,700个输出令牌),因此大写入操作感觉像卡住了一样,即使服务器端的写入是毫秒级的。为小型、模型编写的内容保留 `write_cpm_file` (短的BASIC程序、配置文件)。
对于较大的文件,请将字节从模型中移除:
1. **在服务器的上传目录中暂存文件** (`<data-dir>/uploads`) — 直接复制到那里或通过REST上传端点发布。
2. **使用 `list_uploads` 确认名称**。
3. **使用 `write_cpm_file_from_upload` 写入**,传递 `uploadFilename` (来自步骤2)、目标 `diskFilename` 和要创建的 `cpmFilename`。服务器自己读取暂存文件,因此成本和速度与文件大小无关。目标磁盘不得安装在驱动器上。
示例提示到Claude Code客户端: *"列出上传内容,然后将 `MBASIC.COM` 从上传内容写入 `cpm22.dsk` 作为 `0:MBASIC.COM`。"*
### 安全姿势
- **默认姿势:** 在两个传输方式上都**禁用**。常规的 `fdcsds` 二进制文件不启动MCP;您需要选择每个会话(stdio)或每个配置(HTTP)。
- **stdio:** 没有身份验证。传输方式只能由启动它的进程访问,因此父进程信任是边界。
- **HTTP:** 在共享的 `apiKey` 上进行Bearer身份验证。守护进程拒绝在没有设置API密钥的情况下提供 `/mcp` 服务,并且干净地丢弃任何活动会话。直接在HTTP上运行 - **仅适用于受信任的LAN使用**。如果您将其暴露在LAN之外,请在其前面放置一个带有TLS的反向代理。
### 启用stdio(Claude Desktop / Claude Code在同一台机器上)
```json
{
"mcpServers": {
"fdcplus": {
"command": "fdcsds",
"args": ["--mcp", "--data-dir", "/path/to/your/data"]
}
}
}
```
### 启用HTTP(Claude Code来自LAN上的远程机器)
1. **在Web UI中设置API密钥**,位于 *Configuration → Web & API*,或在配置文件中设置 `apiKey`。守护进程如果没有设置API密钥,则拒绝通过HTTP启用MCP。
2. **在 *Configuration → MCP server (HTTP)* 中启用MCP over HTTP** - 切换按钮立即生效,无需重启。或者,在配置文件中设置 `enableMcpHttp: true`,或在启动时传递 `--mcp-http`。
3. **在运行Claude Code的机器上注册端点**:
```bash
claude mcp add --transport http fdcplus \
http://<pi-hostname>:3000/mcp \
--header "Authorization: Bearer <api-key>"
```
配置页面显示了一个预填充的复制粘贴命令,其中包含当前主机名和端口。
Web界面中的聊天面板(顶部栏 `forum` 图标)同时显示两个配置块,带有复制按钮。
---
## VT102终端模拟器
### 概述
Web界面包括一个由xterm.js支持的完全功能的VT102终端模拟器。这允许您连接到第二个串行端口(与FDC+端口分开)进行交互式终端会话。
### 用例
- **CP/M控制台**:连接到CP/M系统控制台
- **调试**:监视系统输出
- **交互式会话**:交互式运行程序
- **系统管理**:通过串行控制台配置设备
### 配置
终端支持标准的串行端口配置:
| 设置 | 选项 | 默认值 |
|---------|---------|---------|
| 波特率 | 9600, 19200, 38400, 57600, 115200 | 9600 |
| 数据位 | 5, 6, 7, 8 | 8 |
| 停止位 | 1, 2 | 1 |
| 奇偶校验 | 无, 偶数, 奇数, 标记, 空间 | 无 |
| 流控 | 无, 硬件(RTS/CTS), 软件(XON/XOFF) | 无 |
### 终端功能
- **VT102兼容性**:完整的转义序列支持
- **颜色支持**:ANSI颜色代码
- **光标控制**:闪烁的光标和位置跟踪
- **可调整大小**:响应式终端,适应浏览器窗口
- **复制/粘贴**:标准的复制粘贴支持
- **滚动缓冲区**:终端历史缓冲区
- **键盘**:完整的键盘支持,包括功能键
### 硬件设置
连接两个串行端口:
1. **主端口** (`-p /dev/ttyUSB0`):FDC+磁盘控制器
2. **终端端口** (`--terminal-port /dev/ttyUSB1`):系统控制台
示例连接图:
```
计算机 (fdcsds) Altair 8800
----------------- -----------
/dev/ttyUSB0 ←─────────→ FDC+ 端口(磁盘I/O)
/dev/ttyUSB1 ←─────────→ 控制台端口(终端)
```
### 使用示例
```bash
# 启动服务器并启用两个端口
fdcsds -p /dev/ttyUSB0 \
-0 disks/cpm22.dsk \
--terminal-port /dev/ttyUSB1 \
--terminal-baud 9600 \
--terminal-autoconnect \
-w
# 访问Web界面
open http://localhost:3000
```
然后在浏览器中:
1. 导航到终端部分
2. 从下拉菜单中选择串行端口
3. 配置波特率和其他设置
4. 点击“连接”
5. 开始输入以与串行设备交互
---
## GPIO LED状态指示器
### 概述
服务器支持可选的GPIO LED状态指示器,用于树莓派和兼容的Linux系统。这提供了驱动器和终端活动的实时视觉反馈,无需访问Web界面。
### 功能
- **15个LED**:
- 12个驱动器LED(3个/驱动器 × 4个驱动器):启用、加载头部、只读
- 3个终端LED:RX、TX、已连接
- **可配置的引脚映射**,通过配置文件
- **在非树莓派平台上温和失败**
- **无需特殊布线** - 标准LED + 电阻
### 快速入门
```bash
# 使用默认引脚映射启用GPIO LED
fdcsds -p /dev/ttyUSB0 -0 disks/cpm22.dsk --gpio-leds
```
或添加到您的配置文件:
```json
{
"port": "/dev/ttyUSB0",
"gpioLeds": {
"enabled": true
}
}
```
### 默认引脚映射(BCM模式)
| LED功能 | BCM引脚 | 物理引脚 |
|--------------|---------|--------------|
| 驱动器0启用 | GPIO17 | 引脚11 |
| 驱动器0头部加载 | GPIO27 | 引脚13 |
| 驱动器0只读 | GPIO22 | 引脚15 |
| ... | ... | ... |
| 终端RX | GPIO16 | 引脚36 |
| 终端TX | GPIO20 | 引脚38 |
| 终端已连接 | GPIO21 | 引脚40 |
**参见 [docs/GPIO-LEDS.md](docs/GPIO-LEDS.md) 以获取:**
- 完整的引脚映射表
- 布线图和硬件要求
- 配置示例
- 故障排除指南
- 平台支持详情
### 硬件要求
- 树莓派(任何型号,带有GPIO)
- 15个LED(建议:绿色,黄色,红色,蓝色)
- 15 × 220Ω电阻
- 面包板和跳线
### LED行为
- **启用LED**:当磁盘安装时亮起
- **头部加载LED**:当驱动器读取/写入时亮起
- **只读LED**:当写保护时亮起
- **RX/TX LED**:在数据传输时闪烁(100ms)
- **已连接LED**:当终端端口打开时亮起
### 平台支持
- ✅ 树莓派(所有型号)
- ✅ 带有GPIO支持的Linux
- ❌ macOS(温和禁用)
- ❌ Windows(温和禁用)
**注意**:在不支持的平台上,GPIO功能会自动禁用而不会产生错误。
---
## FDC+协议
服务器实现了FDC+串行磁盘协议:
### 命令
1. **STAT** - 状态命令
- 返回已安装驱动器的位掩码
- 更新驱动器头部加载和轨迹位置
2. **READ** - 读取轨迹命令
- 从磁盘镜像中读取137字节的扇区
- 支持每轨最多32个扇区
3. **WRIT** - 写入轨迹命令
- 将扇区写入磁盘镜像
- 尊重只读保护
- 两阶段协议(确认 + 数据 + 状态)
### 命令/响应块
```typescript
struct CommandResponseBlock {
cmd: string; // 4字节ASCII命令
param1: uint16; // 参数1(小端)
param2: uint16; // 参数2(小端)
}
```
### 数据完整性
- 所有数据传输都包含16位校验和
- 校验和 = 所有字节的总和(uint16)
- 格式:LSB,MSB(小端)
---
## 磁盘镜像
### 支持的格式
- **8英寸磁盘**:330KB
- **迷你磁盘**:75KB
- **扩展**:高达8MB
### 格式详情
- **每轨扇区**:32
- **每扇区字节**:137
- **总轨数**:77(最大)
- **轨大小**:4,384字节(137 × 32)
### 创建磁盘镜像
磁盘镜像是原始二进制文件:
```bash
# 创建空白330K 8英寸磁盘
dd if=/dev/zero of=blank.dsk bs=4384 count=77
# 创建空白75K迷你磁盘
dd if=/dev/zero of=minidisk.dsk bs=4384 count=17
```
---
## 开发
### 项目结构
```
fdcplus-web/
├── src/ # 后端TypeScript源代码
├── frontend/ # Svelte 5 前端源代码
├── dist/ # 编译后的后端JavaScript(生成)
├── frontend/dist/ # 编译后的前端资产(生成)
├── disks/ # 磁盘镜像存储
├── test/ # 单元测试(Jest)
├── package.json # 后端依赖和脚本
├── tsconfig.json # TypeScript配置
└── README.md # 此文件
```
### NPM脚本
```bash
npm run build # 编译TypeScript + 生成OpenAPI文档
npm run start # 运行编译后的代码
npm run dev # 运行ts-node(开发)
npm run clean # 删除dist/
npm test # 运行Jest测试
# 前端(从frontend/目录)
cd frontend
npm run dev # Vite开发服务器与HMR
npm run build # 生产构建到frontend/dist/
```
### 类型检查
```bash
npx tsc --noEmit
### 依赖
**后端运行时:**
- `serialport` ^12.0.0 - 串行端口I/O
- `commander` ^11.0.0 - CLI解析
- `express` ^4.18.0 - Web服务器
- `socket.io` ^4.6.0 - WebSocket通信
- `better-sqlite3` - SQLite数据库,WAL模式
- `cors` ^2.8.5 - CORS支持
- `helmet` - 安全头
- `pino` - 结构化日志
- `swagger-jsdoc` / `swagger-ui-express` - OpenAPI文档
- `@anthropic-ai/sdk` - MCP服务器SDK
**可选(仅限树莓派):
- `onoff` ^6.0.3 - GPIO控制LED指示器
**前端(Svelte SPA):**
- `svelte` ^5.0.0 - UI框架
- `vite` ^6.0.0 - 构建工具
- `tailwindcss` ^4.0.0 - 实用优先的CSS
- `@xterm/xterm` ^6.0.0 - 终端模拟器(捆绑)
- `socket.io-client` ^4.6.0 - 实时更新
- Material Symbols Rounded - 字体(从Google Fonts加载 via `app.css`)
**开发工具:
- `typescript` ^5.3.0
- `jest` ^29.0.0
- `ts-node` ^10.9.0
---
## 串行端口权限
### Linux
将您的用户添加到 `dialout` 组:
```bash
sudo usermod -a -G dialout $USER
# 注销并重新登录
```
或使用 `udev` 规则:
```bash
sudo nano /etc/udev/rules.d/50-serial.rules
```
添加:
```
KERNEL=="ttyUSB[0-9]*", MODE="0666"
KERNEL=="ttyACM[0-9]*", MODE="0666"
```
重新加载规则:
```bash
sudo udevadm control --reload-rules`
```
### macOS
串行端口通常无需特殊权限即可工作。端口显示为:
- `/dev/cu.usbserial-*`
- `/dev/tty.usbserial-*`
使用 `cu.*` 设备以获得最佳效果。
---
## 持久串行端口路径(Linux)
### 问题
Linux上的USB串行适配器会根据USB枚举顺序分配易变的设备名称,如 `/dev/ttyUSB0`、`/dev/ttyUSB1`。重启或设备插拔/重新插拔后,这些名称可能会更改,从而破坏您的配置。
**示例:**
- 重启前:FDC+ 在 `/dev/ttyUSB0`,终端在 `/dev/ttyUSB1`
- 重启后:终端在 `/dev/ttyUSB0`,FDC+ 在 `/dev/ttyUSB1` ❌
### 解决方案:持久路径
Linux 提供稳定的设备路径,这些路径在重启和硬件更改后仍然保持不变:
```
/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_ABC123-if00-port0
/dev/serial/by-path/pci-0000:00:14.0-usb-0:1:1.0-port0
```
这些路径:
- ✅ 在重启后保持不变
- ✅ 基于设备序列号或USB端口位置
- ✅ 防止多个适配器导致的配置混淆
- ✅ 在所有Linux发行版上自动工作
### 查找持久路径
#### 方法 1:使用内置助手
```bash
# 显示已配置端口的持久路径
fdcsds --show-persistent-paths
# 示例输出:
# 主串行端口:
# 当前:/dev/ttyUSB0
# 解析:/dev/ttyUSB0
# 存在:是
# 制造商:FTDI
# 持久(按ID):/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_ABC123-if00-port0
# ↑ 推荐在配置文件中使用
```
#### 方法 2:手动发现
```bash
# 列出所有持久路径
ls -l /dev/serial/by-id/
# 示例输出:
# usb-FTDI_FT232R_USB_UART_ABC123-if00-port0 -> ../../ttyUSB0
# usb-Prolific_USB-Serial_Controller_XYZ789-if00-port0 -> ../../ttyUSB1
```
#### 方法 3:使用 udevadm
```bash
# 获取设备信息
udevadm info --name=/dev/ttyUSB0 | grep ID_SERIAL
```
### 迁移指南
#### 步骤 1:查找当前端口
```bash
# 如果您已经运行 fdcsds
fdcsds --show-persistent-paths -c your-config.json
# 或手动列出所有端口
ls -l /dev/serial/by-id/
```
#### 步骤 2:更新您的配置
**之前:**
```json
{
"port": "/dev/ttyUSB0",
"terminalPort": "/dev/ttyUSB1"
}
```
**之后:**
```json
{
"port": "/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_ABC123-if00-port0",
"terminalPort": "/dev/serial/by-id/usb-Prolific_USB-Serial_Controller_XYZ789-if00-port0"
}
```
#### 步骤 3:测试
```bash
# 使用新配置测试
fdcsds -c your-config.json
# 重启并验证它仍然有效
sudo reboot
```
### 在线界面支持
在线界面会自动显示持久路径:
1. 打开Web UI(默认:http://localhost:3000)
2. 进入串行配置
3. 点击“刷新端口”
4. 端口被标记为:
- `(持久 - 推荐)` - 使用这些!
- `(易变)` - 可能会在重启后更改
Web界面将自动使用可用的持久路径。
### 兼容性
**Linux:** 持久路径的完全支持
**macOS/Windows:** 路径正常工作(不需要持久路径)
---
## 实现说明
该服务器在Node.js上运行,并使用与FDC+控制器硬件相同的FDC+线协议:
- 相同的FDC+线协议(STAT / READ / WRIT,137字节扇区,16位校验和)
- 相同的磁盘镜像格式(原始二进制,4,384字节每轨)
- 相同的命令和响应块结构
实现选择:
- 所有串行/文件I/O都是异步的(Node `serialport` + `fs/promises`)
- 基于Promise的超时处理,而不是POSIX `select()`
- 严格模式下的TypeScript + `noUnusedLocals` / `noImplicitReturns`等
- 清晰的模块分离:`routes/ → services/ → 低级别(drive.ts, serial.ts, cpm-filesystem.ts)`
- 参见 `_bmad-output/project-context.md`(gitignored)以获取完整的约定和陷阱集
---
## 故障排除
### 串行端口未找到
```bash
# 使用内置端口发现
fdcsds --show-persistent-paths
# 或手动列出可用串行端口
ls /dev/tty* /dev/cu*
# 检查权限
ls -l /dev/ttyUSB0
```
**如果端口在重启后更改(Linux):**
- 使用持久路径而不是易变的 `/dev/ttyUSB*` 名称
- 参见上面的 [持久串行端口路径](#持久串行端口路径-linux) 部分
- 运行 `fdcsds --show-persistent-paths` 以查找稳定的路径
### 构建错误
#### 一般构建错误
```bash
# 清洁并重建
npm run clean
rm -rf node_modules package-lock.json
npm install
npm run build
```
#### npm install 在macOS/Windows上失败(Python distutils错误)
`onoff` 包是树莓派GPIO LED支持的可选依赖项。它需要本机编译,可能会由于Python兼容性问题在macOS/Windows上失败。
**解决方案:** 在开发中安装时忽略可选依赖:
```bash
npm install --no-optional
npm run build
```
应用程序在没有GPIO支持的情况下完美运行。在树莓派/Linux上,`npm install` 将自动编译并包含GPIO支持。
### 运行时错误
启用调试模式:
```bash
fdcsds -p /dev/ttyUSB0 -0 disk.dsk -v -d
```
---
```
## 未来增强功能
- [x] 使用 Jest 的单元测试
- [x] 基于 Web 的用户界面选项(Svelte 5 单页应用)
- [x] 用于远程管理的 REST API
- [x] VT102 终端模拟器
- [x] GPIO LED 状态指示器
- [x] 通过 Web 界面上传磁盘镜像
- [x] 用于 AI 助手集成的 MCP 服务器
- [x] OpenAPI/Swagger 文档
- [x] 模块化后端架构
- [ ] 集成测试(模拟串行端口)
- [ ] 支持更多类型的驱动器
- [ ] 磁盘镜像转换工具
- [ ] 性能基准测试
- [ ] 终端录制/回放
- [ ] LED 的 PWM 亮度控制
- [ ] 用于物理控制的 GPIO 按钮输入
---
## 贡献
欢迎贡献。完整的指南 — 分支模型、提交风格、PR 前的 `pnpm check` 门控、OpenAPI 再生规则、硬件相关 PR 的注意事项 — 参见 [**CONTRIBUTING.md**](CONTRIBUTING.md)。
快速摘要:
1. 从 `main` 分支 fork 并创建新分支。
2. 进行更改。如果可以,请添加测试。
3. 运行 `pnpm check` — 必须通过。
4. 如果您修改了 `@openapi` JSDoc,请运行 `pnpm docs` 并在同一 PR 中提交重新生成的 `openapi.json`。
5. 打开 PR。CI 运行相同的检查;维护者进行审查。
---
## 许可证
GPL-3.0。
---
## 致谢
- **TypeScript 实现、Web 界面、MCP 服务器、GPIO LED**:Joe Toppe(2024 年至今)。
- **FDC+ 增强型软盘控制器硬件**:Mike Douglas / [deramp.com](http://www.deramp.com)。该软件通过串行端口与该硬件通信;硬件本身是一个独立的产品。
参见 [AUTHORS](AUTHORS) 获取贡献者列表。
---
## 参考资料
- [FDC+ 硬件文档](http://www.deramp.com)
- [Altair 8800 信息](https://en.wikipedia.org/wiki/Altair_8800)
- [Node.js 串行端口](https://serialport.io/)
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器