chenyc
2026-03-22 7885cede659f3255be56f77c1eef2ada7387d6f1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// 配置文件:默认配置 + 同级目录 ../config.json 覆盖
// 这样打包成可执行文件 / APK 时,只需修改外部 JSON 即可调整参数。
 
const fs = require("fs");
const path = require("path");
 
const defaultConfig = {
  log: {
    // 是否写入本地日志文件
    toFile: true,
    // 日志目录(相对本项目根目录)
    dir: "../logs",
    // 预留日志级别控制(当前未严格使用)
    level: "debug"
  },
  tcp: {
    host: "0.0.0.0",
    // 为避免本机已有程序占用,测试阶段改用 19000 端口
    port: 19000,
    idleTimeoutMs: 2 * 60 * 1000, // 设备超时时间 2 分钟
    // 单连接缓冲区上限,防止异常数据导致内存持续增长
    maxBufferBytes: 1024 * 1024,
    // 每次 data 事件最多解析帧数,避免单连接长时间占用事件循环
    maxFramesPerChunk: 50,
    // TCP 最大并发连接数(覆盖 50 台透析机场景并保留余量)
    maxConnections: 200
  },
  http: {
    enabled: false,
    host: "0.0.0.0",
    // HTTP 服务测试端口改为 19001
    port: 19001,
    cors: {
      enabled: true,
      origins: ["*"]
    },
    rateLimit: {
      singleDeviceMs: 5000,
      allDevicesMs: 60 * 1000
    }
  },
  mqtt: {
    enabled: false,
    url: "mqtt.ihemodialysis.com",
    port: 62283,
    username: "data",
    password: "data#2018",
    reconnectPeriod: 5000,
    defaultTopicPrefix: "touxiji",
    // 是否对每台机的最后一条消息做 retain,方便后续订阅方随时获取最新快照
    retain: true
  },
  aliyun: {
    enabled: true,
    // 旧项目中三元组接口的基础地址,如需更换请修改此处
    baseURL: "https://things.icoldchain.cn/",
    // 其他阿里云物联网接入参数可在此扩展
  }
};
 
// 递归浅合并:以 external 为准覆盖 default
function mergeConfig(defaultCfg, externalCfg) {
  if (!externalCfg || typeof externalCfg !== "object") return defaultCfg;
  const result = { ...defaultCfg };
  for (const key of Object.keys(externalCfg)) {
    const val = externalCfg[key];
    if (
      val &&
      typeof val === "object" &&
      !Array.isArray(val) &&
      typeof defaultCfg[key] === "object" &&
      defaultCfg[key] !== null
    ) {
      result[key] = mergeConfig(defaultCfg[key], val);
    } else {
      result[key] = val;
    }
  }
  return result;
}
 
// 运行根目录:
// - 普通 Node 环境下:项目根目录;
// - 使用 pkg 打包后:可执行文件所在目录。
const appRoot = (function () {
  if (process.pkg) {
    // pkg 打包后的场景
    return path.dirname(process.execPath);
  }
  // 开发环境:src 目录的上一级
  return path.resolve(__dirname, "..");
})();
 
let externalConfig = {};
try {
  const jsonPath = path.join(appRoot, "config.json");
  if (fs.existsSync(jsonPath)) {
    const raw = fs.readFileSync(jsonPath, "utf8");
    externalConfig = JSON.parse(raw);
  }
} catch (e) {
  // 如果 JSON 解析失败,直接忽略,继续使用默认配置
}
 
module.exports = mergeConfig(defaultConfig, externalConfig);