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 可能不兼容。优先级:
- 内网/蒲公英组网内走
http://(链路已在专网,最省心,推荐 POC 与多数生产); - 必须公网加密 →
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 批量信封
处理:
- 校验
X-Token(如启用);不过 → 401(盒子会重试,运维需修配置)。 - 遍历
batch[]:按id幂等 upsert(已存在则跳过,仍计成功)。 - 全部落库成功(含幂等命中)→ HTTP 200
{"code":0};任一未持久化 → 返回非 2xx(盒子整批重试)。 - 按记录
ts入库,不依赖到达顺序。 - 不同
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
}
}