feat:【IoT 物联网】调整设备模拟发送消息的接入
This commit is contained in:
@ -49,6 +49,7 @@ export interface DeviceHistoryDataVO {
|
||||
data: string // 数据
|
||||
}
|
||||
|
||||
// TODO @芋艿:调整到 constants
|
||||
// IoT 设备状态枚举
|
||||
export enum DeviceStateEnum {
|
||||
INACTIVE = 0, // 未激活
|
||||
@ -56,22 +57,6 @@ export enum DeviceStateEnum {
|
||||
OFFLINE = 2 // 离线
|
||||
}
|
||||
|
||||
// IoT 设备上行 Request VO
|
||||
export interface IotDeviceUpstreamReqVO {
|
||||
id: number // 设备编号
|
||||
type: string // 消息类型
|
||||
identifier: string // 标识符
|
||||
data: any // 请求参数
|
||||
}
|
||||
|
||||
// IoT 设备下行 Request VO
|
||||
export interface IotDeviceDownstreamReqVO {
|
||||
id: number // 设备编号
|
||||
type: string // 消息类型
|
||||
identifier: string // 标识符
|
||||
data: any // 请求参数
|
||||
}
|
||||
|
||||
// 设备认证参数 VO
|
||||
export interface IotDeviceAuthInfoVO {
|
||||
clientId: string // 客户端 ID
|
||||
@ -79,6 +64,13 @@ export interface IotDeviceAuthInfoVO {
|
||||
password: string // 密码
|
||||
}
|
||||
|
||||
// IoT 设备发送消息 Request VO
|
||||
export interface IotDeviceMessageSendReqVO {
|
||||
deviceId: number // 设备编号
|
||||
method: string // 请求方法
|
||||
params?: any // 请求参数
|
||||
}
|
||||
|
||||
// 设备 API
|
||||
export const DeviceApi = {
|
||||
// 查询设备分页
|
||||
@ -136,16 +128,6 @@ export const DeviceApi = {
|
||||
return await request.download({ url: `/iot/device/get-import-template` })
|
||||
},
|
||||
|
||||
// 设备上行
|
||||
upstreamDevice: async (data: IotDeviceUpstreamReqVO) => {
|
||||
return await request.post({ url: `/iot/device/upstream`, data })
|
||||
},
|
||||
|
||||
// 设备下行
|
||||
downstreamDevice: async (data: IotDeviceDownstreamReqVO) => {
|
||||
return await request.post({ url: `/iot/device/downstream`, data })
|
||||
},
|
||||
|
||||
// 获取设备属性最新数据
|
||||
getLatestDeviceProperties: async (params: any) => {
|
||||
return await request.get({ url: `/iot/device/property/latest`, params })
|
||||
@ -156,18 +138,13 @@ export const DeviceApi = {
|
||||
return await request.get({ url: `/iot/device/property/history-page`, params })
|
||||
},
|
||||
|
||||
// 查询设备日志分页
|
||||
getDeviceMessagePage: async (params: any) => {
|
||||
return await request.get({ url: `/iot/device/message/page`, params })
|
||||
},
|
||||
|
||||
// 获取设备 MQTT 连接参数
|
||||
// 获取设备认证信息
|
||||
getDeviceAuthInfo: async (id: number) => {
|
||||
return await request.get({ url: `/iot/device/get-auth-info`, params: { id } })
|
||||
},
|
||||
|
||||
// 根据 ProductKey 和 DeviceNames 获取设备列表
|
||||
// TODO @puhui999:getDeviceListByProductKeyAndNames 哈。项目的风格统一~
|
||||
// TODO @puhui999:有没可能搞成基于 id 的查询哈?
|
||||
getDevicesByProductKeyAndNames: async (productKey: string, deviceNames: string[]) => {
|
||||
return await request.get({
|
||||
url: `/iot/device/list-by-product-key-and-names`,
|
||||
@ -176,5 +153,15 @@ export const DeviceApi = {
|
||||
deviceNames: deviceNames.join(',')
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 查询设备消息分页
|
||||
getDeviceMessagePage: async (params: any) => {
|
||||
return await request.get({ url: `/iot/device/message/page`, params })
|
||||
},
|
||||
|
||||
// 发送设备消息
|
||||
sendDeviceMessage: async (params: IotDeviceMessageSendReqVO) => {
|
||||
return await request.post({ url: `/iot/device/message/send`, data: params })
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,11 +5,21 @@
|
||||
<el-form :model="queryParams" inline>
|
||||
<el-form-item>
|
||||
<el-select v-model="queryParams.method" placeholder="所有方法" class="!w-160px" clearable>
|
||||
<el-option v-for="item in methodOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
<el-option
|
||||
v-for="item in methodOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-select v-model="queryParams.upstream" placeholder="上行/下行" class="!w-160px" clearable>
|
||||
<el-select
|
||||
v-model="queryParams.upstream"
|
||||
placeholder="上行/下行"
|
||||
class="!w-160px"
|
||||
clearable
|
||||
>
|
||||
<el-option label="上行" value="true" />
|
||||
<el-option label="下行" value="false" />
|
||||
</el-select>
|
||||
@ -33,7 +43,6 @@
|
||||
|
||||
<!-- 消息列表 -->
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" class="whitespace-nowrap">
|
||||
<el-table-column label="请求编号" align="center" prop="requestId" width="300" />
|
||||
<el-table-column label="时间" align="center" prop="ts" width="180">
|
||||
<template #default="scope">
|
||||
{{ formatDate(scope.row.ts) }}
|
||||
@ -51,19 +60,25 @@
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.reply" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="请求编号" align="center" prop="requestId" width="300" />
|
||||
<el-table-column label="请求方法" align="center" prop="method" width="140">
|
||||
<template #default="scope">
|
||||
{{ methodOptions.find(item => item.value === scope.row.method)?.label }}
|
||||
{{ methodOptions.find((item) => item.value === scope.row.method)?.label }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="请求/响应数据" align="center" prop="params" :show-overflow-tooltip="true">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.reply">
|
||||
<el-table-column
|
||||
label="请求/响应数据"
|
||||
align="center"
|
||||
prop="params"
|
||||
:show-overflow-tooltip="true"
|
||||
>
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.reply">
|
||||
{{ `{"code":${scope.row.code},"msg":"${scope.row.msg}","data":${scope.row.data}\}` }}
|
||||
</span>
|
||||
<span v-else>{{ scope.row.params }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<span v-else>{{ scope.row.params }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
@ -106,7 +121,7 @@ let autoRefreshTimer: any = null // TODO @super:autoRefreshEnable,autoRefres
|
||||
|
||||
// 消息方法选项
|
||||
const methodOptions = computed(() => {
|
||||
return Object.values(IotDeviceMessageMethodEnum).map(item => ({
|
||||
return Object.values(IotDeviceMessageMethodEnum).map((item) => ({
|
||||
label: item.name,
|
||||
value: item.method
|
||||
}))
|
||||
@ -166,4 +181,20 @@ onMounted(() => {
|
||||
getMessageList()
|
||||
}
|
||||
})
|
||||
|
||||
/** 刷新消息列表 */
|
||||
const refresh = (delay = 0) => {
|
||||
if (delay > 0) {
|
||||
setTimeout(() => {
|
||||
handleQuery()
|
||||
}, delay)
|
||||
} else {
|
||||
handleQuery()
|
||||
}
|
||||
}
|
||||
|
||||
/** 暴露方法给父组件 */
|
||||
defineExpose({
|
||||
refresh
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
</el-table>
|
||||
<!-- TODO @super:发送按钮,可以放在右侧哈。因为我们的 simulateValue 就在最右侧 -->
|
||||
<div class="mt-10px">
|
||||
<el-button type="primary" @click="handlePropertyReport"> 发送</el-button>
|
||||
<el-button type="primary" @click="handlePropertyPost"> 发送</el-button>
|
||||
</div>
|
||||
</ContentWrap>
|
||||
</el-tab-pane>
|
||||
@ -50,7 +50,7 @@
|
||||
<!-- TODO @super:待实现 -->
|
||||
<el-tab-pane label="事件上报" name="event">
|
||||
<ContentWrap>
|
||||
<!-- TODO @super:因为事件是每个 event 去模拟,而不是类似属性的批量上传。所以,可以每一列后面有个“模拟”按钮。另外,“值”使用 textarea,高度 3 -->
|
||||
<!-- TODO @super:因为事件是每个 event 去模拟,而不是类似属性的批量上传。所以,可以每一列后面有个"模拟"按钮。另外,"值"使用 textarea,高度 3 -->
|
||||
<!-- <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" />
|
||||
@ -132,11 +132,9 @@
|
||||
|
||||
<!-- 右侧设备日志区域 -->
|
||||
<el-col :span="12">
|
||||
<el-tabs type="border-card">
|
||||
<el-tab-pane label="设备日志">
|
||||
<DeviceDetailsMessage :device-id="device.id" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<ContentWrap title="设备消息">
|
||||
<DeviceDetailsMessage ref="deviceMessageRef" :device-id="device.id" />
|
||||
</ContentWrap>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</ContentWrap>
|
||||
@ -149,6 +147,7 @@ import { DeviceApi, DeviceStateEnum, DeviceVO } from '@/api/iot/device/device'
|
||||
import DeviceDetailsMessage from './DeviceDetailsMessage.vue'
|
||||
import { getDataTypeOptionsLabel } from '@/views/iot/thingmodel/config'
|
||||
import { DataDefinition } from '@/views/iot/thingmodel/components'
|
||||
import { IotDeviceMessageMethodEnum } from '@/views/iot/utils/constants'
|
||||
|
||||
const props = defineProps<{
|
||||
product: ProductVO
|
||||
@ -158,10 +157,12 @@ const props = defineProps<{
|
||||
const message = useMessage() // 消息弹窗
|
||||
const activeTab = ref('up') // TODO @super:upstream 上行、downstream 下行
|
||||
const subTab = ref('property') // TODO @super:upstreamTab
|
||||
const deviceMessageRef = ref() // 设备消息组件引用
|
||||
const deviceMessageRefresnhDelay = 2000 // 延迟 N 秒,保证模拟上行的消息被处理
|
||||
|
||||
const loading = ref(false)
|
||||
const queryParams = reactive({
|
||||
type: undefined, // TODO @super:type 默认给个第一个 tab 对应的,避免下面 watch 爆红
|
||||
type: undefined as number | undefined, // TODO @super:type 默认给个第一个 tab 对应的,避免下面 watch 爆红
|
||||
productId: -1
|
||||
})
|
||||
const list = ref<SimulatorData[]>([]) // 物模型列表的数据 TODO @super:thingModelList
|
||||
@ -228,9 +229,6 @@ watch(
|
||||
case 'event':
|
||||
queryParams.type = 3
|
||||
break
|
||||
// case 'status':
|
||||
// queryParams.type = 'status'
|
||||
// break
|
||||
}
|
||||
} else if (newActiveTab === 'down') {
|
||||
switch (newSubTab) {
|
||||
@ -248,26 +246,26 @@ watch(
|
||||
)
|
||||
|
||||
/** 处理属性上报 */
|
||||
const handlePropertyReport = async () => {
|
||||
const handlePropertyPost = async () => {
|
||||
// TODO @super:数据类型效验
|
||||
const data: Record<string, object> = {}
|
||||
const data: Record<string, any> = {}
|
||||
list.value.forEach((item) => {
|
||||
// 只有当 simulateValue 有值时才添加到 content 中
|
||||
// TODO @super:直接 if (item.simulateValue) 就可以哈,js 这块还是比较灵活的
|
||||
if (item.simulateValue !== undefined && item.simulateValue !== '') {
|
||||
if (item.simulateValue !== undefined && item.simulateValue !== '' && item.identifier) {
|
||||
// TODO @super:这里有个红色的 idea 告警,觉得去除下
|
||||
data[item.identifier] = item.simulateValue
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
await DeviceApi.upstreamDevice({
|
||||
id: props.device.id,
|
||||
type: 'property',
|
||||
identifier: 'report',
|
||||
data: data
|
||||
await DeviceApi.sendDeviceMessage({
|
||||
deviceId: props.device.id,
|
||||
method: IotDeviceMessageMethodEnum.PROPERTY_POST.method,
|
||||
params: data
|
||||
})
|
||||
message.success('属性上报成功')
|
||||
deviceMessageRef.value.refresh(deviceMessageRefresnhDelay)
|
||||
} catch (error) {
|
||||
message.error('属性上报失败')
|
||||
}
|
||||
@ -305,13 +303,15 @@ const handlePropertyReport = async () => {
|
||||
/** 处理设备状态 */
|
||||
const handleDeviceState = async (state: number) => {
|
||||
try {
|
||||
await DeviceApi.upstreamDevice({
|
||||
id: props.device.id,
|
||||
type: 'state',
|
||||
identifier: 'report',
|
||||
data: state
|
||||
await DeviceApi.sendDeviceMessage({
|
||||
deviceId: props.device.id,
|
||||
method:
|
||||
state === DeviceStateEnum.ONLINE
|
||||
? IotDeviceMessageMethodEnum.STATE_ONLINE.method
|
||||
: IotDeviceMessageMethodEnum.STATE_OFFLINE.method
|
||||
})
|
||||
message.success(`设备${state === DeviceStateEnum.ONLINE ? '上线' : '下线'}成功`)
|
||||
deviceMessageRef.value.refresh(deviceMessageRefresnhDelay)
|
||||
} catch (error) {
|
||||
message.error(`设备${state === DeviceStateEnum.ONLINE ? '上线' : '下线'}失败`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user