Compare commits

...

2 Commits

  1. 2
      src/api/module/transfer/index.ts
  2. 4
      src/stores/transferData.ts
  3. 2
      src/utils/zmqJsonWorker.ts
  4. 16
      src/views/firmwareUpload/index.vue
  5. 2
      src/views/layout/index.vue
  6. 2
      src/views/stationData/components/PointGroupTree.vue
  7. 44
      src/views/stationData/components/deviceDrawer.vue
  8. 15
      src/views/stationData/components/newDataChart.vue
  9. 2
      src/views/stationData/components/offTransferDlg.vue
  10. 2
      src/views/stationData/components/onLineTransferDlg.vue
  11. 2
      src/views/stationData/index.vue
  12. 52
      src/views/stationData/transferData.vue
  13. 2
      src/views/stationData/type.ts

2
src/api/module/transfer/index.ts

@ -22,7 +22,7 @@ export const getDeviceDetails = (params: IGetDeviceDataParams) =>
export interface ISite { export interface ISite {
id: string id: string
name: string name: string
export_time: string create_time: string
last_modify_time: string last_modify_time: string
export_root_path: string export_root_path: string
} }

4
src/stores/transferData.ts

@ -35,7 +35,6 @@ export const useTransferDataStore = defineStore('transfer', () => {
function getSubDevicesCb(msg: SubMsgData) { function getSubDevicesCb(msg: SubMsgData) {
const { feedback } = msg const { feedback } = msg
const sn = feedback[1] const sn = feedback[1]
const hasDevice = devicesMap.get(sn) const hasDevice = devicesMap.get(sn)
if (hasDevice) { if (hasDevice) {
hasDevice.lastUpdated = Date.now() hasDevice.lastUpdated = Date.now()
@ -45,7 +44,8 @@ export const useTransferDataStore = defineStore('transfer', () => {
clientIp: feedback[0], clientIp: feedback[0],
sn: sn, sn: sn,
site_id: feedback[2], site_id: feedback[2],
footprint: feedback[3] ?? '--', versions: feedback[3] ?? '--',
footprint: feedback[4] ?? '--',
lastUpdated: Date.now(), lastUpdated: Date.now(),
status: '在线', // 初始状态为在线 status: '在线', // 初始状态为在线
isChecked: false, isChecked: false,

2
src/utils/zmqJsonWorker.ts

@ -5,7 +5,7 @@ import { WorkerCMD, ZmqCMD, type PublishMsg, type PubMsgData, } from './zmq'
const HEARTBEAT_TOPIC = 'HEARTBEAT' const HEARTBEAT_TOPIC = 'HEARTBEAT'
const HEARTBEAT_INTERVAL = 3000 const HEARTBEAT_INTERVAL = 3000
const STATUS_CHECK_INTERVAL = 1000 const STATUS_CHECK_INTERVAL = 1000
let messageTimeout = 10000 let messageTimeout = 20000
let heartClient: ZmqClient | null, subClient: ZmqClient | null, pubClient: ZmqClient | null let heartClient: ZmqClient | null, subClient: ZmqClient | null, pubClient: ZmqClient | null
let subHost = '', pubHost = '' let subHost = '', pubHost = ''

16
src/views/firmwareUpload/index.vue

@ -1,6 +1,6 @@
<template> <template>
<div class="flex justify-center items-center size-full"> <div class="flex justify-center items-center size-full">
<EdfsWrap title="固件上传" style="width: 50%; height: 50%"> <EdfsWrap :title="title" style="width: 50%; height: 50%">
<el-upload v-model:fileList="fileList" v-loading="loading" element-loading-text="上传中..." drag action="" <el-upload v-model:fileList="fileList" v-loading="loading" element-loading-text="上传中..." drag action=""
accept=".tar.gz" :limit="1" :on-exceed="handleExceed" :auto-upload="false" ref="uploadRef" accept=".tar.gz" :limit="1" :on-exceed="handleExceed" :auto-upload="false" ref="uploadRef"
class="h-[calc(100%-30px)] w-full"> class="h-[calc(100%-30px)] w-full">
@ -19,7 +19,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { uploadFirmwareFile } from '@/api/module/firmware' import { getFirmwarePath, uploadFirmwareFile } from '@/api/module/firmware'
import { useMessage } from '@/composables/useMessage' import { useMessage } from '@/composables/useMessage'
import type { import type {
UploadInstance, UploadInstance,
@ -159,6 +159,18 @@ function onClone() {
} }
clearData() clearData()
} }
const firmwarePath = ref<string>()
const title = computed(() => {
return `固件上传(${firmwarePath.value ? `当前固件:${firmwarePath.value}` : '当前固件: 未上传'}`
})
onMounted(async () => {
const res = await getFirmwarePath()
if (res.code === 200 || res.code === 0) {
firmwarePath.value = res.data.path.split('/').at(-1)
}
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

2
src/views/layout/index.vue

@ -76,7 +76,7 @@ const { theme } = useTheme()
const menuList = computed<any[]>(() => { const menuList = computed<any[]>(() => {
let data = defaultRouter[0].children let data = defaultRouter[0].children
if (env.VITE_APP_ENV !== 'local') { if (env.VITE_APP_ENV !== 'local') {
data = data.filter(item => item.name === 'firmware-upload') data = data.filter(item => item.name != 'firmware-upload')
} }
return data return data
} }

2
src/views/stationData/components/PointGroupTree.vue

@ -6,7 +6,7 @@
highlight-current @node-click="handleNodeClick" :default-expanded-keys="checkDefault"> highlight-current @node-click="handleNodeClick" :default-expanded-keys="checkDefault">
<template #default="scope"> <template #default="scope">
<div class="item"> <div class="item">
<span class="label">{{ scope.data.name }}</span> <span class="label">{{ scope.data?.cnName ?? '--' }}</span>
</div> </div>
</template> </template>
</el-tree> </el-tree>

44
src/views/stationData/components/deviceDrawer.vue

@ -1,5 +1,6 @@
<template> <template>
<div class="fault-rule-drawer"> <div class="fault-rule-drawer">
<el-drawer v-model="isShowDrawer" :title="title" direction="rtl" size="90%" modal-class="model-dev-opn" <el-drawer v-model="isShowDrawer" :title="title" direction="rtl" size="90%" modal-class="model-dev-opn"
:before-close="handleBeforeClose" @opened="onDrawerOpened"> :before-close="handleBeforeClose" @opened="onDrawerOpened">
<main class="wh-full flex"> <main class="wh-full flex">
@ -9,10 +10,9 @@
:isTransfer="props.isTransfer" :siteInfo="props.siteInfo" ref="pointGroupTreeRef" /> :isTransfer="props.isTransfer" :siteInfo="props.siteInfo" ref="pointGroupTreeRef" />
</EdfsWrap> </EdfsWrap>
<div class="flex-1 p-4 h-full overflow-hidden"> <div class="flex-1 p-4 h-full overflow-hidden">
<el-button class="mb-4" type="primary" <el-button class="mb-4" type="primary" @click="loadChardData">查询数据</el-button>
@click="loadChardData">查询数据</el-button> <NewDataChart v-if="isShowChart && !loading" :chart-datas="chartDatas" :legends="legends"
<NewDataChart v-if="isShowChart" :chart-datas="chartDatas" :legends="legends" :axis-data="Array.from(axisData)" ref="chartRef" />
:axis-data="Array.from(axisData)" ref="chartRef" />
</div> </div>
</main> </main>
</el-drawer> </el-drawer>
@ -70,7 +70,11 @@ async function open(device: IOfflineDevice | IOnlineDevice) {
curDevice.value = device curDevice.value = device
await loadPointGroup() await loadPointGroup()
.then(async () => { .then(async () => {
props.isTransfer ? await loadDeviceDetails() : zmqImport(device as IOfflineDevice) if (env.VITE_APP_ENV === 'local') {
props.isTransfer ? await loadDeviceDetails() : zmqImport(device as IOfflineDevice)
} else {
await loadDeviceDetails()
}
}) })
.catch(() => { .catch(() => {
message.error('获取点位组数据失败') message.error('获取点位组数据失败')
@ -104,6 +108,7 @@ async function loadPoints() {
pointList.value = data.map((i: any) => ({ pointList.value = data.map((i: any) => ({
label: i.cnName, label: i.cnName,
addr: i.addr, addr: i.addr,
unit: i.unit || '',
})) }))
pointList.value.push({ pointList.value.push({
label: '时间', label: '时间',
@ -120,6 +125,9 @@ async function loadDeviceDetails() {
if (!fullscreenLoading.value) { if (!fullscreenLoading.value) {
openFullScreen() openFullScreen()
} }
chartDatas.clear()
axisData.clear()
legends.value = []
const poinsRes = await loadPoints() const poinsRes = await loadPoints()
if (!poinsRes || poinsRes.code !== 0) { if (!poinsRes || poinsRes.code !== 0) {
message.error('获取点位数据失败') message.error('获取点位数据失败')
@ -135,9 +143,12 @@ async function loadDeviceDetails() {
} }
const chartDatas = reactive(new Map<string, any[]>()) const chartDatas = reactive(new Map<string, any[]>())
const axisData = new Set<string>() const axisData = new Set<string>()
const legends = ref<{ addr: string; label: string }[]>([]) const legends = ref<{ addr: string; label: string, unit: string }[]>([])
async function loadChardData(){
if(!columsParams.value.filter(i => i !== 'ts').length){ const loading = ref(false)
async function loadChardData() {
if (!columsParams.value.filter(i => i !== 'ts').length) {
message.error('请选择点位') message.error('请选择点位')
return return
} }
@ -153,18 +164,24 @@ async function loadChardData(){
params.site_id = props.siteInfo!.name || '' params.site_id = props.siteInfo!.name || ''
params.device_id = curDevice.value?.sn || '' params.device_id = curDevice.value?.sn || ''
} }
loading.value = true
const res = await getDeviceDetails(params) const res = await getDeviceDetails(params)
if (res.code === 0) { if (res.code === 0) {
pointData.value = Array.isArray(res.data.results) ? res.data.results : [] pointData.value = Array.isArray(res.data.results) ? res.data.results : []
chartDatas.clear() chartDatas.clear()
axisData.clear() axisData.clear()
for (const addr of columsParams.value.filter(i => i !== 'ts')) {
const label = pointList.value.find(i => i.addr === addr)?.label || addr
const find = legends.value.find(i => i.addr === addr)
const unit = pointList.value.find(i => i.addr === addr)?.unit || ''
if (!find) {
legends.value.push({ addr, label, unit })
}
}
pointData.value.forEach((data: any[]) => { pointData.value.forEach((data: any[]) => {
const [ts, val, addr] = data const [ts, val, addr] = data
legends.value.push({
addr: addr,
label: checkPointList.value.find(i => i.addr === addr)!.label,
})
if (checkPointList.value.some(i => i.addr === addr)) { if (checkPointList.value.some(i => i.addr === addr)) {
const time = dayjs(Number(ts)).format('YYYY-MM-DD HH:mm:ss') const time = dayjs(Number(ts)).format('YYYY-MM-DD HH:mm:ss')
if (addr) { if (addr) {
@ -184,9 +201,11 @@ async function loadChardData(){
} else { } else {
message.error('获取设备数据失败') message.error('获取设备数据失败')
} }
loading.value = false
} }
function zmqImport(device: IOfflineDevice) { function zmqImport(device: IOfflineDevice) {
console.log('object')
if (!device.sn || !props.siteInfo!.name) { if (!device.sn || !props.siteInfo!.name) {
message.error('未找到站点或设备') message.error('未找到站点或设备')
return return
@ -221,6 +240,7 @@ function zmqTimeoutCb(msg: TimeoutMsg) {
if (device && action === 'import') { if (device && action === 'import') {
message.error(`设备${device.sn},查询信息超时,请稍后重试`) message.error(`设备${device.sn},查询信息超时,请稍后重试`)
pubIdWithDevice.delete(msg.timeoutId) pubIdWithDevice.delete(msg.timeoutId)
fullscreenLoading.value?.close()
} }
} }

15
src/views/stationData/components/newDataChart.vue

@ -1,6 +1,6 @@
<template> <template>
<div class="device-data-chart"> <div class="device-data-chart">
<v-chart class="chart" :option="chartOption" :autoresize="autoresize" :loading-options="loadingOpt" <v-chart class="chart" :option="chartOption" :autoresize="autoresize" :loading-options="loadingOpt"
:loading="loading" ref="chartRef" @legendselectchanged="changeLegend" /> :loading="loading" ref="chartRef" @legendselectchanged="changeLegend" />
</div> </div>
</template> </template>
@ -32,6 +32,7 @@ const ZOOM_BOTTOM = 10
type Legend = { type Legend = {
addr: string addr: string
label: string label: string
unit?: string
} }
const autoresize = { const autoresize = {
@ -78,6 +79,7 @@ const chartOption = computed<EChartsOption>(() => {
const lineData: SeriesOption = { const lineData: SeriesOption = {
name: legend.label, name: legend.label,
type: 'line', type: 'line',
id: legend.addr,
// symbol: "none", // symbol: "none",
connectNulls: true, connectNulls: true,
data: entry, data: entry,
@ -90,7 +92,6 @@ const chartOption = computed<EChartsOption>(() => {
tmpSeries.push(lineData) tmpSeries.push(lineData)
} }
const option: EChartsOption = { const option: EChartsOption = {
grid: { grid: {
left: 60, left: 60,
@ -102,6 +103,16 @@ const chartOption = computed<EChartsOption>(() => {
trigger: 'axis', trigger: 'axis',
confine: true, confine: true,
appendToBody: true, appendToBody: true,
formatter: function (params: any) {
var relVal = params[0].name
for (var i = 0, l = params.length; i < l; i++) {
relVal += `<div style="display: flex; justify-content: space-between; gap: 30px;">
<div>${params[i].marker}${params[i].seriesName}:</div> <div">${params[i].value[1]}${props.legends.find(item => item.addr === params[i].seriesId)?.unit || ''}</div>
</div>`
}
return relVal
}
}, },
legend: { legend: {
type: 'scroll', type: 'scroll',

2
src/views/stationData/components/offTransferDlg.vue

@ -1,5 +1,5 @@
<template> <template>
<EdfsDialog :title="'数据迁移'" :is-show="visible" width="40%" @on-close="close" @on-save="onSave"> <EdfsDialog :title="'数据迁移'" :is-show="visible" width="50%" @on-close="close" @on-save="onSave">
<div class="flex-col gap-10 w-80% m-x-30px"> <div class="flex-col gap-10 w-80% m-x-30px">
<el-row> <el-row>
<div class="label"> <div class="label">

2
src/views/stationData/components/onLineTransferDlg.vue

@ -2,7 +2,7 @@
<EdfsDialog <EdfsDialog
:title="isBatchTransfer ? '批量迁移' : '数据迁移'" :title="isBatchTransfer ? '批量迁移' : '数据迁移'"
:is-show="visible" :is-show="visible"
width="40%" width="50%"
@on-close="close" @on-close="close"
@on-save="onSave" @on-save="onSave"
> >

2
src/views/stationData/index.vue

@ -61,7 +61,7 @@
<div class="item"> <div class="item">
<div class="label">迁移时间:</div> <div class="label">迁移时间:</div>
<div class="value"> <div class="value">
{{ dayjs(item.export_time).format('YYYY-MM-DD HH:mm:ss') }} {{ item.create_time }}
</div> </div>
</div> </div>
</div> </div>

52
src/views/stationData/transferData.vue

@ -1,7 +1,7 @@
<template> <template>
<div class="flex-col gap-16 wh-full"> <div class="flex-col gap-16 wh-full">
<el-button type="primary" @click="onBack" class="w-150px"> <el-button type="primary" @click="onBack" class="w-150px">
<i class="i-line-md:arrow-left"></i>返回站点数据 <i class="i-line-md:arrow-left"></i>返回站点数据 {{ isonLineTransfer }}
</el-button> </el-button>
<EdfsWrap title="设备列表" class="flex-1" useScrollBar> <EdfsWrap title="设备列表" class="flex-1" useScrollBar>
<template #title-right> <template #title-right>
@ -87,13 +87,19 @@
</template> </template>
<div class="absolute l-0 t-0 w-full h-full z-10 bg-#FFF-90" <div class="absolute l-0 t-0 w-full h-full z-10 bg-#FFF-90"
v-if="['updating', 'pending', 'rejected', 'timeout'].includes(item.upFirmware)"> v-if="['updating', 'pending', 'rejected', 'timeout'].includes(item.upFirmware)">
<div class="i-material-symbols-light:close absolute-rt text-base text-gray-950 cursor-pointer" <div class="i-material-symbols-light:close absolute-rt text-base text-gray-950 cursor-pointer" v-if="
v-if="['timeout', 'rejected'].includes(item.upFirmware)" @click="upFirmwareSucceed(item.sn)"></div> ['timeout', 'rejected', 'sc'].includes(item.upFirmware) ||
(item.upFirmwareStatus?.step === 4 && item.upFirmwareStatus?.progress === 100)"
@click="upFirmwareSucceed(item.sn)">
</div>
<template v-if="item.upFirmware === 'updating'"> <template v-if="item.upFirmware === 'updating'">
<div class="device-item-body"> <div class="device-item-body">
<div class="info-item"> <div class="info-item">
<div>当前步骤:</div> <div>当前步骤:</div>
<div>{{ item.upFirmwareStatus?.step ?? '--' }}</div> <div>{{item.upFirmwareStatus?.step === 4 && item.upFirmwareStatus?.progress === 100 ? '安装完成' :
upgradeProgressStatusMap.find(r => r.status === item.upFirmwareStatus?.step)?.text ??
'--'}}
</div>
</div> </div>
<div class="info-item"> <div class="info-item">
<div>当前进度:</div> <div>当前进度:</div>
@ -180,7 +186,9 @@
<div class="text-16px font-500"> {{ !!onOffDeviceTransferStatus ? onOffDeviceTransferStatus === <div class="text-16px font-500"> {{ !!onOffDeviceTransferStatus ? onOffDeviceTransferStatus ===
'progress' 'progress'
? '数据导入中' : '数据导入完成' : '数据导入中' }}</div> ? '数据导入中' : `数据导入完成 (失败:${offLineTransferRes().error}个 超时:${offLineTransferRes().timeout}个) ` :
'数据导入中'
}}</div>
</el-progress> </el-progress>
</div> </div>
@ -188,8 +196,8 @@
<div class="h-490 border-radius-8px bg-[#F9FAFB] p-10 flex-col"> <div class="h-490 border-radius-8px bg-[#F9FAFB] p-10 flex-col">
<div class="text-16px font-500">迁移日志</div> <div class="text-16px font-500">迁移日志</div>
<el-scrollbar class="flex-1"> <el-scrollbar class="flex-1">
<div v-for="i in siteTransferLogList" :class="i.status === 'error' ? 'text-red-500' : ''" <div v-for="i in siteTransferLogList"
class="text-gray-600"> :class="['error', 'timeout'].includes(i.status) ? 'text-red-500' : ''" class="text-gray-600">
{{ i.device.sn }}{{ i.msg }} {{ i.device.sn }}{{ i.msg }}
</div> </div>
</el-scrollbar> </el-scrollbar>
@ -390,13 +398,14 @@ function zmqTimeoutCb(msg: TimeoutMsg) {
message.error(`迁移超时,请重新稍后尝试`) message.error(`迁移超时,请重新稍后尝试`)
exportPubDeviceMap.delete(msg.timeoutId) exportPubDeviceMap.delete(msg.timeoutId)
closeTransferMask() closeTransferMask()
transferStatus.value === 'failed'
} }
} }
const onlineDeviceMap: Record< const onlineDeviceMap: Record<
keyof Omit< keyof Omit<
IOnlineDevice, IOnlineDevice,
'lastUpdated' | 'sn' | 'isChecked' | 'upFirmware' | 'upFirmwareStatus' 'lastUpdated' | 'sn' | 'isChecked' | 'upFirmware' | 'upFirmwareStatus' | 'versions'
>, >,
string string
> = { > = {
@ -594,7 +603,7 @@ function firmwareUpTimeoutCb(msg: TimeoutMsg) {
function zmqUpgradeCb(msg: PubMsgData) { function zmqUpgradeCb(msg: PubMsgData) {
if (!isonLineTransfer.value) return if (!isonLineTransfer.value) return
const status = msg.result const status = msg.code
const deviceSn = msg.feedback[0] const deviceSn = msg.feedback[0]
const progressStatus = msg.feedback[1] as number const progressStatus = msg.feedback[1] as number
const progress = msg.feedback[2] || undefined const progress = msg.feedback[2] || undefined
@ -602,16 +611,16 @@ function zmqUpgradeCb(msg: PubMsgData) {
if (curentDevice && curentDevice.action === 'upgrade') { if (curentDevice && curentDevice.action === 'upgrade') {
const { device } = curentDevice const { device } = curentDevice
if (device) { if (device) {
if (status === 'progress') { if (status === ZmqMsgResultType.PROGRESS) {
upFirmwareStatus(deviceSn, msg.feedback) upFirmwareStatus(deviceSn, msg.feedback)
if (progressStatus === 4 && progress === 100) { if (progressStatus === 4 && progress === 100) {
upFirmwareSucceed(deviceSn) upFirmwareSucceed(deviceSn)
} }
} }
} }
if (status === 'success' || status === 'error') { if (status === ZmqMsgResultType.SUCCESS || status === ZmqMsgResultType.ERROR) {
upFirmwareStatus(deviceSn, msg.feedback) upFirmwareStatus(deviceSn, msg.feedback)
if (status === 'error') { if (status === ZmqMsgResultType.ERROR) {
upFirmwareStatusReject(deviceSn, msg.feedback) upFirmwareStatusReject(deviceSn, msg.feedback)
} }
upgradeSnList.value = upgradeSnList.value.filter(item => item !== deviceSn) upgradeSnList.value = upgradeSnList.value.filter(item => item !== deviceSn)
@ -658,6 +667,23 @@ const siteTransferLogList = ref<Array<{
status: 'success' | 'timeout' | 'error' status: 'success' | 'timeout' | 'error'
}>>([]) }>>([])
const offLineTransferRes = () => {
let timeoutNum = 0
let errorNum = 0
const findTimeout = siteTransferLogList.value.filter(i => i.status === 'timeout')
const uniqueTimeoutSn = new Set(findTimeout.map(i => i.device.sn))
timeoutNum = uniqueTimeoutSn.size
const findError = siteTransferLogList.value.filter(i => i.status === 'error')
errorNum = new Set(findError.map(i => i.device.sn)).size
return {
timeout: timeoutNum,
error: errorNum,
}
}
const onOffDeviceTransferStatus = ref<'progress' | 'success' | undefined>() const onOffDeviceTransferStatus = ref<'progress' | 'success' | undefined>()
function zmqImportCb(msg: PubMsgData) { function zmqImportCb(msg: PubMsgData) {
const { id, feedback, result, code } = msg const { id, feedback, result, code } = msg
@ -698,7 +724,7 @@ function zmqImportTimeoutCb(msg: TimeoutMsg) {
siteTransferLogList.value.push({ siteTransferLogList.value.push({
msg: `数据导入超时,请稍后重试`, msg: `数据导入超时,请稍后重试`,
device: offDevice, device: offDevice,
status: 'error' status: 'timeout'
}) })
importQueue.value.shift() importQueue.value.shift()
isImporting.value = false isImporting.value = false

2
src/views/stationData/type.ts

@ -6,6 +6,7 @@ export interface IOnlineDevice {
lastUpdated: number // 新增字段,记录最后更新时间 lastUpdated: number // 新增字段,记录最后更新时间
status?: string status?: string
isChecked?: boolean isChecked?: boolean
versions?: string
upFirmware?: 'updating' | 'pending' | 'fulfilled' | 'rejected' | 'timeout' upFirmware?: 'updating' | 'pending' | 'fulfilled' | 'rejected' | 'timeout'
upFirmwareStatus?: IUpFirmwareStatus upFirmwareStatus?: IUpFirmwareStatus
} }
@ -40,4 +41,5 @@ export interface IUpFirmwareStatus {
export interface IMyPoint { export interface IMyPoint {
addr: string addr: string
label: string label: string
unit?: string
} }
Loading…
Cancel
Save