JMS 联机服务用于管理与监控多台 GC-110N 透析设备。通过持久 TCP 长连接 + 定时 K 指令轮询,实时采集 32 项治疗参数,经 MQTT / 阿里云 IoT 上传至云端,并提供本地 Web 监控大屏。
┌──────────┐ TCP:10001 ┌─────────────────┐ MQTT ┌──────────────┐
│ GC-110N │←──────────────→│ jms-connection │───────────→│ MQTT Broker │
│ 透析机 1 │ K 轮询 (10s) │ -service │ └──────────────┘
└──────────┘ │ │
··· │ dashboard:3100 │ HTTP ┌──────────────┐
┌──────────┐ │ ← 浏览器访问 │───────────→│ 阿里云 IoT │
│ GC-110N │←──────────────→│ │ postProps │ (三元组API) │
│ 透析机 N │ └─────────────────┘ └──────────────┘
| 项目 | 要求 |
|---|---|
| 操作系统 | Linux(推荐 Ubuntu 20.04+ / CentOS 7+)或 Windows Server 2016+ |
| Node.js | ≥ 18.x(推荐 20 LTS) |
| npm | ≥ 9.x(随 Node.js 附带) |
| 内存 | ≥ 256 MB(本服务占用 ~80 MB) |
| 磁盘 | ≥ 1 GB(日志滚动保留 30 天) |
| 网络 | 需与 GC-110N 设备在同一局域网,或能通过路由访问设备 TCP 端口 |
node -v # 应输出 v18.x 或 v20.x
npm -v # 应输出 9.x 或 10.x
Ubuntu/Debian:bash curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - sudo apt-get install -y nodejs
CentOS/RHEL:bash curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash - sudo yum install -y nodejs
Windows:
从 https://nodejs.org 下载 LTS 安装包,勾选「Add to PATH」。
# 将部署包解压或从 Git 拉取
cd /opt
tar -xzf jms-connection-service.tar.gz -C /opt/
# 或: git clone <repo-url> /opt/jms-connection-service
cd /opt/jms-connection-service
npm install --production
仅安装生产依赖(
dependencies),不安装 devDependencies。
复制并修改配置文件:
# 如无可直接编辑 config.json(已内置在部署包中)
vi config.json
详细配置项说明见 第 4 节。
在启动服务前,确认服务器能连通 GC-110N 设备:
# 测试 TCP 端口(替换为实际 IP 和端口)
nc -zv 192.168.160.1 10001
# 或 Windows:
# Test-NetConnection -ComputerName 192.168.160.1 -Port 10001
# 前台运行(调试用)
node index.js
# 后台运行(生产推荐,使用 PM2)
npm install -g pm2
pm2 start index.js --name jms-connection-service
pm2 save
pm2 startup # 设置开机自启
启动成功后控制台输出:
配置校验通过: 1/1 台设备启用
联机服务启动
日志目录: /opt/jms-connection-service/logs 保留: 30 天
轮询间隔: 10s 重连退避: 3s~60s
Dashboard 已启动: http://0.0.0.0:3100
联机服务已启动 → Dashboard: http://localhost:3100
浏览器打开 http://<服务器IP>:3100,可看到设备连接状态、实时数据和上传状态。
配置文件为根目录下的 config.json,格式如下:
{
// ── 全局参数 ──
"pollIntervalMs": 10000, // K 轮询间隔(毫秒),默认 10000 = 10s
"connectTimeoutMs": 5000, // TCP 握手超时(毫秒)
"reconnectBaseMs": 3000, // 重连退避基数(毫秒)
"reconnectMaxMs": 60000, // 重连退避上限(毫秒),实际延迟 = min(base*2^(n-1), max)
"logDir": "./logs", // 日志目录(相对路径基于项目根目录)
"logRetentionDays": 30, // 日志保留天数
"dashboardPort": 3100, // 监控大屏 HTTP 端口
// ── MQTT 上传(可选) ──
"mqtt": {
"enabled": false, // 是否启用 MQTT 上传
"brokerUrl": "mqtt.ihemodialysis.com", // MQTT Broker 地址(不含协议前缀)
"port": 62283, // MQTT 端口
"username": "data", // 认证用户名
"password": "data#2018", // 认证密码
"reconnectPeriod": 5000, // MQTT 重连间隔(毫秒)
"clientCode": "CLIENT9227100800901fcKB", // 客户端标识
"defaultTopicPrefix": "touxiji", // 默认 Topic 前缀,发布 topic = {prefix}/{deviceNo}
"qos": 1 // MQTT QoS 级别
},
// ── 阿里云 IoT 上传(可选) ──
"aliyun": {
"enabled": true, // 是否启用阿里云上传
"autoRegister": true, // 是否自动注册设备(三元组不存在时自动创建)
"tupleApiBaseUrl": "https://things.icoldchain.cn/", // 三元组 API 地址
"tupleApiPath": "device/info/getAliyunDeviceSecret", // 三元组 API 路径
"tupleRetryCooldownMs": 60000 // 三元组请求失败后冷却时间(毫秒)
},
// ── 设备列表 ──
"devices": [
{
"ip": "192.168.160.1", // 设备 IP 地址(必填)
"port": 10001, // 设备 TCP 端口(必填,GC-110N 默认 10001)
"serialNumber": "xy123", // 设备序列号(必填,用于上传标识)
"enabled": true // 是否启用(false 则跳过此设备)
}
// 可添加多台设备...
]
}
服务使用指数退避策略处理断线重连:
| 断开次数 | 延迟时间(base=3000, max=60000) |
|---|---|
| 第 1 次 | 3s |
| 第 2 次 | 6s |
| 第 3 次 | 12s |
| 第 4 次 | 24s |
| 第 5 次 | 48s |
| 第 6 次+ | 60s(达到上限) |
连接成功后计数器重置。
"devices": [
{ "ip": "192.168.1.101", "port": 10001, "serialNumber": "D001", "enabled": true },
{ "ip": "192.168.1.102", "port": 10001, "serialNumber": "D002", "enabled": true },
{ "ip": "192.168.1.103", "port": 10001, "serialNumber": "D003", "enabled": false }
]
pm2 status # 查看进程状态
pm2 logs jms-connection-service # 查看实时日志
pm2 restart jms-connection-service # 重启服务
pm2 stop jms-connection-service # 停止服务
pm2 delete jms-connection-service # 从 PM2 移除
日志文件位于 logDir 配置的目录中(默认 ./logs),按天滚动:
logs/
├── service.2026-05-10.log
├── service.2026-05-11.log
└── service.2026-05-12.log # 最多保留 30 天
日志中的关键符号:
| 符号 | 含义 |
|---|---|
→ |
发送 K 请求 |
← |
收到 K 响应(含原始报文) |
✓ |
TCP 连接成功 / 上传通道连接成功 |
✂ |
TCP 断开 |
↻ |
计划重连 |
↑ |
上传成功 |
✗ |
错误 |
! |
警告 |
# PM2 方式
pm2 stop jms-connection-service
# 直接进程方式(发送 SIGINT)
kill -2 $(pgrep -f "node index.js")
服务会依次关闭:设备连接 → 上传模块 → Dashboard → 日志清理定时器。
服务作为 TCP 客户端,需要访问以下目标:
| 目标 | 协议 | 端口 | 用途 |
|---|---|---|---|
| GC-110N 设备 IP | TCP | 10001(可配置) | K 指令轮询 |
| MQTT Broker | TCP | 62283(可配置) | MQTT 上传 |
| 阿里云 IoT SDK | TCP | 443 (TLS) | 阿里云属性上报 |
| 三元组 API | HTTPS | 443 | 获取设备三元组 |
| 来源 | 协议 | 端口 | 用途 |
|---|---|---|---|
| 浏览器(局域网) | HTTP | 3100(可配置) | 监控大屏 |
| 浏览器(局域网) | WebSocket | 3100(同 HTTP) | 实时数据推送 |
# 允许 Dashboard 入站(仅内网)
iptables -A INPUT -s 192.168.0.0/16 -p tcp --dport 3100 -j ACCEPT
iptables -A INPUT -p tcp --dport 3100 -j DROP
# 出站一般默认为 ACCEPT,无需额外配置
# 允许 Dashboard 端口入站
New-NetFirewallRule -DisplayName "JMS Dashboard" -Direction Inbound -Protocol TCP -LocalPort 3100 -Action Allow
访问 http://<服务器IP>:3100 进入监控大屏:
点击设备行可展开详情面板,显示:
K0000A00010B00005...)再次点击收起。
| 颜色 | 含义 |
|---|---|
| 🟢 绿色 | 在线 / 正常 |
| 🟡 黄色 | 连接中 |
| 🔵 蓝色 | 等待轮询 |
| 🔴 红色 | 断线 / 异常 |
| ⚫ 灰色 | 已禁用 / 等待 |
ping <设备IP> 和 nc -zv <设备IP> <端口>logs/service.YYYY-MM-DD.log 搜索 ✂ 和 ✗← 行,检查原始报文格式是否正确(应以 K 开头 + 4 位状态码)aliyun.tupleApiBaseUrl 能否从服务器访问(curl <url>)aliyun.autoRegister: true 自动注册,或先在阿里云 IoT 平台创建设备tuple 事件mqtt.enabled: truedashboardPort 未被占用:netstat -tlnp | grep 3100服务内置了日志清理机制,每小时检查一次并删除超过保留天数的日志。如需手动清理:
# 删除 30 天前的日志
find ./logs -name "*.log" -mtime +30 -delete
jms-connection-service/
├── index.js # 入口文件
├── config.json # 配置文件 ★
├── package.json # 依赖声明
├── lib/
│ ├── logger.js # 日志模块(按天滚动,控制台彩色 + 文件纯文本)
│ ├── data-cache.js # 内存数据缓存(Map<ip, deviceData>)
│ ├── device-manager.js # 设备管理(遍历 config.devices 创建连接)
│ ├── device-connection.js # 单设备 TCP 长连接 + K 轮询 + 重连
│ ├── protocol.js # GC-110N K 格式解析器
│ └── upload/
│ ├── index.js # 上传总控(顺序执行 MQTT → 阿里云)
│ ├── mqtt-uploader.js # MQTT 单例客户端
│ ├── aliyun-uploader.js # 阿里云 IoT SDK 设备实例池
│ ├── tuple-api.js # 三元组 API HTTP 请求
│ └── field-mapper.js # GC-110N ID → 阿里云 identifier 映射
└── dashboard/
├── server.js # HTTP + WebSocket 服务
└── public/
└── index.html # 监控大屏单页
| ID | 中文名称 | 单位 | 宽度(字节) |
|---|---|---|---|
| A | 目标除水量 | L | 5 |
| B | 当前除水量 | L | 5 |
| C | 除水速度 | L/h | 5 |
| D | 血液流量 | mL/min | 5 |
| E | 注射器泵速度 | mL/h | 5 |
| F | 透析液温度 | ℃ | 5 |
| G | 透析液浓度 | mS/cm | 5 |
| H | 静脉压 | mmHg | 5 |
| I | 透析液压 | mmHg | 5 |
| J | TMP | mmHg | 5 |
| K | 治疗经过时间 | min | 5 |
| L | 透析液流量 | mL/min | 5 |
| a | 液温报警 | — | 1 |
| b | 浓度报警 | — | 1 |
| c | 静脉压报警 | — | 1 |
| d | 液压报警 | — | 1 |
| e | TMP 报警 | — | 1 |
| f | 气泡检测报警 | — | 1 |
| g | 漏血报警 | — | 1 |
| h | 其他报警 | — | 1 |
| M | 治疗中标志 | — | 1 |
| N | 治疗模式 | — | 1 |
| O | 目标补液量 | L | 5 |
| P | 当前补液量 | L | 5 |
| Q | 补液速度 | L/h | 5 |
| R | 补液温度 | ℃ | 5 |
| S | 血压测量时刻 | HHMMSS | 6(变长) |
| T | 收缩压 | mmHg | 5 |
| U | 舒张压 | mmHg | 5 |
| V | 脉搏 | bpm | 5 |
| i | 血压报警 | — | 1 |
| W | 注射器泵累计量 | mL | 5 |