/** * 配置服务模块 * 负责配置的校验、保存和应用,包括各种配置项的规则验证和回调处理 */ import utils from '../common/utils/utils.js' import config from '../../dxmodules/dxConfig.js' import mqtt from '../../dxmodules/dxMqtt.js' import std from '../../dxmodules/dxStd.js' import ntp from '../../dxmodules/dxNtp.js' import common from '../../dxmodules/dxCommon.js' import driver from '../driver.js' import bus from '../../dxmodules/dxEventBus.js' import mqttService from './mqttService.js' import logger from '../../dxmodules/dxLogger.js' const configService = {} /** * IP地址校验正则 * 匹配以点分十进制形式表示的 IP 地址,例如:192.168.0.1 */ const ipCheck = v => /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(v) /** * IP地址或域名校验正则(带端口) * 匹配IP地址或域名,可选择带有端口号 */ const ipOrDomainCheckWithPort = v => /^(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,})(:\d{1,5})?$/.test(v); /** * 正整数校验正则 */ const regpCheck = v => /^[1-9]\d*$/.test(v) /** * 非负整数校验正则 */ const regnCheck = v => /^([1-9]\d*|0{1})$/.test(v) /** * 所有支持的配置项的检验规则以及设置成功后的回调 * rule:校验规则,返回true校验成功,false校验失败 * callback:配置修改触发回调 */ const supported = { // 人脸配置 face: { similarity: { rule: v => typeof v == 'number' && v >= 0 && v <= 1, callback: v => driver.face.faceUpdateConfig({ score: v }) }, livenessOff: { rule: v => [0, 1].includes(v), callback: v => driver.face.faceUpdateConfig({ livingCheckEnable: v }) }, livenessVal: { rule: v => typeof v == 'number' && v >= 0 && v <= 1, callback: v => driver.face.faceUpdateConfig({ livingScore: v }) }, showNir: { rule: v => [0, 1].includes(v), callback: v => driver.capturer.showNir(v) }, detectMask: { rule: v => [0, 1].includes(v), callback: v => driver.face.faceUpdateConfig({ detectMaskEnable: v }) }, stranger: { rule: v => [0, 1, 2].includes(v) }, voiceMode: { rule: v => [0, 1, 2].includes(v) }, voiceModeDate: { rule: v => typeof v == 'string' }, }, // MQTT配置 mqtt: { addr: { rule: ipOrDomainCheckWithPort }, clientId: { rule: v => typeof v == 'string' }, username: { rule: v => typeof v == 'string' }, password: { rule: v => typeof v == 'string' }, qos: { rule: v => [0, 1, 2].includes(v) }, prefix: { rule: v => typeof v == 'string' }, willtopic: { rule: v => typeof v == 'string' }, onlinecheck: { rule: v => [0, 1, 2].includes(v) }, timeout: { rule: regpCheck }, }, // 网络配置 net: { type: { rule: v => [0, 1, 2, 4].includes(v) }, // 网络类型 dhcp: { rule: v => [1, 2, 3].includes(v) }, // DHCP设置 ip: { rule: ipCheck }, // IP地址 gateway: { rule: ipCheck }, // 网关地址 dns: { rule: v => !v.split(",").some(ip => !ipCheck(ip)) }, // DNS服务器 mask: { rule: ipCheck }, // 子网掩码 mac: { rule: v => typeof v == 'string' }, // MAC地址 ssid: { rule: v => typeof v == 'string' }, // WiFi SSID psk: { rule: v => typeof v == 'string' }, // WiFi密码 }, // NTP时间同步配置 ntp: { ntp: { rule: v => [0, 1].includes(v) }, // NTP开关 server: { rule: ipCheck }, // NTP服务器 gmt: { rule: v => typeof v == 'number' && v >= 0 && v <= 24, callback: v => ntp.updateGmt(v) }, // 时区设置 }, // 系统配置 sys: { uuid: { rule: v => typeof v == 'string' }, // 设备UUID model: { rule: v => typeof v == 'string' }, // 设备型号 mode: { rule: v => typeof v == 'string', callback: v => setMode(v) }, // 设备模式 sn: { rule: v => typeof v == 'string' }, // 设备序列号 version: { rule: v => typeof v == 'string' }, // 系统版本 releaseDate: { rule: v => typeof v == 'string' }, // 发布日期 nfc: { rule: v => [0, 1].includes(v) }, // NFC开关 nfcIdentityCardEnable: { rule: v => [1, 3].includes(v) }, // 云证开关 pwd: { rule: v => [0, 1].includes(v) }, // 密码开门开关 strangerImage: { rule: v => [0, 1].includes(v) }, // 陌生人保存图片开关 accessImageType: { rule: v => [0, 1].includes(v) }, // 通行图片类型 interval: { rule: regnCheck }, // 系统间隔设置 }, // 门禁配置 access: { relayTime: { rule: regpCheck }, // 继电器动作时间 offlineAccessNum: { rule: regpCheck }, // 离线开门次数 tamperAlarm: { rule: v => [0, 1].includes(v) }, // 防拆报警开关 }, // 基础配置 base: { firstLogin: { rule: v => [0, 1].includes(v) }, // 首次登录标志 brightness: { rule: regnCheck, callback: v => driver.face.setDisplayBacklight(v) }, // 屏幕亮度 brightnessAuto: { rule: v => [0, 1].includes(v) }, // 自动亮度开关 showIp: { rule: v => [0, 1].includes(v), callback: v => driver.screen.hideIp(v) }, // 是否显示IP地址 showSn: { rule: v => [0, 1].includes(v), callback: v => driver.screen.hideSn(v) }, // 是否显示序列号 appMode: { rule: v => [0, 1].includes(v), callback: v => driver.screen.appMode(v) }, // 应用模式 screenOff: { rule: regnCheck, callback: v => bus.fire("screenManagerRefresh") }, // 熄屏时间 screensaver: { rule: regnCheck, callback: v => bus.fire("screenManagerRefresh") }, // 屏幕保护 volume: { rule: regnCheck, callback: v => driver.alsa.volume(v) }, // 音量设置 password: { rule: v => typeof v == 'string' && v.length >= 8 }, // 管理员密码 language: { rule: v => ["EN", "CN"].includes(v), callback: v => driver.screen.changeLanguage() }, // 语言设置 showProgramCode: { rule: v => [0, 1].includes(v) }, // 是否显示程序代码 showIdentityCard: { rule: v => [0, 1].includes(v) }, // 是否显示身份证信息 luminanceWhite: { rule: v => typeof v == 'number' && v >= 0 && v <= 100, callback: v => driver.pwm.luminanceWhite(v) }, // 白光亮度 luminanceNir: { rule: v => typeof v == 'number' && v >= 0 && v <= 100, callback: v => driver.pwm.luminanceNir(v) }, // 红外光亮度 }, // 密码通行配置 passwordAccess: { passwordAccess: { rule: v => [0, 1].includes(v) }, // 密码通行开关 emergencyPassword: { rule: v => typeof v == 'string' && v.length >= 8 }, // 应急开仓密码 }, // HTTP配置 http: { gas: { rule: v => typeof v == 'string' }, // HTTP_气体 status: { rule: v => typeof v == 'string' }, // HTTP_状态 safeInputAccess: { rule: v => typeof v == 'string' } // HTTP接口路径 }, // 仓廒名称 houseName: {}, // 库区名称 GranaryName: {}, // 更新配置 update: { gasTime: { rule: regpCheck }, // 气体浓度更新时间(秒) statusTime: { rule: regpCheck } // 状态信息更新时间(秒) } } /** * 需要重启的配置项 */ const needReboot = ["sys.nfc", "sys.nfcIdentityCardEnable", "ntp"] configService.needReboot = needReboot /** * 修改设备模式 * @param {string} params - 模式参数 */ function setMode(params) { common.systemWithRes(`echo 'app' > /etc/.app_v1`, 2) common.setMode(params) } /** * 配置json校验并保存 * @param {object} data - 配置json对象 * @returns {boolean|string} true表示校验并保存成功,string表示错误信息 */ configService.configVerifyAndSave = function (data) { let netFlag = false // 网络配置标志 let mqttFlag = false // MQTT配置标志 let isReboot = false // 是否需要重启 for (const key in data) { if (key == 'net') { netFlag = true } if (key == 'mqtt') { mqttFlag = true } // 检查配置组是否支持 if (!supported[key]) { return key + " not supported" } const item = data[key]; if (typeof item != 'object') { // 顶层配置项,直接保存 config.set(key, item) continue } // 检查是否需要重启 if (needReboot.includes(key)) { isReboot = true } for (const subKey in item) { let option = supported[key][subKey] if (isEmpty(option)) { return subKey + " not supported" } const value = item[subKey]; // 检查是否需要重启 if (needReboot.includes(key + "." + subKey)) { isReboot = true } // 校验配置值 if (!option.rule || option.rule(value)) { // 没有校验规则默认校验通过 config.set(key + "." + subKey, value) if (option.callback) { // 执行配置设置回调 option.callback(value) } } else { return value + " check failure" } } } // 保存配置 config.save() // 检查需要重启的配置,3秒后重启 if (isReboot) { driver.screen.upgrade({ title: "confirm.restartDevice", content: "confirm.restartDeviceDis" }) common.asyncReboot(3) } // 处理网络配置变更 if (netFlag) { // 等待 1 秒 因为需要返回 mqtt std.setTimeout(() => { bus.fire("switchNetworkType", config.get("net.type")) }, 1000); } // 处理MQTT配置变更 if (mqttFlag) { let option = { mqttAddr: config.get("mqtt.addr"), clientId: config.get('mqtt.clientId') + std.genRandomStr(3), subs: mqttService.getTopics(), username: config.get("mqtt.username"), password: config.get("mqtt.password"), qos: config.get("mqtt.qos"), willTopic: config.get("mqtt.willTopic"), willMessage: JSON.stringify({ "uuid": config.get("sys.uuid") }) } logger.info("重启mqtt", JSON.stringify(option)) // 销毁 mqtt 重新 init bus.fire(mqtt.RECONNECT, option) } // 触发配置更新事件,通知其他模块配置已更新 bus.fire('configUpdated') return true } /** * 判空函数 * @param {*} value - 要判断的值 * @returns {boolean} true表示值为空,false表示值不为空 */ function isEmpty(value) { return value === undefined || value === null } export default configService