# Fresenius 4008S 透析机串口联机文档 ## 1. 快速开始 ### 1.1 系统要求 - **Node.js**: v12.0 或更高版本 - **操作系统**: Windows/Linux/macOS - **依赖包**: `serialport` - **硬件**: 支持 RS-232 或 USB-to-Serial 适配器的计算机 ### 1.2 安装依赖 ```bash npm install serialport ``` ### 1.3 配置串口参数 编辑 `app.js` 中的配置区: ```javascript const PORT_NAME = 'COM5'; // 修改为实际的串口号 const BAUD_RATE = 2400; // Fresenius 4008S 标准波特率 const GET_DATA_INTERVAL = 30000; // 数据请求间隔(毫秒) const ACTIVATE_INTERVAL = 30000; // 激活保活间隔(毫秒) ``` ### 1.4 运行程序 ```bash node app.js ``` --- ## 2. 硬件连接 ### 2.1 串口连接方式 #### 方案 A:USB-to-Serial 适配器(推荐) ``` Fresenius 4008S ↓ RS-232 9针连接器 ↓ USB-to-Serial 转换器 (CH340/PL2303等) ↓ 计算机 USB 端口 ``` #### 方案 B:直接 RS-232 连接 ``` Fresenius 4008S (DB-9) ↓ 串口线 (RS-232) ↓ 计算机 COM 端口 (DB-9) ``` ### 2.2 RS-232 引脚定义 | 引脚 | 名称 | 功能 | |------|------|------| | 1 | DCD | 数据载体检测 | | 2 | RXD | 接收数据 | | 3 | TXD | 发送数据 | | 4 | DTR | 数据终端就绪 | | 5 | GND | 地线 | | 6 | DSR | 数据集就绪 | | 7 | RTS | 请求发送 | | 8 | CTS | 允许发送 | | 9 | RI | 振铃指示 | **最小连接** (3 线制): - 引脚 2 (RXD) ← 设备 TXD - 引脚 3 (TXD) → 设备 RXD - 引脚 5 (GND) ↔ 设备 GND ### 2.3 串口号查看 #### Windows 1. 按 `Win + R` 打开运行 2. 输入 `devmgmt.msc` 打开设备管理器 3. 展开"端口 (COM 和 LPT)" 4. 查看 COM 号(如 COM5) #### Linux ```bash ls /dev/ttyUSB* # 或 ls /dev/ttyACM* ``` #### macOS ```bash ls /dev/tty.usb* # 或 ls /dev/cu.usb* ``` --- ## 3. 通讯协议 ### 3.1 帧结构 ``` ┌─────────┬──────┬────────────┬─────────┬──────────────────────────┬─────────┐ │ 帧头 │ 帧号 │ 模式标识 │ 分隔符 │ 数据区 (临床参数) │ 帧尾 │ ├─────────┼──────┼────────────┼─────────┼──────────────────────────┼─────────┤ │ 0x16 │ 0x31 │ 0x5F 0x43 │ 0x02 │ [A-Z][±数值]{3,6}... │ 0x06 │ │ `.` │ `1` │ `_C` │ `.` │ 字段1 字段2 ... │ 0x03 │ │ (1 byte)│ │ (3 bytes) │ (1 byte)│ (多字节) │ (2 byte)│ └─────────┴──────┴────────────┴─────────┴──────────────────────────┴─────────┘ ``` ### 3.2 参数字段格式 #### 基本格式 ``` [字段标记][符号可选][数值] 示例: A006 → 动脉压 = 6 mmHg T0325 → 温度 = 32.5°C (需 ÷10) U+087 → TMP = 87 mmHg N-140 → 负值参数 = -140 ``` #### 字段标记表 | 标记 | 名称 | 单位 | 范围 | 备注 | |------|------|------|------|------| | **A** | 动脉压 | mmHg | 0-300 | 血流入压力 | | **V** | 静脉压 | mmHg | 0-300 | 血流出压力 | | **U** | 跨膜压(TMP) | mmHg | 0-200 | 超滤驱动力 | | **T** | 温度 | 0.1°C | 0-500 | 需 ÷10 | | **C** | 电导率 | 0.1 mS/cm | 0-2000 | 需 ÷10 | | **I** | 透析液流量 | mL/min | 0-1000 | 首个I为流量 | | **Q** | 有效血流量 | mL/min | 0-500 | 实际通过的血流 | | **R** | 超滤速率 | mL/h | 0-5000 | 当前超滤速度 | | **P** | 已超滤量 | mL | 0-10000 | 累计超滤量 | | **G** | 超滤目标量 | mL | 0-10000 | 计划超滤量 | | **H** | 剩余治疗时间 | 分钟 | 0-300 | 分钟制 | | **N** | 钠浓度 | 0.1 mmol/L | 0-2000 | 需 ÷10 | | **B** | 血泵设定值 | mL/min | 0-500 | 可多次出现 | | **L** | 肝素累计 | 单位 | 0-10000 | 用药记录 | | **X** | 静脉壶状态 | 状态码 | 0-10 | 传感器状态 | | **M** | 总治疗时间 | 分钟 | 0-600 | 计划时长 | | **Z** | TMP补偿 | mmHg | -100-100 | 自动调节值 | | **S** | 系统状态码 | 十进制 | 0-999 | 位字段状态 | | **Y** | 累计血容量 | mL/L | 0-99999 | 可需 ÷1000 | ### 3.3 通讯命令 #### 命令 1:激活/保活指令 ``` HEX: 16 2f 40 31 04 03 说明: 保持设备在线,防止超时断开 间隔: 每 30 秒 ``` #### 命令 2:获取治疗数据 ``` HEX: 16 31 3f 43 44 04 03 说明: 请求设备报告当前临床参数 间隔: 每 30 秒 ``` ### 3.4 通讯流程 ``` ┌─────────────────────────────────────────────────────┐ │ 程序启动 │ └────────────────────┬────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────┐ │ 打开串口 (COM5 @ 2400 bps) │ └────────────────────┬────────────────────────────────┘ ↓ ┌────────────────────────┐ │ 发送激活指令 (立即) │ └────────────────┬───────┘ ↓ ┌────────────────────────────────────────┐ │ 启动两个定时器: │ │ • 激活保活: 每 30 秒 │ │ • 数据请求: 每 30 秒 │ └────────────────┬───────────────────────┘ ↓ ┌────────────────────────────────────────┐ │ 等待接收数据 │ │ (持续监听串口) │ └────────┬─────────────────────┬─────────┘ ↓ ↓ ┌─────────────────┐ ┌──────────────────┐ │ 收到完整帧 │ │ 定时器触发 │ │ (0x06 0x03) │ │ 发送命令 │ └────────┬────────┘ └──────┬───────────┘ ↓ ↓ ┌──────────────────────────────────────┐ │ 解析数据: │ │ 1. 提取序列号 │ │ 2. 提取临床参数 │ │ 3. 格式化显示 │ └──────────────────────────────────────┘ ``` --- ## 4. 代码详解 ### 4.1 配置参数 ```javascript const PORT_NAME = 'COM5'; // 串口号(必须配置) const BAUD_RATE = 2400; // 波特率 (Fresenius 标准) const GET_DATA_INTERVAL = 30000; // 30秒获取一次数据 const ACTIVATE_INTERVAL = 30000; // 30秒发送一次保活信号 ``` **参数说明**: - `PORT_NAME`: Windows 下通常为 COM1-COM9;Linux/Mac 为 /dev/ttyUSB0 等 - `BAUD_RATE`: 不要修改,透析机固定 2400 bps - 时间间隔: 毫秒制,30000 = 30 秒 ### 4.2 核心函数 #### hexToBuffer(hexStr) ```javascript 功能: 将 HEX 字符串转换为 Buffer 输入: "16 2f 40 31 04 03" 输出: ``` #### sendCommand(cmdHex, label) ```javascript 功能: 向设备发送命令 参数: - cmdHex: HEX 格式命令字符串 - label: 日志标签 示例: sendCommand('16 2f 40 31 04 03', '激活指令'); ``` #### extractSerialNumber(frame) ```javascript 功能: 从数据帧提取设备序列号 规则: 第二个 "I" 后跟的 8 位大小写字母/数字 输入: Buffer (完整数据帧) 输出: "7VCA0Y82" 或 null ``` #### extractAllFields(frame) ```javascript 功能: 从数据帧提取所有临床参数 正则: /([A-Z])([+-]?\d{3,6})/g 输出: {A: 6, V: 0, U: 87, T: 325, C: 105, ...} ``` #### printClinicalParams(params) ```javascript 功能: 格式化显示临床参数 处理: 1. 已知参数的单位转换和显示 2. 未知参数的列出 3. 异常情况提醒 ``` #### handleFrame(frame) ```javascript 功能: 处理接收到的完整数据帧 流程: 1. 显示 HEX 和 ASCII 2. 过滤短帧 (<15 bytes) 3. 提取序列号 4. 提取并显示临床参数 ``` ### 4.3 事件处理 #### 数据接收事件 ```javascript port.on('data', (chunk) => { receiveBuffer = Buffer.concat([receiveBuffer, chunk]); // 查找帧尾 (0x06 0x03) let endIndex; while ((endIndex = receiveBuffer.indexOf(FRAME_END)) !== -1) { const frame = receiveBuffer.subarray(0, endIndex + FRAME_END.length); handleFrame(frame); receiveBuffer = receiveBuffer.subarray(endIndex + FRAME_END.length); } // 缓冲区过大时清空(防止内存溢出) if (receiveBuffer.length > 2048) { console.warn('⚠️ 缓冲区异常增长,已清空'); receiveBuffer = Buffer.alloc(0); } }); ``` #### 进程终止信号 ```javascript process.on('SIGINT', shutdown); // Ctrl+C process.on('SIGTERM', shutdown); // 系统关闭信号 function shutdown() { if (port && isPortOpen) { port.close(() => { console.log('串口已关闭'); process.exit(0); }); } } ``` --- ## 5. 使用指南 ### 5.1 初次连接步骤 1. **确认硬件连接** ``` ✓ 串口线已连接 ✓ 设备已通电 ✓ 驱动程序已安装 ``` 2. **查找串口号** - Windows: 设备管理器 → 端口 - Linux: `ls /dev/ttyUSB*` 3. **编辑配置** ```javascript const PORT_NAME = 'COM5'; // 改为实际串口号 ``` 4. **运行程序** ```bash node app.js ``` 5. **观察输出** ``` ✅ 串口 COM5 已打开,波特率 2400 📤 [时间] 发送 激活指令(初始) 📥 [时间] 接收到完整数据帧 🏷️ 设备序列号: 7VCA0Y82 🩺 临床参数解析 ``` ### 5.2 日志说明 | 符号 | 含义 | 说明 | |------|------|------| | 📤 | 发送 | 向设备发送命令 | | 📥 | 接收 | 从设备接收数据 | | 🏷️ | 标签 | 设备标识符 | | 🩺 | 医疗 | 临床参数显示 | | ✅ | 成功 | 操作成功 | | ⚠️ | 警告 | 需要注意的事项 | | ❌ | 错误 | 出现故障 | ### 5.3 常见问题 #### Q: 无法打开串口 **原因**: - 串口号不正确 - 驱动程序未安装 - 串口被其他程序占用 **解决**: 1. 检查设备管理器中的实际串口号 2. 使用 USB 适配器时安装驱动 (CH340/PL2303) 3. 关闭其他串口监听程序 #### Q: 连接后没有接收数据 **原因**: - 设备未通电或离线 - 硬件连接不良 - 波特率设置错误 **解决**: 1. 检查设备屏幕是否正常显示 2. 检查串口线两端是否牢固 3. 确认波特率为 2400 bps #### Q: 出现乱码 **原因**: - 波特率设置不匹配 - 数据传输受干扰 **解决**: 1. 确认波特率为 2400 2. 使用屏蔽串口线 3. 重启设备和程序 #### Q: 序列号无法识别 **原因**: - 数据帧格式异常 - 设备返回不同格式的数据 **解决**: 1. 检查 ASCII 输出中是否有 "I...S" 的模式 2. 查看数据帧完整性 3. 修改正则表达式规则 ### 5.4 性能优化 #### 增加接收缓冲区 ```javascript const port = new SerialPort({ path: PORT_NAME, baudRate: BAUD_RATE, autoOpen: false, highWaterMark: 256 // 增加缓冲区大小 }); ``` #### 调整请求间隔 ```javascript // 如果数据太密集,增加间隔 const GET_DATA_INTERVAL = 60000; // 改为 60 秒 const ACTIVATE_INTERVAL = 120000; // 改为 120 秒 ``` #### 降低日志输出 ```javascript // 注释掉冗长的日志 // console.log(` └─ 尝试提取序列号,完整 ASCII: "${ascii}"`); ``` --- ## 6. 参数解析示例 ### 示例 1:基础参数 ``` 接收数据: A006V000U087T0325C0105 解析结果: A = 006 → 动脉压 = 6 mmHg V = 000 → 静脉压 = 0 mmHg U = 087 → TMP = 87 mmHg T = 0325 → 温度 = 32.5°C (÷10) C = 0105 → 电导率 = 10.5 mS/cm (÷10) ``` ### 示例 2:流量和超滤 ``` 接收数据: Q200I500R100P2000G5000 解析结果: Q = 200 → 血流量 = 200 mL/min I = 500 → 液流量 = 500 mL/min R = 100 → 超滤速率 = 100 mL/h P = 2000 → 已超滤 = 2000 mL G = 5000 → 目标 = 5000 mL ``` ### 示例 3:时间和药物 ``` 接收数据: H180M240L4953 解析结果: H = 180 → 剩余时间 = 180 分钟 (3 小时) M = 240 → 总时间 = 240 分钟 (4 小时) L = 4953 → 肝素 = 4953 单位 ``` ### 示例 4:有符号数 ``` 接收数据: A+150V-050U+087 解析结果: A = +150 → 动脉压 = 150 mmHg V = -050 → 静脉压 = -50 mmHg (异常) U = +087 → TMP = 87 mmHg ``` --- ## 7. 远程监控集成 ### 7.1 数据上传模式 ```javascript // 添加云平台上报 function reportToCloud(params, serial) { const payload = { device_id: serial, timestamp: new Date().toISOString(), blood_pressure_a: params.A, blood_pressure_v: params.V, tmp: params.U, temperature: params.T / 10, conductivity: params.C / 10, blood_flow: params.Q, ultrafiltration_rate: params.R, treatment_time_remaining: params.H }; // POST 到服务器 // fetch('https://api.example.com/dialysis', { // method: 'POST', // body: JSON.stringify(payload) // }); } ``` ### 7.2 告警规则 ```javascript function checkAlarms(params) { const alarms = []; // 血压异常 if (params.A > 200) alarms.push('⚠️ 动脉压过高'); if (params.V > 200) alarms.push('⚠️ 静脉压过高'); // 温度异常 if (params.T < 350 || params.T > 380) alarms.push('⚠️ 温度异常'); // 电导率异常 if (params.C < 100 || params.C > 140) alarms.push('⚠️ 电导率异常'); // 血流中断 if (params.Q === 0 && params.H > 0) alarms.push('🚨 血流中断'); return alarms; } ``` ### 7.3 数据持久化 ```javascript const fs = require('fs'); function logToFile(params, serial) { const log = { timestamp: new Date().toISOString(), device: serial, ...params }; fs.appendFileSync('dialysis_log.jsonl', JSON.stringify(log) + '\n' ); } ``` --- ## 8. 故障排查 ### 8.1 诊断工具 ```javascript // 添加诊断命令 function diagnose() { console.log('=== 系统诊断 ==='); console.log(`✓ 串口状态: ${isPortOpen ? '开启' : '关闭'}`); console.log(`✓ 缓冲区大小: ${receiveBuffer.length} bytes`); console.log(`✓ 设备序列号: ${detectedSerial || '未识别'}`); console.log(`✓ 最后接收时间: ${new Date().toLocaleTimeString()}`); } ``` ### 8.2 数据验证 ```javascript function validateFrame(frame) { const checks = { frameStart: frame[0] === 0x16, frameEnd: frame[frame.length-2] === 0x06 && frame[frame.length-1] === 0x03, minLength: frame.length >= 15, hasData: frame.length > 20 }; return checks; } ``` ### 8.3 连接监控 ```javascript let lastReceiveTime = Date.now(); const TIMEOUT = 60000; // 60秒超时 setInterval(() => { if (Date.now() - lastReceiveTime > TIMEOUT) { console.warn('⚠️ 长时间无数据接收,设备可能离线'); // 可选:自动重新连接 // reconnect(); } }, 10000); ``` --- ## 9. 扩展功能 ### 9.1 多设备支持 ```javascript const devices = [ { name: 'Device1', port: 'COM5', baud: 2400 }, { name: 'Device2', port: 'COM6', baud: 2400 } ]; devices.forEach(dev => { initSerialForDevice(dev); }); ``` ### 9.2 录像/回放 ```javascript const recordings = []; function recordFrame(frame, params) { recordings.push({ time: Date.now(), frame: frame.toString('hex'), params: params }); } function playback(index) { const rec = recordings[index]; console.log(`回放: ${new Date(rec.time).toLocaleTimeString()}`); console.log(rec.params); } ``` ### 9.3 Web 仪表板 ```javascript const http = require('http'); const server = http.createServer((req, res) => { if (req.url === '/api/status') { res.writeHead(200, {'Content-Type': 'application/json'}); res.end(JSON.stringify({ device: detectedSerial, status: 'online' })); } }); server.listen(3000); ``` --- ## 10. 安全注意事项 ⚠️ **重要**: 本工具用于数据采集,不应用于临床决策 ### 10.1 数据完整性 - 验证每个数据帧的完整性 - 检查参数值的合理性 - 对异常数据进行告警 ### 10.2 系统稳定性 - 定期重启连接以清理缓冲区 - 监控内存占用 - 实现断线重连机制 ### 10.3 隐私保护 - 不在日志中保存患者敏感信息 - 加密传输远程数据 - 定期清理本地日志文件 --- ## 11. 附录 ### 11.1 串口速率对照表 | 速率 | 说明 | |------|------| | 300 | 极低速,不适用 | | 1200 | 低速通讯 | | **2400** | **Fresenius 标准** ✓ | | 9600 | 常见速率 | | 19200 | 高速通讯 | | 115200 | 超高速 | ### 11.2 帧尾字节说明 | HEX | ASCII | 含义 | |-----|-------|------| | 0x06 | ACK | 确认字符 | | 0x03 | ETX | 文本结束 | 帧尾 `06 03` 表示数据帧结束和传输完成确认。 ### 11.3 完整 ASCII 字段索引 | 字段 | 类型 | 范围 | 转换 | 临床意义 | |------|------|------|------|---------| | A | 压力 | 0-300 | ×1 | 血液流入压 | | V | 压力 | 0-300 | ×1 | 血液流出压 | | U | 压力 | 0-200 | ×1 | 超滤驱动压 | | T | 温度 | 0-500 | ÷10 | 透析液温度 | | C | 导率 | 0-2000 | ÷10 | 液体电解质 | | I | 流量 | 0-1000 | ×1 | 液体流速 | | Q | 流量 | 0-500 | ×1 | 血液流速 | | R | 速率 | 0-5000 | ×1 | 超滤速度 | | P | 量 | 0-10000 | ×1 | 已去除液体 | | G | 量 | 0-10000 | ×1 | 目标去除量 | | H | 时间 | 0-300 | ×1 | 剩余分钟数 | | N | 浓度 | 0-2000 | ÷10 | 血清钠浓度 | | B | 设定 | 0-500 | ×1 | 血泵速度 | | L | 量 | 0-10000 | ×1 | 肝素用量 | --- ## 12. 技术支持 ### 问题反馈格式 ``` 问题: [描述问题] 时间: [问题发生时间] 日志: [相关日志输出] 配置: [使用的配置参数] 硬件: [硬件连接方式] ``` ### 常用命令集 ```bash # 查看已有 Node 进程 ps aux | grep node # 杀死进程 kill -9 # 实时查看日志 tail -f app.log # 后台运行 nohup node app.js > app.log 2>&1 & # 开启详细日志 NODE_DEBUG=* node app.js ``` --- **文档版本**: 1.0 **更新时间**: 2025-12-11 **适用版本**: app.js v1.0+ **设备型号**: Fresenius 4008S **通讯方式**: RS-232 串口 (2400 bps)