/**
|
* 配置服务模块
|
* 负责配置的校验、保存和应用,包括各种配置项的规则验证和回调处理
|
*/
|
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
|