🩸 血透采集 · 对接接口文档 采集盒 → 上报服务器 v1 · 2026-06-23
说明:本文档定义采集盒主动上报数据的标准协议——盒子按本规范发,上游数据需求方 / HIS 按本规范收。 文中示例上报地址 https://eye.example.com/api/hd/ingest占位,正式地址、是否启用 Token、单批上限等另行约定。

🔧 需要机读规范 / 一键导入工具? OpenAPI · Postman / Apifox 集合 · 在线交互文档 →

血透采集 · 上报接口规范 v1

采集盒(collector)把归一后的统一标准记录主动 HTTP POST 到上报服务器。 这是我们自定的标准协议——盒子按本规范发,服务器(上游数据需求方/HIS/任何接收方)按本规范收,不依赖对方先给契约。本文即给接收方的对接文档。

1. 角色与方向

血透机 →串口→ [采集盒 collector: 解析+归一(kb/42/43)] →HTTP POST→ [上报服务器(接收方提供)]
  • 盒子主动推(push),不是服务器拉。盒子在内网/蒲公英组网里,主动出站到可配置 URL。
  • 选 HTTP POST 而非 MQTT:实现简单、盒子已带 libcurl.so.4、接收方一个 HTTP 接口即可,无需架 broker。MQTT 为备选(见 §10)。

2. 传输层

规定
协议 HTTP/1.1,POST
URL 可配置(collector -u);http://https://,可带路径,如 https://eye.example.com/api/hd/ingest
Content-Type application/json; charset=utf-8(盒子发 application/json,体为 UTF-8)
见 §4 批量信封
超时 单次默认 10s(可配)
编码 全程 UTF-8

TLS 注意(重要):盒子是 OpenSSL 1.0.x,对新式证书链/TLS1.3 可能不兼容。优先级:

  1. 内网/蒲公英组网内走 http://(链路已在专网,最省心,推荐 POC 与多数生产);
  2. 必须公网加密 → https://,但接收端证书要兼容老 OpenSSL;自签/内网证书可用 collector -K 跳过校验(仅测试/可信内网)。

3. 鉴权

Header 必填 说明
X-Device-Id 采集盒/设备唯一号(= collector -i)。服务器据此识别来源盒子
X-Token 预共享令牌(collector -a)。接收方下发,盒子按盒配置。无则不带

升级位:将来可换 Authorization: Bearer <jwt> / mTLS。v1 用预共享 token 足够(盒子数量可控、在专网)。

4. 请求体:批量信封

一次 POST 携带 1~N 条标准记录(批量省连接/省 4G 流量):

{
  "ver": "1.0",
  "dev": "BSLRS23220260379",
  "batch": [
    { "ver":"1.0", "type":"treat", "id":"BSLRS23220260379-1781515321-0",
      "dev":"BSLRS23220260379", "brand":"fresenius", "model":"4008A",
      "port":"ttyUSB0", "ts":"2026-06-15T13:21:05+08:00",
      "data": { "qb":250, "vp":120, "ap":-150, "tmp":140, "uf":1200, "ufr":800,
                "cond":14.0, "temp_d":36.5, "mode":"HD" } }
  ]
}
  • batch[] 的每个元素就是一条 kb/42 标准记录(treat/bp/alarm/status/setting 任意混排)。
  • 每条记录多带一个 id 字段(幂等键,见 §5)——这是上报相比纯 kb/42 记录唯一增加的字段。
  • 单条上报 = batch 里只放 1 条。
  • 外层 dev 与各记录内 dev 一致(外层便于服务器在不拆 batch 时路由)。

5. 幂等键 id 与投递语义(at-least-once

  • 每条记录带全局唯一 id,格式:<dev>-<盒子启动时刻epoch>-<自启动起的序号>,如 BSLRS23220260379-1781515321-42
    • 跨重启不撞(启动时刻变)、单次运行内单调递增(序号)。
  • 投递保证 = at-least-once:网络抖动/服务器抖动时盒子会重发,同一条可能到达多次
  • 服务器必须按 id 去重幂等:已存在的 id 视为成功(返回 2xx),不要重复落库、不要报错。
  • 不保证 exactly-once(盒子侧无法单方面做到);幂等去重是接收方的硬要求

6. 响应契约(ACK 以 HTTP 状态为准

盒子只认 HTTP 状态码判断是否成功,响应体仅供日志/排错:

HTTP 状态 含义 盒子动作
2xx 已持久化(含幂等重复命中) 视为成功,从缓存删除该批
413 体过大 自动减半 batch 重试
其它 4xx 客户端错误(鉴权/格式…) 视为未成功,保留并退避重试(不静默丢,避免误配导致丢数据;靠缓存上限兜底)
5xx / 超时 / 连不上 服务器/网络问题 同上,保留并退避重试

接收方铁律:非「已持久化」绝不返回 2xx。 收到但还没落库(仅入队/仅校验通过)就回 2xx,会让盒子删缓存 → 丢数据。落库成功(或确认是已存在的 id)才回 2xx。

响应体(可选,建议返回,便于盒子日志):

{ "code": 0, "msg": "ok" }

code/msg 仅信息性。v1 盒子不解析体,只看状态码(C 端不引 JSON 解析、最稳)。

7. 重试、退避与本地缓存

  • 缓存:盒子内存 FIFO 队列为运行期权威;可选落 spool 文件(collector -S)以扛断电/重启续传。
  • 退避:失败后 1→2→4→…→60s 指数退避,成功即归零。
  • 缓存上限(collector -z,默认 256KB):超出按 FIFO 丢最旧并打告警日志(防 4G 长断导致内存/flash 撑爆)。丢弃即数据丢失,告警里有累计丢弃数,运维据此评估。
  • 持久化权衡(盒子硬约束):
    • -S 指向 /tmp(内存盘 61MB):快、不磨损 flash,但断电丢未发数据
    • -S 指向 /overlay(持久 ~1.8MB):断电不丢,但每条都写 flash、磨损且空间小;
    • 不给 -S:纯内存,进程退出即丢未发数据(仅扛运行期网络抖动)。
    • 推荐:默认 /tmp 续传 + 适度上限;对“断电也不能丢”的场景再评估少量落 overlay。

8. 排序

  • 盒子尽量按时间顺序发,但不保证(重试会打乱)。
  • 接收方按记录里的 ts 落库/排序,不要依赖到达顺序。ts 一律带时区(见 kb/42 §1)。

9. 接收方最小实现建议(给上游数据需求方)

一个 HTTP 接口即可:

POST /api/hd/ingest
Headers: X-Device-Id, X-Token(可选), Content-Type: application/json
Body:    §4 批量信封

处理:

  1. 校验 X-Token(如启用);不过 → 401(盒子会重试,运维需修配置)。
  2. 遍历 batch[]:按 id 幂等 upsert(已存在则跳过,仍计成功)。
  3. 全部落库成功(含幂等命中)→ HTTP 200 {"code":0};任一未持久化 → 返回非 2xx(盒子整批重试)。
  4. 按记录 ts 入库,不依赖到达顺序。
  5. 不同 type(treat/bp/alarm/status)按 kb/42 各表分流。

10. 备选:MQTT

盒子带 libmosquitto.so.1。若接收方更想要 MQTT:主题 hd/<dev>/<type>,payload = 单条标准记录(含 id),QoS1(at-least-once,与本规范语义一致),broker 侧按 id 去重。本规范字段/语义不变,仅换传输。v1 默认 HTTP。


血透采集 · 统一数据标准 v1(草案)

目的:把多品牌血透机的私有协议,在边缘盒子里归一成这一套标准,上游数据需求方 / HIS / 任何读取方只认这一套格式,无需关心底层是费森还是金宝。

1. 通用约定(防坑硬规则

规定
编码 JSON / UTF-8;一条记录一个 JSON 对象
字段名 snake_case 全小写 ASCII,稳定不变(JSON 大小写敏感,全小写杜绝大小写对不上)
单位 标准里钉死(见各表),解析时统一换算(如某机 Qb 报 L/h → 必须转成 mL/min 再出)。数据需求方永远按标准单位读
数值 JSON number(不是字符串);保留 1 位小数足够时不要塞一堆浮点尾巴
缺失 机器不支持的字段 → 省略该 key;支持但本条无值 → null。数据需求方:缺 key = 该机型没有,null = 暂无
时间 ISO8601 必带时区偏移,如 2026-06-15T13:21:05+08:00严禁裸本地时间(最大的坑)
版本 每条带 ver,标准演进不破坏老读取方

2. 记录信封(所有记录共用)

{
  "ver": "1.0",
  "type": "treat",            // treat | bp | alarm | status | setting
  "id": "BSLRS232...0379-1781515321-0", // 幂等键(上报去重用,见 kb/44);本地落盘可省略
  "dev": "BSLRS232...0379",   // 采集盒/设备唯一号
  "sn": "FMC4008-12345",      // 血透机序列号(没有则盒子号代)
  "brand": "fresenius",       // 品牌枚举(见 §8)
  "model": "4008A",           // 机型
  "port": "ttyUSB0",          // 采集串口
  "ts": "2026-06-15T13:21:05+08:00",
  "data": { ... }             // 按 type 不同,见下
}

data 里的负载字段才是各机型归一后的测量值;信封字段所有类型一致。 id 仅在上报时必带(at-least-once 去重,见 44-上报接口规范.md);不上报场景可省略。

3. type=treat 治疗实时数据(data 字段)

字段 单位 含义
elapsed s 已治疗时间
remain s 剩余时间
treat_set s 预设治疗时长
filter_life s 滤器使用时间(CRRT)
mode str 治疗模式(归一名)
mode_raw str 治疗模式原始值
qb mL/min 血流量
qb_avg mL/min 平均血流量
bv L 累计血容量
vp mmHg 静脉压
ap mmHg 动脉压
tmp mmHg 跨膜压
uf mL 超滤量
uf_goal mL 预设超滤量
ufr mL/h 超滤率
qd mL/min 透析液流速
qd_avg mL/min 平均透析液流速
dvol mL 透析液总量
cond mS/cm 电导率
temp_d °C 透析液温度
temp_d_avg °C 平均透析液温度
temp_b °C 血温
temp_body °C 体温
qsub mL/min 置换液率
sub_vol mL 置换液量
qrp mL/min RP 液流量
hepr mL/h 肝素速率
hep_vol mL 肝素量
k mL/min 清除率
ktv Kt/V
balance mL 液体平衡(CRRT)
intake_goal mL 液体摄入目标
pid str 患者卡号(预留)
pname str 患者姓名(预留)

4. type=bp 血压

字段 单位 含义
sys mmHg 收缩压
dia mmHg 舒张压
map mmHg 平均动脉压(可算,可缺省)
pulse bpm 心率

5. type=alarm 报警/事件

字段 类型 含义
text str 事件/报警文本(归一或原文)
code str 机器报警码(有则带)
level str info | warn | alarm(有则带)
active bool 触发/解除(有则带)

6. type=status 设备状态

字段 类型 含义
state str offline | standby | treating | alarm | ended
phase str 治疗阶段(预冲/治疗/回血…,可缺省)
alarm bool 当前是否有报警(可缺省)

7. type=setting 预设处方(可选,快照)

复用 §3 同名字段表达"预设值":qb / ufr / qsub / sub_vol / hepr / qd / temp_d(在 setting 记录里即代表处方设定值)。

8. brand 枚举(小写短码)

fresenius(费森尤斯) · gambro(金宝) · baxter(百特) · bbraun(贝朗/Baravat) · toray(东丽) · wego(威高) · nipro(尼普洛) · sws(山外山) · wesley · biolight · jms · maxlinear · mednovel

9. 完整示例

{
  "ver": "1.0", "type": "treat",
  "dev": "BSLRS23220260379", "sn": "12345",
  "brand": "fresenius", "model": "4008A", "port": "ttyUSB0",
  "ts": "2026-06-15T13:21:05+08:00",
  "data": {
    "elapsed": 3600, "remain": 10800, "treat_set": 14400,
    "qb": 250, "vp": 120, "ap": -150, "tmp": 140,
    "uf": 1200, "uf_goal": 3000, "ufr": 800,
    "qd": 500, "cond": 14.0, "temp_d": 36.5
  }
}