|
@@ -0,0 +1,387 @@
|
|
|
+<template>
|
|
|
+ <ContentWrap>
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <!-- 左侧指令调试区域 -->
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-tabs v-model="activeTab" type="border-card">
|
|
|
+ <!-- 上行指令调试 -->
|
|
|
+ <el-tab-pane label="上行指令调试" name="up">
|
|
|
+ <el-tabs v-model="subTab" v-if="activeTab === 'up'">
|
|
|
+ <!-- 属性上报 -->
|
|
|
+ <el-tab-pane label="属性上报" name="property">
|
|
|
+ <ContentWrap>
|
|
|
+ <el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
|
|
+ <el-table-column align="center" label="功能名称" prop="name" />
|
|
|
+ <el-table-column align="center" label="标识符" prop="identifier" />
|
|
|
+ <el-table-column align="center" label="数据类型" prop="identifier">
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ dataTypeOptionsLabel(row.property?.dataType) ?? '-' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column align="left" label="数据定义" prop="identifier">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <!-- 属性 -->
|
|
|
+ <template v-if="row.type === ThingModelType.PROPERTY">
|
|
|
+ <!-- 非列表型:数值 -->
|
|
|
+ <div
|
|
|
+ v-if="
|
|
|
+ [
|
|
|
+ DataSpecsDataType.INT,
|
|
|
+ DataSpecsDataType.DOUBLE,
|
|
|
+ DataSpecsDataType.FLOAT
|
|
|
+ ].includes(row.property.dataType)
|
|
|
+ "
|
|
|
+ >
|
|
|
+ 取值范围:{{
|
|
|
+ `${row.property.dataSpecs.min}~${row.property.dataSpecs.max}`
|
|
|
+ }}
|
|
|
+ </div>
|
|
|
+ <!-- 非列表型:文本 -->
|
|
|
+ <div v-if="DataSpecsDataType.TEXT === row.property.dataType">
|
|
|
+ 数据长度:{{ row.property.dataSpecs.length }}
|
|
|
+ </div>
|
|
|
+ <!-- 列表型: 数组、结构、时间(特殊) -->
|
|
|
+ <div
|
|
|
+ v-if="
|
|
|
+ [
|
|
|
+ DataSpecsDataType.ARRAY,
|
|
|
+ DataSpecsDataType.STRUCT,
|
|
|
+ DataSpecsDataType.DATE
|
|
|
+ ].includes(row.property.dataType)
|
|
|
+ "
|
|
|
+ >
|
|
|
+ -
|
|
|
+ </div>
|
|
|
+ <!-- 列表型: 布尔值、枚举 -->
|
|
|
+ <div
|
|
|
+ v-if="
|
|
|
+ [DataSpecsDataType.BOOL, DataSpecsDataType.ENUM].includes(
|
|
|
+ row.property.dataType
|
|
|
+ )
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <div>
|
|
|
+ {{
|
|
|
+ DataSpecsDataType.BOOL === row.property.dataType
|
|
|
+ ? '布尔值'
|
|
|
+ : '枚举值'
|
|
|
+ }}:
|
|
|
+ </div>
|
|
|
+ <div v-for="item in row.property.dataSpecsList" :key="item.value">
|
|
|
+ {{ `${item.name}-${item.value}` }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <!-- 服务 -->
|
|
|
+ <div v-if="row.type === ThingModelType.SERVICE">
|
|
|
+ 调用方式:{{ getCallTypeByValue(row.service.callType) }}
|
|
|
+ </div>
|
|
|
+ <!-- 事件 -->
|
|
|
+ <div v-if="row.type === ThingModelType.EVENT">
|
|
|
+ 事件类型:{{ getEventTypeByValue(row.event.type) }}
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="值" align="center" width="80">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-input v-model="scope.row.simulateValue" class="!w-60px" />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <div class="mt-10px">
|
|
|
+ <el-button type="primary" @click="handlePropertyReport">发送</el-button>
|
|
|
+ </div>
|
|
|
+ </ContentWrap>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <!-- 事件上报 -->
|
|
|
+ <el-tab-pane label="事件上报" name="event">
|
|
|
+ <ContentWrap>
|
|
|
+ <!-- <el-table v-loading="loading" :data="eventList" :stripe="true">
|
|
|
+ <el-table-column label="功能名称" align="center" prop="name" />
|
|
|
+ <el-table-column label="标识符" align="center" prop="identifier" />
|
|
|
+ <el-table-column label="数据类型" align="center" prop="dataType" />
|
|
|
+ <el-table-column
|
|
|
+ label="数据定义"
|
|
|
+ align="center"
|
|
|
+ prop="specs"
|
|
|
+ :show-overflow-tooltip="true"
|
|
|
+ />
|
|
|
+ <el-table-column label="值" align="center" width="80">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-input v-model="scope.row.simulateValue" class="!w-60px" />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <div class="mt-10px">
|
|
|
+ <el-button type="primary" @click="handleEventReport">发送</el-button>
|
|
|
+ </div> -->
|
|
|
+ </ContentWrap>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <!-- 状态变更 -->
|
|
|
+ <el-tab-pane label="状态变更" name="status">
|
|
|
+ <ContentWrap>
|
|
|
+ <div class="flex gap-4">
|
|
|
+ <el-button type="primary" @click="handleDeviceState('online')"
|
|
|
+ >设备上线</el-button
|
|
|
+ >
|
|
|
+ <el-button type="primary" @click="handleDeviceState('offline')"
|
|
|
+ >设备下线</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </ContentWrap>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <!-- 下行指令调试 -->
|
|
|
+ <el-tab-pane label="下行指令调试" name="down">
|
|
|
+ <el-tabs v-model="subTab" v-if="activeTab === 'down'">
|
|
|
+ <!-- 属性调试 -->
|
|
|
+ <el-tab-pane label="属性调试" name="propertyDebug">
|
|
|
+ <ContentWrap>
|
|
|
+ <!-- <el-table v-loading="loading" :data="propertyList" :stripe="true">
|
|
|
+ <el-table-column label="功能名称" align="center" prop="name" />
|
|
|
+ <el-table-column label="标识符" align="center" prop="identifier" />
|
|
|
+ <el-table-column label="数据类型" align="center" prop="dataType" />
|
|
|
+ <el-table-column
|
|
|
+ label="数据定义"
|
|
|
+ align="center"
|
|
|
+ prop="specs"
|
|
|
+ :show-overflow-tooltip="true"
|
|
|
+ />
|
|
|
+ <el-table-column label="值" align="center" width="80">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-input v-model="scope.row.simulateValue" class="!w-60px" />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <div class="mt-10px">
|
|
|
+ <el-button type="primary" @click="handlePropertyGet">获取</el-button>
|
|
|
+ </div> -->
|
|
|
+ </ContentWrap>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <!-- 服务调用 -->
|
|
|
+ <el-tab-pane label="服务调用" name="service">
|
|
|
+ <ContentWrap>
|
|
|
+ <!-- 服务调用相关内容 -->
|
|
|
+ </ContentWrap>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <!-- 右侧设备日志区域 -->
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-tabs type="border-card">
|
|
|
+ <el-tab-pane label="设备日志">
|
|
|
+ <DeviceDetailsLog :deviceKey="device.deviceKey" />
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </ContentWrap>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ProductVO } from '@/api/iot/product/product'
|
|
|
+import { ThingModelApi, ThingModelData,SimulatorData } from '@/api/iot/thingmodel'
|
|
|
+import { DeviceApi, DeviceVO,SimulatorDataVO } from '@/api/iot/device/device'
|
|
|
+import DeviceDetailsLog from './DeviceDetailsLog.vue'
|
|
|
+import {
|
|
|
+ DataSpecsDataType,
|
|
|
+ getCallTypeByValue,
|
|
|
+ getDataTypeOptionsLabel,
|
|
|
+ getEventTypeByValue,
|
|
|
+ ThingModelType
|
|
|
+} from '@/views/iot/thingmodel/config'
|
|
|
+
|
|
|
+const message = useMessage() // 消息弹窗
|
|
|
+const loading = ref(false)
|
|
|
+const activeTab = ref('up')
|
|
|
+const subTab = ref('property')
|
|
|
+
|
|
|
+const queryParams = reactive({
|
|
|
+ type: undefined,
|
|
|
+ productId: -1
|
|
|
+})
|
|
|
+const dataTypeOptionsLabel = computed(() => (value: string) => getDataTypeOptionsLabel(value)) // 解析数据类型
|
|
|
+const props = defineProps<{ product: ProductVO; device: DeviceVO }>()
|
|
|
+const list = ref<SimulatorData[]>([]) // 物模型列表的数据
|
|
|
+
|
|
|
+/** 查询列表 */
|
|
|
+const getList = async () => {
|
|
|
+ loading.value = true
|
|
|
+ try {
|
|
|
+ queryParams.productId = props.product?.id || -1
|
|
|
+ const data = await ThingModelApi.getThingModelList(queryParams)
|
|
|
+ // 转换数据,添加 simulateValue 字段
|
|
|
+ list.value = data.map(item => ({
|
|
|
+ ...item,
|
|
|
+ simulateValue: ''
|
|
|
+ }))
|
|
|
+ } finally {
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// // 功能列表数据结构定义
|
|
|
+// interface TableItem {
|
|
|
+// name: string
|
|
|
+// identifier: string
|
|
|
+// value: string | number
|
|
|
+// }
|
|
|
+
|
|
|
+// // 添加计算属性来过滤物模型数据
|
|
|
+// const propertyList = computed(() => {
|
|
|
+// return list.value
|
|
|
+// .filter((item) => item.type === 'property')
|
|
|
+// .map((item) => ({
|
|
|
+// name: item.name,
|
|
|
+// identifier: item.identifier,
|
|
|
+// value: ''
|
|
|
+// }))
|
|
|
+// })
|
|
|
+
|
|
|
+// const eventList = computed(() => {
|
|
|
+// return list.value
|
|
|
+// .filter((item) => item.type === 'event')
|
|
|
+// .map((item) => ({
|
|
|
+// name: item.name,
|
|
|
+// identifier: item.identifier,
|
|
|
+// value: ''
|
|
|
+// }))
|
|
|
+// })
|
|
|
+
|
|
|
+// 监听标签页变化 todo:后续改成查询字典
|
|
|
+watch(
|
|
|
+ [activeTab, subTab],
|
|
|
+ ([newActiveTab, newSubTab]) => {
|
|
|
+ // 根据标签页设置查询类型
|
|
|
+ if (newActiveTab === 'up') {
|
|
|
+ switch (newSubTab) {
|
|
|
+ case 'property':
|
|
|
+ queryParams.type = 1
|
|
|
+ break
|
|
|
+ case 'event':
|
|
|
+ queryParams.type = 3
|
|
|
+ break
|
|
|
+ // case 'status':
|
|
|
+ // queryParams.type = 'status'
|
|
|
+ // break
|
|
|
+ }
|
|
|
+ } else if (newActiveTab === 'down') {
|
|
|
+ switch (newSubTab) {
|
|
|
+ case 'propertyDebug':
|
|
|
+ queryParams.type = 1
|
|
|
+ break
|
|
|
+ case 'service':
|
|
|
+ queryParams.type = 2
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ getList() // 切换标签时重新获取数据
|
|
|
+ },
|
|
|
+ { immediate: true }
|
|
|
+)
|
|
|
+
|
|
|
+// interface ReportData {
|
|
|
+// productKey: string
|
|
|
+// deviceKey: string
|
|
|
+// type: string
|
|
|
+// subType: string
|
|
|
+// reportTime: string
|
|
|
+// content: string // 改为 string 类型,存储 JSON 字符串
|
|
|
+// }
|
|
|
+
|
|
|
+// 处理属性上报 TODO:数据类型效验
|
|
|
+const handlePropertyReport = async () => {
|
|
|
+ const contentObj: Record<string, any> = {}
|
|
|
+ list.value.forEach((item) => {
|
|
|
+ // 只有当 simulateValue 有值时才添加到 content 中
|
|
|
+ if (item.simulateValue !== undefined && item.simulateValue !== '') {
|
|
|
+ contentObj[item.identifier] = item.simulateValue
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ const reportData: SimulatorDataVO = {
|
|
|
+ productKey: props.product.productKey,
|
|
|
+ deviceKey: props.device.deviceKey,
|
|
|
+ type: 'property',
|
|
|
+ subType: 'report',
|
|
|
+ reportTime: Date.now(), // 将 reportTime 变为数字类型的时间戳
|
|
|
+ content: JSON.stringify(contentObj) // 转换为 JSON 字符串
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ await DeviceApi.simulatorDevice(reportData)
|
|
|
+ message.success('属性上报成功')
|
|
|
+ } catch (error) {
|
|
|
+ message.error('属性上报失败')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// // 处理事件上报
|
|
|
+// const handleEventReport = async () => {
|
|
|
+// const contentObj: Record<string, any> = {}
|
|
|
+// list.value
|
|
|
+// .filter(item => item.type === 'event')
|
|
|
+// .forEach((item) => {
|
|
|
+// if (item.simulateValue !== undefined && item.simulateValue !== '') {
|
|
|
+// contentObj[item.identifier] = item.simulateValue
|
|
|
+// }
|
|
|
+// })
|
|
|
+
|
|
|
+// const reportData: ReportData = {
|
|
|
+// productKey: props.product.productKey,
|
|
|
+// deviceKey: props.device.deviceKey,
|
|
|
+// type: 'event',
|
|
|
+// subType: list.value.find(item => item.type === 'event')?.identifier || '',
|
|
|
+// reportTime: new Date().toISOString(),
|
|
|
+// content: JSON.stringify(contentObj) // 转换为 JSON 字符串
|
|
|
+// }
|
|
|
+
|
|
|
+// try {
|
|
|
+// // TODO: 调用API发送数据
|
|
|
+// console.log('上报数据:', reportData)
|
|
|
+// message.success('事件上报成功')
|
|
|
+// } catch (error) {
|
|
|
+// message.error('事件上报失败')
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// // 处理设备状态变更
|
|
|
+// const handleDeviceState = async (state: 'online' | 'offline') => {
|
|
|
+// const reportData: ReportData = {
|
|
|
+// productKey: props.product.productKey,
|
|
|
+// deviceKey: props.device.deviceKey,
|
|
|
+// type: 'status',
|
|
|
+// subType: state,
|
|
|
+// reportTime: new Date().toISOString(),
|
|
|
+// content: JSON.stringify({ status: state }) // 转换为 JSON 字符串
|
|
|
+// }
|
|
|
+
|
|
|
+// try {
|
|
|
+// // TODO: 调用API发送数据
|
|
|
+// console.log('状态变更数据:', reportData)
|
|
|
+// console.log('reportData.content111111111', reportData.content)
|
|
|
+// message.success(`设备${state === 'online' ? '上线' : '下线'}成功`)
|
|
|
+// } catch (error) {
|
|
|
+// message.error(`设备${state === 'online' ? '上线' : '下线'}失败`)
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// 处理属性获取
|
|
|
+const handlePropertyGet = async () => {
|
|
|
+ // TODO: 实现属性获取逻辑
|
|
|
+ message.success('属性获取成功')
|
|
|
+}
|
|
|
+
|
|
|
+// 初始化
|
|
|
+onMounted(() => {
|
|
|
+ getList()
|
|
|
+})
|
|
|
+</script>
|