// propertyMapper.js - 阿里云物模型数据映射 const logger = require('./logger'); const fs = require('fs'); const path = require('path'); class PropertyMapper { constructor() { // 从文件加载物模型,或使用内置配置 this.schema = this.loadSchema(); this.propertyMap = this.buildPropertyMap(); } /** * 加载物模型定义 */ loadSchema() { try { // 尝试从 schema.json 文件加载 const schemaPath = path.join(__dirname, 'schema.json'); if (fs.existsSync(schemaPath)) { const schemaContent = fs.readFileSync(schemaPath, 'utf8'); logger.info('✅ 已从 schema.json 加载物模型'); return JSON.parse(schemaContent); } } catch (err) { logger.warn(`加载 schema.json 失败: ${err.message}`); } // 返回默认配置 logger.info('ℹ️ 使用默认物模型配置'); return this.getDefaultSchema(); } /** * 获取默认物模型(完整版 - 与schema.json同步) */ getDefaultSchema() { return { properties: [ { identifier: 'A', name: '脱水目标量' }, { identifier: 'B', name: '脱水量' }, { identifier: 'C', name: '脱水速率' }, { identifier: 'D', name: '血液流速' }, { identifier: 'E', name: '肝素速率' }, { identifier: 'F', name: '透析液温度' }, { identifier: 'G', name: '透析液电导度' }, { identifier: 'H', name: '静脉压' }, { identifier: 'I', name: '透析液压' }, { identifier: 'J', name: '跨膜压' }, { identifier: 'K', name: '透析时间' }, { identifier: 'a', name: '透析液温度报警' }, { identifier: 'b', name: '电导度报警' }, { identifier: 'c', name: '静脉压报警' }, { identifier: 'd', name: '透析液压力报警' }, { identifier: 'e', name: '跨膜压警报' }, { identifier: 'f', name: '气泡侦测器警报' }, { identifier: 'g', name: '漏血报警' }, { identifier: 'h', name: '其他报警' }, { identifier: 'L', name: '透析液流速' }, { identifier: 'M', name: 'BPM监测时间' }, { identifier: 'N', name: 'BPM最高血压' }, { identifier: 'O', name: 'BPM最低血压' }, { identifier: 'P', name: 'BPM脉冲' }, { identifier: 'Q', name: '收缩压上限' }, { identifier: 'R', name: '收缩压下限' }, { identifier: 'S', name: 'BPM压脉带压力' }, { identifier: 'T', name: 'BPM检测间隔时间' }, { identifier: 'U', name: '血流总量' }, { identifier: 'V', name: '静脉压上限报警' }, { identifier: 'W', name: '静脉压下限报警' }, { identifier: 'X', name: '肝素总量' }, { identifier: 'Y', name: '透析液压上限报警' }, { identifier: 'Z', name: '透析液压下限警报' }, { identifier: 'i', name: '单补钠个性化程序' }, { identifier: 'j', name: '脱水个性化程序' }, { identifier: 'k', name: '透析液选择' }, { identifier: 'l', name: '电导度档位' }, { identifier: 'm', name: '数据通信状态' }, { identifier: 'n', name: '序列号' }, { identifier: 'o', name: '动脉压' }, { identifier: 'p', name: '动脉压警报' }, { identifier: 'q', name: '动脉压上限警报' }, { identifier: 'r', name: '动脉压下限警报' }, { identifier: 's', name: '跨膜压上限警报' }, { identifier: 't', name: '跨膜压下限警报' }, { identifier: 'u', name: '置换液速率' }, { identifier: 'v', name: '置换液目标量' }, { identifier: 'w', name: '置换液进程量' }, { identifier: 'x', name: '电导度个性化程序' }, { identifier: 'y', name: '血液流速个性化程序' }, { identifier: 'z', name: '肝素个性化程序' }, { identifier: 'C53', name: '透析液个性化程序' }, { identifier: 'C54', name: '透析液温度初始设置' }, { identifier: 'C55', name: '缺水2警报' }, { identifier: 'suedtime', name: '传输时间' }, { identifier: 'deviceType', name: 'deviceType' }, { identifier: 'IPAddress', name: 'IP地址' }, { identifier: 'deviceName', name: '设备名称' }, { identifier: 'warn', name: '警告' }, { identifier: 'ICCID', name: 'iccid' }, { identifier: 'mb', name: '脉搏-德朗' }, { identifier: 'szy', name: '舒张压-德朗' }, { identifier: 'ssy', name: '收缩压-德朗' }, { identifier: 'sysj', name: '剩余时间-德朗' }, { identifier: 'bjbh', name: '报警编号-德朗' }, { identifier: 'xlllsd', name: '血泵流量设定-德朗' } ] }; } /** * 构建属性映射表 (identifier -> name) */ buildPropertyMap() { const map = {}; if (this.schema.properties && Array.isArray(this.schema.properties)) { this.schema.properties.forEach(prop => { map[prop.identifier] = prop.name; }); } return map; } /** * 获取属性名称 * @param {string} identifier - 属性标识符 * @returns {string} 属性名称 */ getPropertyName(identifier) { return this.propertyMap[identifier] || identifier; } /** * 将原始数据转换为标准格式 * @param {object} rawData - 原始数据对象 * @returns {array} 转换后的数据数组 */ mapData(rawData) { if (!rawData || typeof rawData !== 'object') { logger.warn('输入数据无效'); return []; } const mappedData = []; for (const [key, value] of Object.entries(rawData)) { // 跳过内部字段 if (key.startsWith('_')) { continue; } // 获取属性名称 const name = this.getPropertyName(key); mappedData.push({ identifier: key, name: name, value: value }); } return mappedData; } /** * 将原始数据转换为 HTTP 响应格式 * @param {object} rawData - 原始数据 * @param {string} deviceNumber - 设备号 * @returns {array} 转换后的属性数组 */ transformForHTTP(rawData, deviceNumber) { const mappedData = this.mapData(rawData); return mappedData; } /** * 将原始数据转换为 MQTT 发布格式 * @param {object} rawData - 原始数据 * @param {string} deviceNumber - 设备号 * @returns {object} 转换后的数据 */ transformForMQTT(rawData, deviceNumber) { const mappedData = this.mapData(rawData); return { deviceNumber: deviceNumber, deviceId: deviceNumber, timestamp: new Date().toISOString(), data: mappedData }; } /** * 将原始数据转换为阿里云物联网平台格式 * @param {object} rawData - 原始数据 * @returns {object} 转换后的属性对象 */ transformForAliyun(rawData) { const props = {}; for (const [key, value] of Object.entries(rawData)) { // 跳过内部字段 if (key.startsWith('_')) { continue; } // 直接使用 identifier 作为属性 props[key] = value; } return props; } /** * 获取所有属性定义 */ getAllProperties() { return this.schema.properties || []; } /** * 获取属性定义 * @param {string} identifier - 属性标识符 * @returns {object} 属性定义 */ getPropertyDefinition(identifier) { if (!this.schema.properties) return null; return this.schema.properties.find(p => p.identifier === identifier); } /** * 统计属性信息 */ getPropertyStats() { const properties = this.schema.properties || []; const readOnlyCount = properties.filter(p => p.accessMode === 'r').length; const readWriteCount = properties.filter(p => p.accessMode === 'rw').length; // 统计数据类型分布 const dataTypeDistribution = new Map(); const dataTypes = new Set(); properties.forEach(prop => { dataTypes.add(prop.dataType); dataTypeDistribution.set( prop.dataType, (dataTypeDistribution.get(prop.dataType) || 0) + 1 ); }); return { totalProperties: properties.length, readOnlyCount: readOnlyCount, readWriteCount: readWriteCount, dataTypes: dataTypes, dataTypeDistribution: Array.from(dataTypeDistribution.entries()) }; } } // 导出单例 module.exports = new PropertyMapper();