费森4008s 网口通讯 ,原生串口透传网口
编辑 | blame | 历史 | 原始文档

Fresenius 4008S 透析机串口联机文档

1. 快速开始

1.1 系统要求

  • Node.js: v12.0 或更高版本
  • 操作系统: Windows/Linux/macOS
  • 依赖包: serialport
  • 硬件: 支持 RS-232 或 USB-to-Serial 适配器的计算机

1.2 安装依赖

npm install serialport

1.3 配置串口参数

编辑 app.js 中的配置区:

const PORT_NAME = 'COM5';           // 修改为实际的串口号
const BAUD_RATE = 2400;            // Fresenius 4008S 标准波特率
const GET_DATA_INTERVAL = 30000;   // 数据请求间隔(毫秒)
const ACTIVATE_INTERVAL = 30000;   // 激活保活间隔(毫秒)

1.4 运行程序

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

ls /dev/ttyUSB*
# 或
ls /dev/ttyACM*

macOS

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 配置参数

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)

功能: 将 HEX 字符串转换为 Buffer
输入: "16 2f 40 31 04 03"
输出: <Buffer 16 2f 40 31 04 03>

sendCommand(cmdHex, label)

功能: 向设备发送命令
参数:
  - cmdHex: HEX 格式命令字符串
  - label: 日志标签
示例:
  sendCommand('16 2f 40 31 04 03', '激活指令');

extractSerialNumber(frame)

功能: 从数据帧提取设备序列号
规则: 第二个 "I" 后跟的 8 位大小写字母/数字
输入: Buffer (完整数据帧)
输出: "7VCA0Y82" 或 null

extractAllFields(frame)

功能: 从数据帧提取所有临床参数
正则: /([A-Z])([+-]?\d{3,6})/g
输出: {A: 6, V: 0, U: 87, T: 325, C: 105, ...}

printClinicalParams(params)

功能: 格式化显示临床参数
处理:
  1. 已知参数的单位转换和显示
  2. 未知参数的列出
  3. 异常情况提醒

handleFrame(frame)

功能: 处理接收到的完整数据帧
流程:
  1. 显示 HEX 和 ASCII
  2. 过滤短帧 (<15 bytes)
  3. 提取序列号
  4. 提取并显示临床参数

4.3 事件处理

数据接收事件

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);
  }
});

进程终止信号

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*
  1. 编辑配置
    javascript const PORT_NAME = 'COM5'; // 改为实际串口号

  2. 运行程序
    bash node app.js

  3. 观察输出
    ✅ 串口 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 性能优化

增加接收缓冲区

const port = new SerialPort({
  path: PORT_NAME,
  baudRate: BAUD_RATE,
  autoOpen: false,
  highWaterMark: 256  // 增加缓冲区大小
});

调整请求间隔

// 如果数据太密集,增加间隔
const GET_DATA_INTERVAL = 60000;      // 改为 60 秒
const ACTIVATE_INTERVAL = 120000;     // 改为 120 秒

降低日志输出

// 注释掉冗长的日志
// 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 数据上传模式

// 添加云平台上报
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 告警规则

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 数据持久化

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 诊断工具

// 添加诊断命令
function diagnose() {
  console.log('=== 系统诊断 ===');
  console.log(`✓ 串口状态: ${isPortOpen ? '开启' : '关闭'}`);
  console.log(`✓ 缓冲区大小: ${receiveBuffer.length} bytes`);
  console.log(`✓ 设备序列号: ${detectedSerial || '未识别'}`);
  console.log(`✓ 最后接收时间: ${new Date().toLocaleTimeString()}`);
}

8.2 数据验证

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 连接监控

let lastReceiveTime = Date.now();
const TIMEOUT = 60000;  // 60秒超时

setInterval(() => {
  if (Date.now() - lastReceiveTime > TIMEOUT) {
    console.warn('⚠️ 长时间无数据接收,设备可能离线');
    // 可选:自动重新连接
    // reconnect();
  }
}, 10000);

9. 扩展功能

9.1 多设备支持

const devices = [
  { name: 'Device1', port: 'COM5', baud: 2400 },
  { name: 'Device2', port: 'COM6', baud: 2400 }
];

devices.forEach(dev => {
  initSerialForDevice(dev);
});

9.2 录像/回放

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 仪表板

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. 技术支持

问题反馈格式

问题: [描述问题]
时间: [问题发生时间]
日志: [相关日志输出]
配置: [使用的配置参数]
硬件: [硬件连接方式]

常用命令集

# 查看已有 Node 进程
ps aux | grep node

# 杀死进程
kill -9 <PID>

# 实时查看日志
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)