From a43f8991d3f5fa2ef4e0f3eeeca00fb4afc263c0 Mon Sep 17 00:00:00 2001
From: chenyc <501753378@qq.com>
Date: 星期日, 24 五月 2026 17:08:18 +0800
Subject: [PATCH] gx
---
config.json | 2
lib/obfuscate.js | 34 +++++++++++
tools/decode-log.js | 36 ++++++++++++
scripts/build.js | 2
dashboard/public/index.html | 13 ---
dashboard/server.js | 6 +
lib/logger.js | 5 +
package.json | 3
.claude/settings.local.json | 4 +
DEPLOY.md | 16 ++--
10 files changed, 95 insertions(+), 26 deletions(-)
diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index eb50cf5..578d4fc 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -7,7 +7,9 @@
"Bash(netstat -ano)",
"Bash(powershell *)",
"Bash(curl -s -o nul -w \"%{http_code}\" http://localhost:3100/)",
- "Bash(node scripts/build.js --linux)"
+ "Bash(node scripts/build.js --linux)",
+ "Bash(node tools/decode-log.js --stdin)",
+ "Bash(timeout 3 node -e \"require\\('./lib/logger'\\)\")"
]
}
}
diff --git a/DEPLOY.md b/DEPLOY.md
index d0ab685..a9f15a6 100644
--- a/DEPLOY.md
+++ b/DEPLOY.md
@@ -2,12 +2,12 @@
## 1. 系统概述
-JMS 联机服务用于管理与监控多台 **GC-110N 透析设备**。通过持久 TCP 长连接 + 定时 K 指令轮询,实时采集 32 项治疗参数,经 MQTT / 阿里云 IoT 上传至云端,并提供本地 Web 监控大屏。
+JMS 联机服务用于管理与监控多台 **GC-110N 透析设备**。通过持久 TCP 长连接 + 定时指令轮询,实时采集 32 项治疗参数,经 MQTT / 阿里云 IoT 上传至云端,并提供本地 Web 监控大屏。
```
┌──────────┐ TCP:10001 ┌─────────────────┐ MQTT ┌──────────────┐
│ GC-110N │←──────────────→│ jms-connection │───────────→│ MQTT Broker │
-│ 透析机 1 │ K 轮询 (10s) │ -service │ └──────────────┘
+│ 透析机 1 │ 轮询 (10s) │ -service │ └──────────────┘
└──────────┘ │ │
··· │ dashboard:3100 │ HTTP ┌──────────────┐
┌──────────┐ │ ← 浏览器访问 │───────────→│ 阿里云 IoT │
@@ -233,7 +233,7 @@
```jsonc
{
// ── 全局参数 ──
- "pollIntervalMs": 10000, // K 轮询间隔(毫秒),默认 10s
+ "pollIntervalMs": 10000, // 轮询间隔(毫秒),默认 10s
"connectTimeoutMs": 5000, // TCP 握手超时(毫秒)
"reconnectBaseMs": 3000, // 重连退避基数(毫秒)
"reconnectMaxMs": 60000, // 重连退避上限(毫秒)
@@ -414,7 +414,7 @@
| 目标 | 协议 | 端口 | 用途 |
|------|------|------|------|
-| GC-110N 设备 | TCP | 10001(可配置) | K 指令轮询 |
+| GC-110N 设备 | TCP | 10001(可配置) | 指令轮询 |
| MQTT Broker | TCP | 62283(可配置) | MQTT 上传 |
| 阿里云 IoT | TCP | 443 (TLS) | 属性上报 |
| 三元组 API | HTTPS | 443 | 获取设备凭证 |
@@ -475,8 +475,8 @@
### 9.2 收不到数据 / 字段数为 0
1. 确认设备已进入治疗状态(待机状态可能返回有限数据)
-2. 查看日志中 `←` 行,确认报文以 `K` 开头 + 4 位状态码
-3. 确认设备固件支持 K 格式协议
+2. 查看日志中 `←` 行,确认收到设备报文(混淆格式可通过 `node tools/decode-log.js --stdin` 解码查看)
+3. 确认设备固件协议版本兼容
### 9.3 阿里云上传失败
@@ -519,8 +519,8 @@
│ ├── logger.js # 日志模块(按天滚动)
│ ├── data-cache.js # 内存数据缓存(Map<ip, deviceData>)
│ ├── device-manager.js # 设备管理(遍历创建连接)
-│ ├── device-connection.js # 单设备 TCP 长连接 + K 轮询 + 重连
-│ ├── protocol.js # GC-110N K 格式解析器
+│ ├── device-connection.js # 单设备 TCP 长连接 + 轮询 + 重连
+│ ├── protocol.js # GC-110N 协议解析器
│ └── upload/
│ ├── index.js # 上传总控(顺序:MQTT → 阿里云)
│ ├── mqtt-uploader.js # MQTT 单例客户端
diff --git a/config.json b/config.json
index 9add152..8218e68 100644
--- a/config.json
+++ b/config.json
@@ -26,7 +26,7 @@
},
"devices": [
{
- "ip": "192.168.160.1",
+ "ip": "169.254.233.58",
"port": 10001,
"serialNumber": "xy123",
"enabled": true
diff --git a/dashboard/public/index.html b/dashboard/public/index.html
index 3fd17f0..17a4699 100644
--- a/dashboard/public/index.html
+++ b/dashboard/public/index.html
@@ -80,16 +80,9 @@
padding: 4px 8px; border-radius: 4px; background: #161b22;
font-size: 12px;
}
-.detail-panel .field .fid { color: #58a6ff; font-weight: 600; min-width: 18px; }
.detail-panel .field .fname { color: #8b949e; flex: 1; margin: 0 8px; }
.detail-panel .field .fval { color: #f0f6fc; font-family: 'Consolas', monospace; }
.detail-panel .field .funit { color: #484f58; margin-left: 4px; font-size: 11px; }
-.detail-panel .raw-frame {
- grid-column: 1 / -1; margin-top: 8px; padding: 8px 12px;
- background: #0d1117; border: 1px solid #30363d; border-radius: 4px;
- font-family: 'Consolas', monospace; font-size: 13px; color: #d2a8ff;
- word-break: break-all; max-height: 100px; overflow-y: auto;
-}
.clickable { cursor: pointer; user-select: none; }
.clickable td:first-child { color: #58a6ff; }
@@ -104,7 +97,7 @@
<div class="header">
<div>
<h1>JMS GC-110N 联机服务监控</h1>
- <div class="info">协议 Ver.3.0 · K 格式状态轮询</div>
+ <div class="info">TCP 长连接 · 定时轮询</div>
</div>
<div class="info">
<span class="dot live"></span>
@@ -236,13 +229,11 @@
<div class="detail-panel">
${fields.length === 0 ? '<div style="color:#484f58;grid-column:1/-1;">暂无解析数据</div>' :
fields.map(f => `<div class="field">
- <span class="fid">${f.id}</span>
<span class="fname">${f.name||''}</span>
<span class="fval">${f.displayValue||f.value||'(空)'}</span>
<span class="funit">${f.unit||''}</span>
</div>`).join('')}
- ${dev.rawFrame ? `<div class="raw-frame">${dev.rawFrame}</div>` : ''}
- </div>
+ </div>
</td>
</tr>`;
}
diff --git a/dashboard/server.js b/dashboard/server.js
index 6fbd7d4..3a3b354 100644
--- a/dashboard/server.js
+++ b/dashboard/server.js
@@ -32,11 +32,15 @@
}
function buildSnapshot() {
+ const devices = dataCache.getAll().map((dev) => {
+ const { rawFrame, ...rest } = dev;
+ return rest;
+ });
return {
type: "snapshot",
timestamp: new Date().toISOString(),
summary: dataCache.getSummary(),
- devices: dataCache.getAll(),
+ devices,
config: {
pollIntervalMs: config.pollIntervalMs,
reconnectBaseMs: config.reconnectBaseMs,
diff --git a/lib/logger.js b/lib/logger.js
index 1105e3d..80a5be0 100644
--- a/lib/logger.js
+++ b/lib/logger.js
@@ -2,6 +2,7 @@
const fs = require("fs");
const path = require("path");
+const { obfuscate } = require("./obfuscate");
const C = { reset: "\x1b[0m", dim: "\x1b[2m", green: "\x1b[32m", yellow: "\x1b[33m", red: "\x1b[31m", cyan: "\x1b[36m", white: "\x1b[37m" };
@@ -79,10 +80,10 @@
// ── 数据收发 ──
sendK(ip) {
- log(ip, C.yellow, "→", `发送 K 请求`);
+ log(ip, C.yellow, "→", `发送轮询请求`);
},
recvK(ip, rawFrame, fieldCount, statusCode) {
- log(ip, C.green, "←", `${rawFrame}`);
+ log(ip, C.green, "←", `${obfuscate(rawFrame)}`);
},
// ── 异常 ──
diff --git a/lib/obfuscate.js b/lib/obfuscate.js
new file mode 100644
index 0000000..a82547b
--- /dev/null
+++ b/lib/obfuscate.js
@@ -0,0 +1,34 @@
+"use strict";
+
+const DEFAULT_KEY = "jms-gc110n-log-obfuscate-default";
+
+function getKey() {
+ return process.env.JMS_LOG_KEY || DEFAULT_KEY;
+}
+
+function obfuscate(text) {
+ if (!text) return text;
+ const key = Buffer.from(getKey(), "utf-8");
+ const data = Buffer.from(text, "utf-8");
+ const out = Buffer.alloc(data.length);
+ for (let i = 0; i < data.length; i++) {
+ out[i] = data[i] ^ key[i % key.length];
+ }
+ return "[OBF]" + out.toString("base64");
+}
+
+function deobfuscate(encoded) {
+ if (!encoded) return encoded;
+ if (encoded.startsWith("[OBF]")) {
+ encoded = encoded.slice(5);
+ }
+ const key = Buffer.from(getKey(), "utf-8");
+ const data = Buffer.from(encoded, "base64");
+ const out = Buffer.alloc(data.length);
+ for (let i = 0; i < data.length; i++) {
+ out[i] = data[i] ^ key[i % key.length];
+ }
+ return out.toString("utf-8");
+}
+
+module.exports = { obfuscate, deobfuscate };
diff --git a/package.json b/package.json
index e8fad98..de5a458 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,8 @@
"dev": "node index.js",
"build": "node scripts/build.js",
"build:win": "node scripts/build.js --win",
- "build:linux": "node scripts/build.js --linux"
+ "build:linux": "node scripts/build.js --linux",
+ "decode-log": "node tools/decode-log.js logs/service.2026-05-18.log"
},
"pkg": {
"assets": [
diff --git a/scripts/build.js b/scripts/build.js
index 4423024..5e10890 100644
--- a/scripts/build.js
+++ b/scripts/build.js
@@ -97,7 +97,7 @@
readmeLines.push("──────────────────────────────────────────────────────");
readmeLines.push("");
readmeLines.push("【全局参数】");
- readmeLines.push(" pollIntervalMs = 10000 // K 轮询间隔(毫秒)");
+ readmeLines.push(" pollIntervalMs = 10000 // 轮询间隔(毫秒)");
readmeLines.push(" connectTimeoutMs = 5000 // TCP 握手超时(毫秒)");
readmeLines.push(" reconnectBaseMs = 3000 // 重连退避基数(毫秒)");
readmeLines.push(" reconnectMaxMs = 60000 // 重连退避上限(毫秒)");
diff --git a/tools/decode-log.js b/tools/decode-log.js
new file mode 100644
index 0000000..4464b58
--- /dev/null
+++ b/tools/decode-log.js
@@ -0,0 +1,36 @@
+"use strict";
+
+const fs = require("fs");
+const { deobfuscate } = require("../lib/obfuscate");
+
+function decode(text) {
+ return text.replace(/\[OBF\][A-Za-z0-9+/=]+/g, (match) => {
+ return deobfuscate(match);
+ });
+}
+
+function decodeStdin() {
+ const chunks = [];
+ process.stdin.setEncoding("utf-8");
+ process.stdin.on("data", (chunk) => chunks.push(chunk));
+ process.stdin.on("end", () => {
+ process.stdout.write(decode(chunks.join("")));
+ });
+}
+
+function decodeFile(filePath) {
+ const content = fs.readFileSync(filePath, "utf-8");
+ process.stdout.write(decode(content));
+}
+
+if (process.argv.includes("--stdin")) {
+ decodeStdin();
+} else if (process.argv[2]) {
+ decodeFile(process.argv[2]);
+} else {
+ console.log("用法:");
+ console.log(" node tools/decode-log.js <日志文件> 解码日志文件");
+ console.log(" node tools/decode-log.js --stdin 解码标准输入");
+ console.log("");
+ console.log("密钥: JMS_LOG_KEY 环境变量 (未设置时使用默认值)");
+}
--
Gitblit v1.8.0