From ddc740eab26d72026719e00f52cd10f7b5e016a6 Mon Sep 17 00:00:00 2001 From: 1 <1> Date: Fri, 6 Jun 2025 06:26:26 +0800 Subject: [PATCH] 6.6 --- src/views/iot/device/relay.vue | 2 +- src/views/iot/device/running-status.vue | 2 + src/views/iot/device/voicecard.vue | 808 +++++++++++++++++++++++- 3 files changed, 790 insertions(+), 22 deletions(-) diff --git a/src/views/iot/device/relay.vue b/src/views/iot/device/relay.vue index d4d65af..b9f2beb 100644 --- a/src/views/iot/device/relay.vue +++ b/src/views/iot/device/relay.vue @@ -622,7 +622,7 @@ export default { this.$nextTick(function () { this.MonitorChart(); }); - // console.log("物模型", JSON.stringify(this.deviceInfo.thingsModels)); + console.log("物模型", JSON.stringify(this.deviceInfo.thingsModels)); //物模型排序 if (this.deviceInfo.thingsModels && this.deviceInfo.thingsModels.length > 0) { this.deviceInfo.thingsModels = this.device.thingsModels.sort((a, b) => b.order - a.order); diff --git a/src/views/iot/device/running-status.vue b/src/views/iot/device/running-status.vue index 1bb8fc9..00f3664 100644 --- a/src/views/iot/device/running-status.vue +++ b/src/views/iot/device/running-status.vue @@ -515,6 +515,7 @@ export default { this.$nextTick(function () { this.MonitorChart(); }); + // console.log("物模型", JSON.stringify(this.deviceInfo.thingsModels)); //物模型排序 if (this.deviceInfo.thingsModels && this.deviceInfo.thingsModels.length > 0) { this.deviceInfo.thingsModels = this.device.thingsModels.sort((a, b) => b.order - a.order); @@ -631,6 +632,7 @@ export default { // 监听值的实时更新 this.$busEvent.$on('updateData', (params) => { this.updateParam(params); + // console.log("物模型", JSON.stringify(this.deviceInfo.thingsModels)); }); }, initDataStatus() { diff --git a/src/views/iot/device/voicecard.vue b/src/views/iot/device/voicecard.vue index d7adbf0..ae1fe58 100644 --- a/src/views/iot/device/voicecard.vue +++ b/src/views/iot/device/voicecard.vue @@ -13,6 +13,9 @@
{{ title }} + + 打印物模型 +
@@ -38,10 +41,15 @@
基础设置
- + + + + + + @change="handleVolumeChange" style="width: 80%" :disabled="!basicSettings.audioEnabled"> @@ -51,6 +59,9 @@
音频列表 + + 添加音频 +
@@ -58,9 +69,11 @@ - - - + +
@@ -68,7 +81,10 @@
- 默认列表 + 播放列表 + + 添加音频 +
@@ -76,15 +92,30 @@ - + - + + + + + + + + +
@@ -219,6 +250,101 @@ {{ $t('cancel') }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+ + + 周一 + 周二 + 周三 + 周四 + 周五 + 周六 + 周日 + + + + + + + +
+ +
+ + + - + + + km/h +
+
+
+
+
+ +
@@ -244,6 +370,8 @@ export default { this.$nextTick(function () { this.MonitorChart(); }); + console.log("物模型", JSON.stringify(this.deviceInfo.thingsModels)); + if (this.deviceInfo.thingsModels && this.deviceInfo.thingsModels.length > 0) { this.deviceInfo.thingsModels = this.device.thingsModels.sort((a, b) => b.order - a.order); } @@ -318,17 +446,39 @@ export default { }, // 声卡相关数据 basicSettings: { - volume: 50 + volume: 50, + audioEnabled: true }, audioList: [ - { id: 1, name: '音频1', duration: '00:30', size: '2.5MB' }, - { id: 2, name: '音频2', duration: '01:15', size: '3.8MB' }, - { id: 3, name: '音频3', duration: '00:45', size: '2.1MB' } + { id: 1, name: '音频1' }, + { id: 2, name: '音频2' }, + { id: 3, name: '音频3' } ], defaultList: [ - { id: 1, name: '默认音频1', type: '系统', status: '启用' }, - { id: 2, name: '默认音频2', type: '用户', status: '启用' }, - { id: 3, name: '默认音频3', type: '系统', status: '禁用' } + { + id: 1, + name: '默认音频1', + playTime: '08:00', + weekdays: '周一, 周三, 周五', + radarEnabled: true, + status: '启用' + }, + { + id: 2, + name: '默认音频2', + playTime: '12:30', + weekdays: '周二, 周四', + radarEnabled: false, + status: '启用' + }, + { + id: 3, + name: '默认音频3', + playTime: '18:00', + weekdays: '周六, 周日', + radarEnabled: true, + status: '禁用' + } ], // 录音相关数据 isRecording: false, @@ -340,6 +490,91 @@ export default { recordings: [], timer: null, audioUrl: null, + // 音频列表相关数据 + addAudioDialogVisible: false, + newAudio: { + remark: '', + per: 0, + spd: 5, + pit: 5, + vol: 5, + tex_utf8: '', + filename: '' + }, + audioRules: { + remark: [ + { required: true, message: '请输入备注', trigger: 'blur' } + ], + per: [ + { required: true, message: '请选择主持人声音', trigger: 'change' } + ], + spd: [ + { required: true, message: '请设置合成语速', trigger: 'change' } + ], + pit: [ + { required: true, message: '请设置合成音调', trigger: 'change' } + ], + vol: [ + { required: true, message: '请设置合成音量', trigger: 'change' } + ], + tex_utf8: [ + { required: true, message: '请输入合成文本', trigger: 'blur' } + ] + }, + // 播放列表相关数据 + addPlaylistDialogVisible: false, + newPlaylist: { + name: '', + type: '用户', + status: '启用', + audioId: '', + playTimeStart: null, + playTimeEnd: null, + weekdays: [], + radarEnabled: false, + radarSpeedMin: 0, + radarSpeedMax: 120 + }, + playlistRules: { + audioId: [ + { required: true, message: '请选择音频', trigger: 'change' } + ], + playTime: [ + { + validator: (rule, value, callback) => { + if (!this.newPlaylist.playTimeStart || !this.newPlaylist.playTimeEnd) { + callback(new Error('请选择播放时间段')); + } else if (this.newPlaylist.playTimeStart >= this.newPlaylist.playTimeEnd) { + callback(new Error('开始时间必须小于结束时间')); + } else { + callback(); + } + }, + trigger: 'change' + } + ], + weekdays: [ + { required: true, message: '请选择重复日期', trigger: 'change' } + ], + radarSpeed: [ + { + validator: (rule, value, callback) => { + if (this.newPlaylist.radarEnabled) { + if (!this.newPlaylist.radarSpeedMin || !this.newPlaylist.radarSpeedMax) { + callback(new Error('请设置速度范围')); + } else if (this.newPlaylist.radarSpeedMin >= this.newPlaylist.radarSpeedMax) { + callback(new Error('最小速度必须小于最大速度')); + } else { + callback(); + } + } else { + callback(); + } + }, + trigger: 'change' + } + ] + }, }; }, mounted() { @@ -351,6 +586,71 @@ export default { }, methods: { + //发送指令 + async mqttPublish(device, model) { + const command = {}; + command[model.id] = model.shadow; + const params = { + deviceId: device.deviceId, + modelId: model.modelId, + }; + const response = await getOrderControl(params); + if (response.code != 200) { + this.$message({ + type: 'warning', + message: response.msg, + }); + return; + } + const data = { + serialNumber: device.serialNumber, + productId: device.productId, + remoteCommand: command, + identifier: model.id, + modelName: model.name, + isShadow: device.status != 3, + type: model.type, + }; + //设备在线状态判断 + if (this.device.status !== 3 && this.device.isShadow !== 1) { + if (this.device.status === 1) { + title = this.$t('device.device-variable.930930-0'); + } else if (this.device.status === 2) { + title = this.$t('device.device-variable.930930-1'); + } else { + title = this.$t('device.device-variable.930930-2'); + } + this.$message({ + type: 'warning', + message: title, + }); + return; + } + if ((this.deviceInfo.protocolCode === 'MODBUS-TCP' || this.deviceInfo.protocolCode === 'MODBUS-RTU') && this.device.status === 3) { + await serviceInvokeReply(data).then((response) => { + if (response.code === 200) { + this.$message({ + type: 'success', + message: this.$t('device.running-status.866086-25'), + }); + } else { + this.$message.error(response.msg); + } + }); + } else { + await serviceInvoke(data).then((response) => { + if (response.code === 200) { + this.$message({ + type: 'success', + message: this.$t('device.running-status.866086-25'), + }); + } else { + this.$message.error(response.msg); + } + }); + } + }, + // 保留原有的设备状态相关方法 handleDeviceChange(device) { if (device && device.deviceId != 0) { @@ -367,6 +667,8 @@ export default { }); if (this.deviceInfo.thingsModels && this.deviceInfo.thingsModels.length > 0) { this.deviceInfo.thingsModels = this.deviceInfo.thingsModels.sort((a, b) => b.order - a.order); + this.updateBasicSettings(); // 更新基础设置 + this.printThingsModels(); } if (this.deviceInfo.chartList && this.deviceInfo.chartList.length > 0) { this.deviceInfo.chartList = this.deviceInfo.chartList.sort((a, b) => b.order - a.order); @@ -374,15 +676,67 @@ export default { } }, + // 更新基础设置 + updateBasicSettings() { + if (!this.deviceInfo.thingsModels) return; + + // 更新音频开关状态 + const playEnModel = this.deviceInfo.thingsModels.find(model => model.id === 'play_en'); + if (playEnModel) { + this.basicSettings.audioEnabled = playEnModel.shadow === '1'; + } + + // 更新音量设置 + const volumeModel = this.deviceInfo.thingsModels.find(model => model.id === 'volume'); + if (volumeModel) { + this.basicSettings.volume = parseInt(volumeModel.shadow) || 50; + } + + // 更新音频列表 + const mp3ListModel = this.deviceInfo.thingsModels.find(model => model.id === 'mp3_list'); + if (mp3ListModel && mp3ListModel.shadow) { + try { + // 解析 JSON 字符串 + const jsonStr = mp3ListModel.shadow.replace('JSON=', ''); + const data = JSON.parse(jsonStr); + + // 获取 mp3_list 数组 + if (data.sound_card && data.sound_card.mp3_list) { + // 更新音频列表 + this.audioList = data.sound_card.mp3_list.map((item, index) => { + // 从 "1_def" 格式中提取名称 + const name = item.split('_')[1] || item; + return { + id: index + 1, + name: name + }; + }); + } + } catch (error) { + console.error('解析音频列表失败:', error); + } + } + }, + + printThingsModels() { + console.log('当前物模型数据:', JSON.stringify(this.deviceInfo.thingsModels, null, 2)); + this.$message({ + message: '物模型数据已打印到控制台', + type: 'success' + }); + }, + // 声卡特有方法 formatVolume(val) { return val + '%'; }, handleVolumeChange(val) { - // 处理音量变化 - console.log('音量变化:', val); - // TODO: 调用相应的API更新设备音量 + const volumeModel = this.deviceInfo.thingsModels.find(model => model.id === 'volume'); + if (volumeModel) { + volumeModel.shadow = val.toString(); + this.mqttPublish(this.deviceInfo, volumeModel); + } }, // 保留其他必要的方法 @@ -392,6 +746,157 @@ export default { }); }, + // 处理设备上报的数据更新 + // updateParam(params) { + // console.log(1111111111) + // if (!params || !this.deviceInfo.thingsModels) return; + + // const { serialNumber, productId, data } = params; + // if (data && this.deviceInfo.serialNumber === serialNumber) { + // // 更新物模型数据 + // this.deviceInfo.thingsModels.forEach(model => { + // if (data[model.id] !== undefined) { + // model.shadow = data[model.id]; + // } + // }); + + // // 更新基础设置 + // this.updateBasicSettings(); + // } + // }, + + + //更新参数值 + updateParam(params) { + let { serialNumber, productId, data } = params; + let isComplete = false; + data = data.message; + if (data) { + for (let j = 0; j < data.length; j++) { + for (let k = 0; k < this.deviceInfo.thingsModels.length && !isComplete; k++) { + if (this.deviceInfo.thingsModels[k].id == data[j].id) { + const variable = this.deviceInfo.thingsModels[k]; + // 普通类型(小数/整数/字符串/布尔/枚举) + if (this.deviceInfo.thingsModels[k].datatype.type == 'decimal' || this.deviceInfo.thingsModels[k].datatype.type == 'integer') { + variable.shadow = Number(data[j].value); + } else { + variable.shadow = data[j].value; + } + } + if (this.deviceInfo.thingsModels[k].datatype.type == 'object') { + // 对象类型 + for (let n = 0; n < this.deviceInfo.thingsModels[k].datatype.params.length; n++) { + if (this.deviceInfo.thingsModels[k].datatype.params[n].id == data[j].id) { + this.deviceInfo.thingsModels[k].datatype.params[n].shadow = data[j].value; + isComplete = true; + break; + } + } + } else if (this.deviceInfo.thingsModels[k].datatype.type == 'array') { + // 数组类型 + if (this.deviceInfo.thingsModels[k].datatype.arrayType == 'object') { + // 1.对象类型数组,id为数组中一个元素,例如:array_01_gateway_temperature + if (String(data[j].id).indexOf('array_') == 0) { + for (let n = 0; n < this.deviceInfo.thingsModels[k].datatype.arrayParams.length; n++) { + for (let m = 0; m < this.deviceInfo.thingsModels[k].datatype.arrayParams[n].length; m++) { + if (this.deviceInfo.thingsModels[k].datatype.arrayParams[n][m].id == data[j].id) { + this.deviceInfo.thingsModels[k].datatype.arrayParams[n][m].shadow = data[j].value; + isComplete = true; + break; + } + } + if (isComplete) { + break; + } + } + } else { + // 2.对象类型数组,例如:gateway_temperature,消息ID添加前缀后匹配 + for (let n = 0; n < this.deviceInfo.thingsModels[k].datatype.arrayParams.length; n++) { + for (let m = 0; m < this.deviceInfo.thingsModels[k].datatype.arrayParams[n].length; m++) { + let index = n > 9 ? String(n) : '0' + k; + let prefix = 'array_' + index + '_'; + if (this.deviceInfo.thingsModels[k].datatype.arrayParams[n][m].id == prefix + data[j].id) { + this.deviceInfo.thingsModels[k].datatype.arrayParams[n][m].shadow = data[j].value; + } + } + } + } + } else { + // 整数、小数和字符串类型数组 + for (let n = 0; n < this.deviceInfo.thingsModels[k].datatype.arrayModel.length; n++) { + if (this.deviceInfo.thingsModels[k].datatype.arrayModel[n].id == data[j].id) { + this.deviceInfo.thingsModels[k].datatype.arrayModel[n].shadow = data[j].value; + break; + } + } + } + } + } + // 图表数据 + for (let k = 0; k < this.deviceInfo.chartList.length; k++) { + if (this.deviceInfo.chartList[k].id.indexOf('array_') == 0) { + // 数组类型匹配,例如:array_00_gateway_temperature + if (this.deviceInfo.chartList[k].id == data[j].id) { + this.deviceInfo.chartList[k].shadow = data[j].value; + // 更新图表 + for (let m = 0; m < this.monitorChart.length; m++) { + if (data[j].id == this.monitorChart[m].data.id) { + let data = [ + { + value: this.deviceInfo.chartList[k].shadow, + name: this.monitorChart[m].data.name, + }, + ]; + this.monitorChart[m].chart.setOption({ + series: [ + { + data: data, + }, + ], + }); + break; + } + } + } + } else { + // 普通类型匹配 + if (this.deviceInfo.chartList[k].id == data[j].id) { + this.deviceInfo.chartList[k].shadow = data[j].value; + // 更新图表 + for (let m = 0; m < this.monitorChart.length; m++) { + if (data[j].id == this.monitorChart[m].data.id) { + let data = [ + { + value: this.deviceInfo.chartList[k].shadow, + name: this.monitorChart[m].data.name, + }, + ]; + this.monitorChart[m].chart.setOption({ + series: [ + { + data: data, + }, + ], + }); + break; + } + } + } + } + if (isComplete) { + break; + } + } + } + } + this.updateBasicSettings(); + }, + + + + + + initDataStatus() { this.$busEvent.$on('updateStatus', (status) => { this.updateStatus(status); @@ -637,7 +1142,230 @@ export default { deleteRecording(index) { this.recordings.splice(index, 1); - } + }, + + handleAudioSwitchChange(val) { + const playEnModel = this.deviceInfo.thingsModels.find(model => model.id === 'play_en'); + if (playEnModel) { + playEnModel.shadow = val ? '1' : '0'; + this.mqttPublish(this.deviceInfo, playEnModel); + } + }, + + showAddAudioDialog() { + this.addAudioDialogVisible = true; + this.newAudio = { + remark: '', + per: 0, + spd: 5, + pit: 5, + vol: 5, + tex_utf8: '', + filename: '' + }; + }, + + // 获取未使用的最小ID + getNextAvailableId() { + const mp3ListModel = this.deviceInfo.thingsModels.find(model => model.id === 'mp3_list'); + if (mp3ListModel) { + try { + const jsonStr = mp3ListModel.shadow.replace('JSON=', ''); + const data = JSON.parse(jsonStr); + if (data.sound_card && data.sound_card.mp3_list) { + // 获取所有已使用的ID + const usedIds = data.sound_card.mp3_list.map(item => { + const id = parseInt(item.split('_')[0]); + return isNaN(id) ? 0 : id; + }); + + // 找到最小的未使用ID + let nextId = 1; + while (usedIds.includes(nextId)) { + nextId++; + } + return nextId; + } + } catch (error) { + console.error('解析mp3_list失败:', error); + } + } + return 1; // 如果出错,返回1 + }, + + submitAudioForm() { + this.$refs.audioForm.validate((valid) => { + if (valid) { + // 找到mp3_list物模型 + const mp3ListModel = this.deviceInfo.thingsModels.find(model => model.id === 'mp3_list'); + if (mp3ListModel) { + try { + // 获取下一个可用的ID + const nextId = this.getNextAvailableId(); + + // 生成文件名 + const filename = `${nextId}_${this.newAudio.remark}`; + + // 构建TTS对象 + const ttsData = { + JSON_id: 1, + sound_card: { + TTS: { + per: this.newAudio.per, + spd: this.newAudio.spd, + pit: this.newAudio.pit, + vol: this.newAudio.vol, + tex_utf8: this.newAudio.tex_utf8, + filename: filename + } + } + }; + + // 更新物模型shadow值 + mp3ListModel.shadow = 'JSON=' + JSON.stringify(ttsData); + + // 发送更新 + this.mqttPublish(this.deviceInfo, mp3ListModel); + + this.addAudioDialogVisible = false; + this.$message.success('添加成功'); + } catch (error) { + console.error('添加音频失败:', error); + this.$message.error('添加失败'); + } + } + } + }); + }, + + handleDeleteAudio(row) { + this.$confirm('确认删除该音频吗?', '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }).then(() => { + // 找到mp3_list物模型 + const mp3ListModel = this.deviceInfo.thingsModels.find(model => model.id === 'mp3_list'); + if (mp3ListModel) { + try { + // 解析当前JSON + const jsonStr = mp3ListModel.shadow.replace('JSON=', ''); + const data = JSON.parse(jsonStr); + + // 从音频名称中提取完整ID + const audioId = row.name; + + // 从mp3_list中移除对应音频 + if (data.sound_card && data.sound_card.mp3_list) { + data.sound_card.mp3_list = data.sound_card.mp3_list.filter(item => !item.endsWith(audioId)); + + // 更新物模型shadow值 + const newShadow = 'JSON=' + JSON.stringify(data); + mp3ListModel.shadow = newShadow; + + // 发送更新 + this.mqttPublish(this.deviceInfo, mp3ListModel).then(() => { + this.$message.success('删除成功'); + }).catch(error => { + console.error('发送删除命令失败:', error); + this.$message.error('删除失败'); + }); + } + } catch (error) { + console.error('解析或更新mp3_list失败:', error); + this.$message.error('删除失败'); + } + } + }).catch(() => { }); + }, + + showAddPlaylistDialog() { + this.addPlaylistDialogVisible = true; + this.newPlaylist = { + name: '', + type: '用户', + status: '启用', + audioId: '', + playTimeStart: null, + playTimeEnd: null, + weekdays: [], + radarEnabled: false, + radarSpeedMin: 0, + radarSpeedMax: 120 + }; + }, + + submitPlaylistForm() { + this.$refs.playlistForm.validate((valid) => { + if (valid) { + const selectedAudio = this.audioList.find(audio => audio.id === this.newPlaylist.audioId); + this.defaultList.push({ + id: this.defaultList.length + 1, + name: selectedAudio ? selectedAudio.name : '', + type: this.newPlaylist.type, + status: this.newPlaylist.status, + playTime: `${this.formatTime(this.newPlaylist.playTimeStart)} - ${this.formatTime(this.newPlaylist.playTimeEnd)}`, + weekdays: this.formatWeekdays(this.newPlaylist.weekdays), + radarEnabled: this.newPlaylist.radarEnabled, + radarSpeed: this.newPlaylist.radarEnabled ? + `${this.newPlaylist.radarSpeedMin}-${this.newPlaylist.radarSpeedMax}km/h` : '' + }); + this.addPlaylistDialogVisible = false; + this.$message.success('添加成功'); + } + }); + }, + + formatTime(time) { + if (!time) return ''; + const hours = time.getHours().toString().padStart(2, '0'); + const minutes = time.getMinutes().toString().padStart(2, '0'); + return `${hours}:${minutes}`; + }, + + formatWeekdays(weekdays) { + const weekMap = { + '0': '周日', + '1': '周一', + '2': '周二', + '3': '周三', + '4': '周四', + '5': '周五', + '6': '周六' + }; + return weekdays.map(day => weekMap[day]).join(', '); + }, + + handleStatusChange(row) { + // TODO: 调用相应的API更新播放列表状态 + console.log('状态变更:', row); + }, + + handleDeletePlaylist(row) { + this.$confirm('确认删除该音频吗?', '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }).then(() => { + const index = this.defaultList.findIndex(item => item.id === row.id); + if (index > -1) { + this.defaultList.splice(index, 1); + } + this.$message.success('删除成功'); + }).catch(() => { }); + }, + + formatSpeed(val) { + return val; + }, + + formatPitch(val) { + return val; + }, + + formatVolume(val) { + return val; + }, }, }; @@ -903,4 +1631,42 @@ export default { box-shadow: 0 0 0 0 rgba(245, 108, 108, 0); } } + +.radar-settings { + margin-top: -20px; + padding: 15px; + background-color: #f8f9fa; + border-radius: 4px; + + .speed-range { + display: flex; + align-items: center; + gap: 10px; + + .el-input-number { + width: 120px; + } + + .speed-separator { + color: #606266; + font-size: 16px; + } + + .speed-unit { + color: #606266; + margin-left: 5px; + } + } +} + +.time-range { + display: flex; + align-items: center; + gap: 10px; + + .time-separator { + color: #606266; + font-size: 14px; + } +} \ No newline at end of file