
【原 因】:飞行数据传到服务器 【过 程】:订阅飞机状态时候 检测到解锁 就记录飞行数据 切换到加锁之后 将数据长传 数据包括 加解锁时间戳 使用电量 经纬高 飞行距离 【影 响】: # 类型 包含: # feat:新功能(feature) # fix:修补bug # docs:文档(documentation) # style: 格式(不影响代码运行的变动) # refactor:重构(即不是新增功能,也不是修改bug的代码变动) # test:增加测试 # chore:构建过程或辅助工具的变动
286 lines
9.7 KiB
Vue
286 lines
9.7 KiB
Vue
<template>
|
|
<el-container id="layoutBox">
|
|
<el-aside class="animation" :class="isCollapse ? 'menuW' : 'menuS'">
|
|
<Menubar />
|
|
</el-aside>
|
|
<el-container>
|
|
<el-header>
|
|
<Headbar />
|
|
</el-header>
|
|
<el-main>
|
|
<router-view :key="rouKey" />
|
|
</el-main>
|
|
<el-footer>
|
|
<BlogBox />
|
|
</el-footer>
|
|
</el-container>
|
|
</el-container>
|
|
</template>
|
|
|
|
<script>
|
|
import mqtt from '@/utils/mqtt'
|
|
import geodist from 'geodist'
|
|
import { saveFlyData } from '@/utils/api/table'
|
|
import Menubar from '@/views/layout/components/menubar'
|
|
import Headbar from '@/views/layout/components/headbar'
|
|
import BlogBox from '@/views/layout/components/BlogBox'
|
|
|
|
export default {
|
|
name: 'Layout',
|
|
data () {
|
|
return {
|
|
frequencySetTimeout: null // 对频信息保持
|
|
}
|
|
},
|
|
methods: {
|
|
// 加锁时 解构 并 上传飞行数据
|
|
async handleFlightDataUpload (plane) {
|
|
const flyData = { ...plane.planeState.flyDataSave }
|
|
const path = flyData.path || []
|
|
const distanceList = []
|
|
|
|
// 记录加锁时间与电量
|
|
flyData.endTime = Math.floor(Date.now() / 1000)
|
|
flyData.endBattery = plane.planeState.batteryRemaining
|
|
|
|
// 计算累计飞行距离
|
|
let totalDistance = 0
|
|
for (let i = 1; i < path.length; i++) {
|
|
const prev = path[i - 1]
|
|
const curr = path[i]
|
|
const dist = geodist(
|
|
{ lat: prev[1], lon: prev[0] },
|
|
{ lat: curr[1], lon: curr[0] },
|
|
{ exact: true, unit: 'meters' }
|
|
)
|
|
totalDistance += dist
|
|
distanceList.push(dist)
|
|
}
|
|
|
|
// 计算电量消耗
|
|
const batteryDrop = (flyData.startBattery || 0) - (flyData.endBattery || 0)
|
|
const totalCapacity = plane.planeState.battCapacity || 1
|
|
const powerUsed = Math.round((batteryDrop / 100) * totalCapacity)
|
|
|
|
// 构造上传数据
|
|
const uploadData = {
|
|
plane_id: plane.id,
|
|
start_time: flyData.startTime,
|
|
end_time: flyData.endTime,
|
|
gps_path: JSON.stringify(path),
|
|
distance: Math.round(totalDistance),
|
|
power_used: powerUsed
|
|
}
|
|
try {
|
|
await saveFlyData(uploadData)
|
|
} catch (err) {
|
|
console.error('上传飞行数据失败', err)
|
|
}
|
|
}
|
|
},
|
|
components: {
|
|
Menubar,
|
|
Headbar,
|
|
BlogBox
|
|
},
|
|
computed: {
|
|
rouKey () {
|
|
return this.$route.path
|
|
},
|
|
isCollapse () {
|
|
return this.$store.state.app.isCollapse
|
|
},
|
|
shop_id () {
|
|
return this.$store.state.user.shop_id
|
|
},
|
|
airList () {
|
|
return this.$store.state.airList
|
|
},
|
|
shopList () {
|
|
return this.$store.state.shopList
|
|
}
|
|
},
|
|
mounted () {
|
|
/* mqtt */
|
|
mqtt.mqttConf()// 连接mqtt
|
|
},
|
|
created () {
|
|
/* init */
|
|
this.$store.commit('app/setIsMobile')// 获取客户端平台类型
|
|
this.$store.dispatch('fetchAirList')// 获取飞机列表
|
|
this.$store.dispatch('fetchShopList')// 获取商铺列表
|
|
this.$store.dispatch('fetchAdminList')// 获取管理员列表
|
|
this.$store.dispatch('fetchSiteList')// 获取站点列表
|
|
this.$store.dispatch('fetchRouteList')// 获取航线列表
|
|
this.$store.dispatch('fetchNoflyData')// 获取禁飞区数据
|
|
this.$store.dispatch('fetchCategoryList')// 获取分类列表(小程序)
|
|
this.$store.dispatch('fetchSpuList')// 获取商品spu列表
|
|
this.$store.dispatch('fetchSkuList')// 获取商品sku列表
|
|
this.$store.dispatch('fetchPaidOrderList')// 获取订单列表
|
|
this.$store.dispatch('fetchMessageList')// 获取管理员公告列表 并弹出公告框
|
|
},
|
|
watch: {
|
|
/**
|
|
* @description: 异步拿到飞机列表之后 再进行一些初始化操作
|
|
* @param {*} res 飞机列表
|
|
*/
|
|
airList (res) {
|
|
// 订阅飞机信息
|
|
mqtt.doSubscribe('planeState/+', (mqttRes) => {
|
|
res.forEach(plane => {
|
|
if (plane.macadd && mqttRes.topic.indexOf(plane.macadd) > -1) {
|
|
try {
|
|
// 反序列化 mqtt信息
|
|
const jsonData = JSON.parse(mqttRes.msg.trim())
|
|
|
|
// 更新mqtt信息 选择性更新状态
|
|
for (const key in jsonData) {
|
|
if (key === 'heartBeat') {
|
|
console.log('接收到心跳信息', plane)
|
|
// 每次接收到心跳时,更新随机数以触发 watch 监听
|
|
plane.planeState.heartRandom = Math.random()
|
|
plane.planeState.heartBeat = jsonData.heartBeat
|
|
|
|
const oldMark = plane.planeState.flyDataMark
|
|
const newMark = (Number(jsonData.heartBeat) & 128) !== 0
|
|
|
|
// 解锁状态变更检测:用于控制飞行数据的采集与上传
|
|
if (!oldMark && newMark) {
|
|
// 从加锁切换为解锁:初始化飞行数据记录
|
|
plane.planeState.flyDataMark = true
|
|
plane.planeState.flyDataSave = {
|
|
startTime: Math.floor(Date.now() / 1000), // 解锁时间(秒)
|
|
startBattery: plane.planeState.batteryRemaining,
|
|
endTime: null,
|
|
endBattery: null,
|
|
path: []
|
|
}
|
|
} else if (oldMark && !newMark) {
|
|
plane.planeState.flyDataMark = false
|
|
|
|
this.handleFlightDataUpload(plane) // 上传飞行数据
|
|
|
|
// 上传完成后清空记录缓存
|
|
plane.planeState.flyDataSave = {
|
|
startTime: null, // 解锁时的时间戳(秒)
|
|
endTime: null, // 加锁时的时间戳(秒)
|
|
startBattery: null, // 解锁时的电池电量(百分比)
|
|
endBattery: null, // 加锁时的电池电量(百分比)
|
|
path: []
|
|
}
|
|
}
|
|
} else if (key === 'position') {
|
|
// 如果是飞机位置信息 则不是直接刷新状态 而是累计 到数组 以便于画出飞机路径
|
|
const position = JSON.parse(jsonData.position)
|
|
// 检查 lon, lat, 和 alt 是否不全为零或空值
|
|
if (position.lon !== 0 && position.lat !== 0 &&
|
|
position.lon !== null && position.lat !== null &&
|
|
position.lon !== '' && position.lat !== '') {
|
|
plane.planeState.position.push([position.lon / 10e6, position.lat / 10e6, Number(position.alt)])
|
|
}
|
|
if (plane.planeState.position.length > 1000) {
|
|
plane.planeState.position.shift() // 删除最早的经纬度
|
|
}
|
|
// 如果是解锁状态,记录轨迹用于上传
|
|
if (plane.planeState.flyDataMark && plane.planeState.flyDataSave?.path) {
|
|
plane.planeState.flyDataSave.path.push([
|
|
position.lon / 10e6,
|
|
position.lat / 10e6,
|
|
Number(position.alt)
|
|
])
|
|
}
|
|
} else if (key === 'homePosition') {
|
|
const homePosition = JSON.parse(jsonData.homePosition)// home点反序列化再赋值
|
|
homePosition.lon = homePosition.lon / 10e6
|
|
homePosition.lat = homePosition.lat / 10e6
|
|
homePosition.alt = Number(homePosition.alt)
|
|
plane.planeState[key] = homePosition
|
|
} else if (key === 'statusText') {
|
|
/* 飞控信息 插入日志 */
|
|
this.$store.dispatch('fetchLog', { content: jsonData[key], color: '#f57c00' })
|
|
} else {
|
|
plane.planeState[key] = jsonData[key] // 按订阅信息 刷新飞机状态
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error parsing JSON or processing message:', error)
|
|
}
|
|
}
|
|
})
|
|
})
|
|
// 订阅对频
|
|
mqtt.doSubscribe('crosFrequency/+', (res) => {
|
|
if (res.topic.indexOf('crosFrequency') > -1) {
|
|
this.$store.commit('setCrosFrequency', res.msg)// 设置对频信息
|
|
clearTimeout(this.frequencySetTimeout)// 接收到新的订阅信息 清除之前的倒计时
|
|
this.frequencySetTimeout = setTimeout(() => { // 倒计时10秒 随之清除缓存中对频信息
|
|
this.$store.commit('setCrosFrequency', null)
|
|
}, 10000)
|
|
}
|
|
})
|
|
// 订阅游客下单频道
|
|
mqtt.doSubscribe(`refreshQuestList/${this.shop_id}`, (res) => {
|
|
if (res.topic.indexOf(`refreshQuestList/${this.shop_id}`) > -1) {
|
|
this.$store.dispatch('fetchSiteList')// 刷新站点列表
|
|
this.$store.dispatch('fetchPaidOrderList')// 刷新订单列表 ps:待发货及待收货 并且不是退款成功状态
|
|
}
|
|
})
|
|
// 向所有飞机发送 让飞控刷新各种请求
|
|
res.forEach(item => {
|
|
mqtt.publishFun(`cmd/${item.macadd}`, '{"refreshRequest":1}')
|
|
})
|
|
}
|
|
},
|
|
destroyed () {
|
|
mqtt.mqttDestroy()// 断开mqtt
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
@import "@/styles/theme.scss";
|
|
|
|
#layoutBox {
|
|
height: 100%;
|
|
}
|
|
|
|
.el-aside {
|
|
background-color: #304156;
|
|
overflow: hidden !important;
|
|
}
|
|
|
|
.menuW {
|
|
width: 60px !important;
|
|
}
|
|
|
|
/* 竖屏模式样式 */
|
|
@media (orientation: portrait) {
|
|
.menuW {
|
|
width: 0px !important;
|
|
}
|
|
}
|
|
|
|
.menuS {
|
|
width: 210px !important;
|
|
}
|
|
|
|
.el-header {
|
|
padding: 0 !important;
|
|
height: 50px !important;
|
|
line-height: 50px !important;
|
|
overflow: hidden;
|
|
z-index: 99;
|
|
box-shadow: 0 0px 4px rgba(0, 21, 41, .08);
|
|
}
|
|
|
|
.el-main {
|
|
padding: 0 !important;
|
|
height: 100%;
|
|
background-color: $white-color;
|
|
}
|
|
|
|
.el-footer {
|
|
height: 24px !important;
|
|
}
|
|
</style>
|