Commit 72685f74 authored by 高晓帆's avatar 高晓帆

fix

parent aee8262a
{
"SERVICE_IP": "126.0.0.34:1234",
"MAP_CENTER": [39.74441007068,111.24544532751],
"version": "1.1.3"
}
\ No newline at end of file
{
"SERVICE_IP": "172.16.0.114:1234",
"MAP_CENTER": [39.74441007068,111.24544532751],
"version": "1.1.3"
}
\ No newline at end of file
{
"SERVICE_IP": "126.0.0.34:1234",
"MAP_CENTER": [39.74441007068,111.24544532751],
"version": "1.1.3"
}
\ No newline at end of file
{
"SERVICE_IP": "126.0.0.34:1234",
"MAP_CENTER": [39.74441007068,111.24544532751],
"version": "1.1.3"
}
\ No newline at end of file
{
"SERVICE_IP": "126.0.0.34:1234",
"MAP_CENTER": [39.74441007068,111.24544532751],
"version": "1.1.3"
}
\ No newline at end of file
{
"SERVICE_IP": "172.16.0.114:1234",
"MAP_CENTER": [39.74441007068,111.24544532751],
"version": "1.1.3"
}
\ No newline at end of file
{
"SERVICE_IP": "172.16.0.114:1234",
"MAP_CENTER": [39.74441007068,111.24544532751],
"version": "1.1.3"
}
\ No newline at end of file
import { useStateStore } from '@/store/StateStore'
import { useVehicleStore } from '@/store/VehicleStore.js'
import { useMapStore } from '@/store/MapStore.js'
import router from '../../router'
import { storeToRefs } from 'pinia'
import { Message } from 'view-ui-plus'
const pingIntervalSec = 5000 // 心跳链接时间
const NETWORK_COUNT = 3
const TRUCK_MONITOR_EXIT = 15 * 60
const TOPIC_CLASS = {
vehicle: ['/vehicle/positioninfo'],
map: ['/map/obstacles', '/map/lanenode', '/map/lane', '/map/runablearea', '/map/centerline', '/map/diggingworkarea', '/map/dumparea', '/map/barricade', '/map/stationarea', '/map/parkspot', '/map/staticobjs', '/map/electronicFence', 'mapVersionAck'],
path: ['/vehicle/currPathinfo', '/vehicle/nextPathinfo', '/singleTrack/morepoint', '/singleTrack/morePath', '/vehicle/basicinfo', '/vehicle/dynamicPathinfo'], // 车辆循迹路径信息,当前路径;车辆循迹路径信息,下一段路径;单机循迹预览多路径;单机循迹预览单路径; 车辆动态路权信息
hmi: [
'/vehicle/debuginfo', // 车辆调试信息
'/vehicle/statusinfo', // 车辆状态信息
'/vehicle/errorCode', // 车辆故障码信息
'/vehicle/pairinfo', // 车辆配对信息
'/info/commandInfo', // 车辆提示信息(左下角)
'/info/tipInfo', // 车辆弹框提示信息
'/vehicle/paraminfo', // 车辆全局参数信息
'/command/debugack', //
'/pattern/ack', // 车辆模式切换按钮-发送模式切换回复
'/task/retranTimer', // 车辆任务重传定时器消息
'/setting/singleTrackAck', // 设置单机循迹
'/product',
'/info/popup', // 弹框确认信息
'/setting/basicinfo', // 基本设置数据信息
'/setting/light', // 灯光
'/setting/safe', // 安全
'/setting/controlInfo', // 启动参数
'/setting/ack', // 设置信息结果返回
'/setting/terminal', // 终端对应关系列表
'/setting/terminalAck',
'/setting/ignoreObstaclesAck', // 忽略障碍物回复消息
],
mapHmi: [
'/setting/map', // 地图管理图层
'/setting/mapControlAck', // 地图设置回复
'/setting/roughnessFileAck', // 地图不平整度文件回复
],
login: [
'/login/ack', // 登录信息
'/btn/quitApp', // 退出
'/truckmonitor/start', // 车端登录成功,有连结进来 车端就会发这个消息
'/truckmonitor/exit', // 车端程序退出,不关闭websocket
],
heart: ['heartbeat']
}
const SETTING_TYPE = {
1: '基本信息',
2: '灯光信息',
3: '安全信息',
4: '启动控制信息',
5: '地图管理信息',
6: '单机循迹信息',
// 7: '终端打印信息'
}
export default class Socket {
constructor(root) {
this.ws = new WebSocket(`ws://${document.location.serviceIP}`)
this.stateStore = useStateStore()
this.vehicleStore = useVehicleStore()
this.mapStore = useMapStore()
this.heartTimer = null // 心跳定时器
this.serverTimer = null // 服务器超时定时器
this.sendFixHeartTimer = null // 定时发送心跳
this.timeoutTimer = null // 超时定时器
this.lockReconnect = false // 是否真正建立连接
this.reconnectTimer = null // 重连倒计时
this.isSendSync = false // 是否要重连成功之后,发送同步消息
this.heartCount = 0 // 重连之后,如果没有收到车端登录的消息,需要发送同步消息
this.root = root
}
getConnection() {
const self = this
return new Promise((resolve, reject) => {
self.ws = new WebSocket(`ws://${document.location.serviceIP}`)
self.ws.onopen = function () {
// 开启心跳
self.start()
self.sendFixHeart()
self.resetTimeout()
self.count = 0
resolve(self.ws)
}
self.ws.onerror = function (err) {
console.log(err)
self.isSendSync = true
self.reconnect()
}
self.ws.onclose = function () {
!self.isClosed && self.reconnect()
console.log('closed', new Date())
}
self.ws.onmessage = function (msg) {
self.dealData(msg.data)
// 收到服务器信息,心跳重置
self.reset()
}
})
}
start() {
this.heartTimer && clearTimeout(this.heartTimer)
this.serverTimer && clearTimeout(this.serverTimer)
this.heartTimer = setTimeout(() => {
this.send({
type: 'heartbeat',
msg: 'heart beat'
})
// 超时关闭
this.serverTimer = setTimeout(() => {
this.ws.close()
}, 15000)
}, pingIntervalSec)
}
// 15s定时发送心跳
sendFixHeart() {
this.sendFixHeartTimer && clearInterval(this.sendFixHeartTimer)
this.sendFixHeartTimer = setInterval(() => {
this.send({
type: 'heartbeat',
msg: 'heart beat'
})
}, 15000)
}
resetTimeout() {
const self = this
if (this.timeoutTimer) {
this.timeoutCount = 0
this.stateStore.dealNetworkTip({ type: false })
clearInterval(this.timeoutTimer)
}
this.timeoutTimer = setInterval(() => {
if (this.timeoutCount > NETWORK_COUNT) {
this.stateStore.dealNetworkTip({ type: true, count: this.timeoutCount })
}
this.timeoutCount++
// 超时一定时间,认为车端程序未启动,跳转登录页面
if (self.timeoutCount > TRUCK_MONITOR_EXIT) {
this.dealTruckMointor({
visible: true,
msg: '车端程序未运行',
type: 1
})
router.push({
name: 'login'
})
}
}, 1000)
}
reset() {
this.start()
}
reconnect() {
if (this.lockReconnect) return
const self = this
// this.resetTimeout()
console.log('reconnect-----')
if (this.count > 5) {
Message.error('当前网络不稳定')
this.count = 0
// return
}
this.count++
this.lockReconnect = true
this.reconnectTimer && clearTimeout(this.reconnectTimer)
this.reconnectTimer = setTimeout(() => {
this.getConnection().then(sock => {
self.root.sock = sock
})
this.lockReconnect = false
}, 4000)
}
send(msg) {
msg = JSON.stringify(msg)
console.log(`发送消息${msg}`, new Date())
this.ws && this.ws.send(msg)
}
sendLoginMsg() {
let loginForm = this.stateStore.loginForm || localStorage.getItem(`${location.host}loginForm`)
if (typeof loginForm === 'string') {
loginForm = JSON.parse(loginForm)
}
this.send({
type: '/login/req',
msg: loginForm
})
}
judgeCurrentPath() {
let currentPath = router.currentRoute.value.path
if (currentPath.includes('basic-message')) {
this.sendLoginMsg()
}
}
dealTruckMointor(msg) {
this.stateStore.setTruckMonitor(msg)
}
// 初始化indexDB里的地图图层数据
initMapLayer(layerName, version) {
let db = this.root.indexDB
db.getStoreData(layerName, version).then(res => {
if (res) {
this.mapStore.setData(layerName, res.msg)
} else {
let data = {
type: 'syncMapData',
msg: [layerName]
}
this.send(data)
}
}).catch(error => {
console.warn(error)
})
}
sendVehicleSyncMsg() {
let syncVehicleData = {
type: 'syncVehicleData',
msg: {}
}
this.send(syncVehicleData)
}
async dealMapVersion(msg) {
console.log('msg', msg)
let storeLayers = window.localStorage.getItem('layers')
let syncMapLayers = [], initLayers = []
if (storeLayers) {
let layers = JSON.parse(storeLayers)
for (let i = 0; i < msg.length; i++) {
let item = msg[i]
let { layerName, layerVersion } = item
let oldLayer = layers.find(item => item.layerName === layerName)
if (oldLayer) {
let { layerVersion: oldLayerVersion } = oldLayer
if (oldLayerVersion !== layerVersion) {
syncMapLayers.push(layerName)
} else {
initLayers.push({ layerName, layerVersion })
}
} else {
syncMapLayers.push(layerName)
}
}
} else {
syncMapLayers = msg.map(item => item.layerName)
}
if (syncMapLayers.length) {
await this.root.indexDB.initStore(syncMapLayers)
if (initLayers.length) {
await this.root.indexDB.compareTable(initLayers)
initLayers.forEach(({ layerName, layerVersion }) => {
this.initMapLayer(layerName, layerVersion)
})
}
let data = {
type: 'syncMapData',
msg: syncMapLayers
}
this.send(data)
} else {
if (initLayers.length) {
await this.root.indexDB.compareTable(initLayers)
initLayers.forEach(({ layerName, layerVersion }) => {
this.initMapLayer(layerName, layerVersion)
})
}
this.sendVehicleSyncMsg()
}
localStorage.setItem('layers', JSON.stringify(msg))
}
dealheartData() {
if (this.isSendSync && this.heartCount > 2) {
// 没有收到车端退出的消息时,收到3次心跳,就发送同步消息
if (this.heartCount > 2) {
this.judgeCurrentPath()
this.isSendSync = false
this.heartCount = 0
} else {
this.heartCount++
}
}
}
dealmapData(result, key) {
const self = this
const { type, msg, version } = result
let worker = this.root.worker.worker[key]
switch (type) {
case 'mapVersionAck':
this.dealMapVersion(msg)
break
default:
let strData = window.atob(msg);
worker.postMessage({ type, msg: strData, version })
break
}
worker.onmessage = ({ data }) => {
const { mapName, result, mapVersion } = data
self.mapStore.setData(mapName, result)
self.root.indexDB.storeData(mapName, result, mapVersion)
self.sendVehicleSyncMsg()
}
}
dealpathData(result, key) {
const { type, msg } = result
let [, , name] = type.split('/')
const worker = this.root.worker.worker[key]
switch (type) {
case '/vehicle/basicinfo':
const { avoidancePath, obstacle } = msg
avoidancePath && worker.postMessage({ name: 'avoidancePath', msg: avoidancePath })
obstacle && worker.postMessage({ name: 'obstacle', msg: obstacle })
this.vehicleStore.setBasicInfo(msg)
break
case '/vehicle/nextPathinfo':
let { available, path } = msg
this.mapStore.setData('nextPathAvailable', available)
path && worker.postMessage({ name, msg: path })
break
default:
msg && worker.postMessage({ name, msg })
break
}
worker.onmessage = ({ data }) => {
const { name, dealedData } = data
this.mapStore.setData(name === 'obstacle' ? 'obstacleData' : name, dealedData)
}
}
dealvehicleData(result, key) {
const { type, msg } = result
const vehicleStoreToRefs = storeToRefs(this.vehicleStore)
const { paraminfo, positioninfo } = vehicleStoreToRefs
const { deviceId } = paraminfo.value
const worker = this.root.worker.worker[key]
const prePositionInfo = JSON.parse(JSON.stringify(positioninfo.value))
worker.postMessage({ type, msg, deviceId, pre: prePositionInfo })
const diffWorker = this.root.worker.worker['vehicleDiff']
diffWorker.postMessage({ pre: prePositionInfo, cur: msg })
worker.onmessage = ({ data }) => {
const { vehicles, safeObj, trajObj, gps, currentBasic } = data
this.stateStore.setData('gps', gps)
currentBasic && this.vehicleStore.setData('currentBasic', currentBasic)
this.vehicleStore.$patch({
vehiclesPositioninfo: vehicles,
safeObj: safeObj,
trajObj: trajObj
})
}
diffWorker.onmessage = ({ data }) => {
this.vehicleStore.setData('deleteVehicles', data)
}
this.vehicleStore.setData('positioninfo', msg)
}
handleSettingAck(msg) {
let { type, code } = msg
if (!SETTING_TYPE[type]) return
let ackType = code === 0 ? 'success' : 'error'
let text = SETTING_TYPE[type];
if (type == 4) {
text = '指令'
}
Message[ackType](`${text}设置${code === 0 ? '成功' : '失败'}`)
if (type == 6 && code === 0) {
let data = {
type: '/setting/req',
msg: {
type: 6
}
}
this.send(data)
}
}
dealObstaclesAck(msg) {
let { code } = msg
let type = code === 0 ? 'success' : 'error'
let tip = code === 0 ? '成功' : '失败'
Message[type](`发送车端${tip}`)
}
dealhmiData(result) {
const { type, msg } = result
let [, , name] = type.split('/')
// console.log(name, msg)
switch (type) {
// 车辆状态信息
case '/vehicle/statusinfo':
const { pattern, connStatus } = msg
this.stateStore.setData('wifi', connStatus)
this.vehicleStore.setData('pattern', pattern)
this.vehicleStore.setData(name, msg)
break
// 车辆故障码信息
case '/vehicle/errorCode':
this.vehicleStore.dealErrorCode(msg)
break
// 车辆模式切换按钮-发送模式切换回复
case '/pattern/ack':
this.vehicleStore.setData('patternAck', msg)
break
// 弹框确认信息
case '/info/popup':
this.vehicleStore.dealInfoPop(msg)
break
// 设置信息结果返回
case '/setting/ack':
this.handleSettingAck(msg)
break
case '/setting/terminalAck':
this.vehicleStore.dealTerminalAck(msg)
break
case '/setting/ignoreObstaclesAck':
this.dealObstaclesAck(msg)
break
default:
this.vehicleStore.setData(name, msg)
break
}
}
dealmapHmiData(result) {
const { type, msg } = result
let [, , name] = type.split('/')
switch (type) {
// 地图不平整度文件回复
case '/setting/roughnessFileAck':
const { type, fileName } = msg
if (type === 1) {
this.mapStore.setData(name, fileName)
}
break
default:
this.mapStore.setData(name, msg)
break
}
}
startTime() {
const self = this
const worker = this.root.worker.worker.time
worker.postMessage('start')
worker.onmessage = ({ data }) => {
self.stateStore.setData('currentTime', data)
}
}
postParentMessage(data) {
if (data) {
let loginInfo = data
if (window.top && loginInfo && data.code === 0) {
window.top.postMessage({
type: 'token',
data: {
ip: window.location.host,
loginInfo,
}
}, '*')
}
try {
localStorage.setItem(window.location.host, JSON.stringify(loginInfo))
} catch (error) {
this.isTraceless = true
console.log(error)
Message.error('请退出无痕模式!')
}
}
}
dealloginData(result) {
const { type, msg } = result
switch (type) {
// 车端登录成功,有连结进来 车端就会发这个消息
case '/truckmonitor/start':
// 收到车端登录成功的消息,则不需要判断收到心跳的次数
// 需要判断是否再登录页面,如果再登录页面,不需要处理,如果不在登录页面,则需要发送同步信息
this.isSendSync = false
this.judgeCurrentPath()
this.dealTruckMointor({
visible: false,
type: 1
})
break
case '/truckmonitor/exit':
router.push({
name: 'login'
})
this.dealTruckMointor({
visible: true,
msg: '车端程序未运行',
type: 1
})
this.stateStore.setData('loginAck', msg)
break
// 登录
case '/login/ack':
console.log('收到登录信息', new Date())
this.stateStore.setData('loginAck', msg)
this.postParentMessage(msg)
// this.startTime()
break
// 退出
case '/btn/quitApp':
if (window.state === 'logout') {
window.state = null
localStorage.removeItem(window.location.host)
this.close()
router.push({
name: 'login'
})
}
}
}
async dealData(data) {
this.resetTimeout()
let result = await (new Response(data)).text()
if (typeof result === 'string' && result !== 'heart beat') {
try {
result = JSON.parse(result)
} catch (err) {
console.log('err----', result)
}
}
const { type, msg } = result
// console.log(`接收消息${type}`, new Date().format('yyyy-MM-dd hh:mm:ss'))
if (!type) return
// console.log(type)
let keys = Object.keys(TOPIC_CLASS)
for (let i = 0; i < keys.length; i++) {
let key = keys[i]
if (TOPIC_CLASS[key].includes(type)) {
this[`deal${key}Data`](result, key)
break
}
}
}
close() {
this.isClosed = true
this.heartTimer && clearTimeout(this.heartTimer)
this.serverTimer && clearTimeout(this.serverTimer)
this.reconnectTimer && clearTimeout(this.reconnectTimer)
this.sendFixHeartTimer && clearInterval(this.sendFixHeartTimer)
this.timeoutTimer && clearInterval(this.timeoutTimer)
this.heartTimer = null
this.serverTimer = null
this.reconnectTimer = null
this.sendFixHeartTimer = null
this.ws.close()
this.root.sock = null
this.root.worker = null
this.root.indexDB = null
}
}
\ No newline at end of file
import { useStateStore } from '@/store/StateStore'
import { useVehicleStore } from '@/store/VehicleStore.js'
import { useMapStore } from '@/store/MapStore.js'
import router from '../../router'
import { storeToRefs } from 'pinia'
import { Message } from 'view-ui-plus'
const pingIntervalSec = 5000 // 心跳链接时间
const NETWORK_COUNT = 3
const TRUCK_MONITOR_EXIT = 15 * 60
const TOPIC_CLASS = {
vehicle: ['/vehicle/positioninfo'],
map: ['/map/obstacles', '/map/lanenode', '/map/lane', '/map/runablearea', '/map/centerline', '/map/diggingworkarea', '/map/dumparea', '/map/barricade', '/map/stationarea', '/map/parkspot', '/map/staticobjs', '/map/electronicFence', 'mapVersionAck'],
path: ['/vehicle/currPathinfo', '/vehicle/nextPathinfo', '/singleTrack/morepoint', '/singleTrack/morePath', '/vehicle/basicinfo', '/vehicle/dynamicPathinfo'], // 车辆循迹路径信息,当前路径;车辆循迹路径信息,下一段路径;单机循迹预览多路径;单机循迹预览单路径; 车辆动态路权信息
hmi: [
'/vehicle/debuginfo', // 车辆调试信息
'/vehicle/statusinfo', // 车辆状态信息
'/vehicle/errorCode', // 车辆故障码信息
'/vehicle/pairinfo', // 车辆配对信息
'/info/commandInfo', // 车辆提示信息(左下角)
'/info/tipInfo', // 车辆弹框提示信息
'/vehicle/paraminfo', // 车辆全局参数信息
'/command/debugack', //
'/pattern/ack', // 车辆模式切换按钮-发送模式切换回复
'/task/retranTimer', // 车辆任务重传定时器消息
'/setting/singleTrackAck', // 设置单机循迹
'/product',
'/info/popup', // 弹框确认信息
'/setting/basicinfo', // 基本设置数据信息
'/setting/light', // 灯光
'/setting/safe', // 安全
'/setting/controlInfo', // 启动参数
'/setting/ack', // 设置信息结果返回
'/setting/terminal', // 终端对应关系列表
'/setting/terminalAck',
'/setting/ignoreObstaclesAck', // 忽略障碍物回复消息
],
mapHmi: [
'/setting/map', // 地图管理图层
'/setting/mapControlAck', // 地图设置回复
'/setting/roughnessFileAck', // 地图不平整度文件回复
],
login: [
'/login/ack', // 登录信息
'/btn/quitApp', // 退出
'/truckmonitor/start', // 车端登录成功,有连结进来 车端就会发这个消息
'/truckmonitor/exit', // 车端程序退出,不关闭websocket
],
heart: ['heartbeat']
}
const SETTING_TYPE = {
1: '基本信息',
2: '灯光信息',
3: '安全信息',
4: '启动控制信息',
5: '地图管理信息',
6: '单机循迹信息',
// 7: '终端打印信息'
}
export default class Socket {
constructor(root) {
this.ws = new WebSocket(`wss://${document.location.serviceIP}`)
this.stateStore = useStateStore()
this.vehicleStore = useVehicleStore()
this.mapStore = useMapStore()
this.heartTimer = null // 心跳定时器
this.serverTimer = null // 服务器超时定时器
this.sendFixHeartTimer = null // 定时发送心跳
this.timeoutTimer = null // 超时定时器
this.lockReconnect = false // 是否真正建立连接
this.reconnectTimer = null // 重连倒计时
this.isSendSync = false // 是否要重连成功之后,发送同步消息
this.heartCount = 0 // 重连之后,如果没有收到车端登录的消息,需要发送同步消息
this.root = root
}
getConnection() {
const self = this
return new Promise((resolve, reject) => {
self.ws = new WebSocket(`ws://${document.location.serviceIP}`)
self.ws.onopen = function () {
// 开启心跳
self.start()
self.sendFixHeart()
self.resetTimeout()
self.count = 0
resolve(self.ws)
}
self.ws.onerror = function (err) {
console.log(err)
self.isSendSync = true
self.reconnect()
}
self.ws.onclose = function () {
!self.isClosed && self.reconnect()
console.log('closed', new Date())
}
self.ws.onmessage = function (msg) {
self.dealData(msg.data)
// 收到服务器信息,心跳重置
self.reset()
}
})
}
start() {
this.heartTimer && clearTimeout(this.heartTimer)
this.serverTimer && clearTimeout(this.serverTimer)
this.heartTimer = setTimeout(() => {
this.send({
type: 'heartbeat',
msg: 'heart beat'
})
// 超时关闭
this.serverTimer = setTimeout(() => {
this.ws.close()
}, 15000)
}, pingIntervalSec)
}
// 15s定时发送心跳
sendFixHeart() {
this.sendFixHeartTimer && clearInterval(this.sendFixHeartTimer)
this.sendFixHeartTimer = setInterval(() => {
this.send({
type: 'heartbeat',
msg: 'heart beat'
})
}, 15000)
}
resetTimeout() {
const self = this
if (this.timeoutTimer) {
this.timeoutCount = 0
this.stateStore.dealNetworkTip({ type: false })
clearInterval(this.timeoutTimer)
}
this.timeoutTimer = setInterval(() => {
if (this.timeoutCount > NETWORK_COUNT) {
this.stateStore.dealNetworkTip({ type: true, count: this.timeoutCount })
}
this.timeoutCount++
// 超时一定时间,认为车端程序未启动,跳转登录页面
if (self.timeoutCount > TRUCK_MONITOR_EXIT) {
this.dealTruckMointor({
visible: true,
msg: '车端程序未运行',
type: 1
})
router.push({
name: 'login'
})
}
}, 1000)
}
reset() {
this.start()
}
reconnect() {
if (this.lockReconnect) return
const self = this
// this.resetTimeout()
console.log('reconnect-----')
if (this.count > 5) {
Message.error('当前网络不稳定')
this.count = 0
// return
}
this.count++
this.lockReconnect = true
this.reconnectTimer && clearTimeout(this.reconnectTimer)
this.reconnectTimer = setTimeout(() => {
this.getConnection().then(sock => {
self.root.sock = sock
})
this.lockReconnect = false
}, 4000)
}
send(msg) {
msg = JSON.stringify(msg)
console.log(`发送消息${msg}`, new Date())
this.ws && this.ws.send(msg)
}
sendLoginMsg() {
let loginForm = this.stateStore.loginForm || localStorage.getItem(`${location.host}loginForm`)
if (typeof loginForm === 'string') {
loginForm = JSON.parse(loginForm)
}
this.send({
type: '/login/req',
msg: loginForm
})
}
judgeCurrentPath() {
let currentPath = router.currentRoute.value.path
if (currentPath.includes('basic-message')) {
this.sendLoginMsg()
}
}
dealTruckMointor(msg) {
this.stateStore.setTruckMonitor(msg)
}
// 初始化indexDB里的地图图层数据
initMapLayer(layerName, version) {
let db = this.root.indexDB
db.getStoreData(layerName, version).then(res => {
if (res) {
this.mapStore.setData(layerName, res.msg)
} else {
let data = {
type: 'syncMapData',
msg: [layerName]
}
this.send(data)
}
}).catch(error => {
console.warn(error)
})
}
sendVehicleSyncMsg() {
let syncVehicleData = {
type: 'syncVehicleData',
msg: {}
}
this.send(syncVehicleData)
}
async dealMapVersion(msg) {
console.log('msg', msg)
let storeLayers = window.localStorage.getItem('layers')
let syncMapLayers = [], initLayers = []
if (storeLayers) {
let layers = JSON.parse(storeLayers)
for (let i = 0; i < msg.length; i++) {
let item = msg[i]
let { layerName, layerVersion } = item
let oldLayer = layers.find(item => item.layerName === layerName)
if (oldLayer) {
let { layerVersion: oldLayerVersion } = oldLayer
if (oldLayerVersion !== layerVersion) {
syncMapLayers.push(layerName)
} else {
initLayers.push({ layerName, layerVersion })
}
} else {
syncMapLayers.push(layerName)
}
}
} else {
syncMapLayers = msg.map(item => item.layerName)
}
if (syncMapLayers.length) {
await this.root.indexDB.initStore(syncMapLayers)
if (initLayers.length) {
await this.root.indexDB.compareTable(initLayers)
initLayers.forEach(({ layerName, layerVersion }) => {
this.initMapLayer(layerName, layerVersion)
})
}
let data = {
type: 'syncMapData',
msg: syncMapLayers
}
this.send(data)
} else {
if (initLayers.length) {
await this.root.indexDB.compareTable(initLayers)
initLayers.forEach(({ layerName, layerVersion }) => {
this.initMapLayer(layerName, layerVersion)
})
}
this.sendVehicleSyncMsg()
}
localStorage.setItem('layers', JSON.stringify(msg))
}
dealheartData() {
if (this.isSendSync && this.heartCount > 2) {
// 没有收到车端退出的消息时,收到3次心跳,就发送同步消息
if (this.heartCount > 2) {
this.judgeCurrentPath()
this.isSendSync = false
this.heartCount = 0
} else {
this.heartCount++
}
}
}
dealmapData(result, key) {
const self = this
const { type, msg, version } = result
let worker = this.root.worker.worker[key]
switch (type) {
case 'mapVersionAck':
this.dealMapVersion(msg)
break
default:
let strData = window.atob(msg);
worker.postMessage({ type, msg: strData, version })
break
}
worker.onmessage = ({ data }) => {
const { mapName, result, mapVersion } = data
self.mapStore.setData(mapName, result)
self.root.indexDB.storeData(mapName, result, mapVersion)
self.sendVehicleSyncMsg()
}
}
dealpathData(result, key) {
const { type, msg } = result
let [, , name] = type.split('/')
const worker = this.root.worker.worker[key]
switch (type) {
case '/vehicle/basicinfo':
const { avoidancePath, obstacle } = msg
avoidancePath && worker.postMessage({ name: 'avoidancePath', msg: avoidancePath })
obstacle && worker.postMessage({ name: 'obstacle', msg: obstacle })
this.vehicleStore.setBasicInfo(msg)
break
case '/vehicle/nextPathinfo':
let { available, path } = msg
this.mapStore.setData('nextPathAvailable', available)
path && worker.postMessage({ name, msg: path })
break
default:
msg && worker.postMessage({ name, msg })
break
}
worker.onmessage = ({ data }) => {
const { name, dealedData } = data
this.mapStore.setData(name === 'obstacle' ? 'obstacleData' : name, dealedData)
}
}
dealvehicleData(result, key) {
const { type, msg } = result
const vehicleStoreToRefs = storeToRefs(this.vehicleStore)
const { paraminfo, positioninfo } = vehicleStoreToRefs
const { deviceId } = paraminfo.value
const worker = this.root.worker.worker[key]
const prePositionInfo = JSON.parse(JSON.stringify(positioninfo.value))
worker.postMessage({ type, msg, deviceId, pre: prePositionInfo })
const diffWorker = this.root.worker.worker['vehicleDiff']
diffWorker.postMessage({ pre: prePositionInfo, cur: msg })
worker.onmessage = ({ data }) => {
const { vehicles, safeObj, trajObj, gps, currentBasic } = data
this.stateStore.setData('gps', gps)
currentBasic && this.vehicleStore.setData('currentBasic', currentBasic)
this.vehicleStore.$patch({
vehiclesPositioninfo: vehicles,
safeObj: safeObj,
trajObj: trajObj
})
}
diffWorker.onmessage = ({ data }) => {
this.vehicleStore.setData('deleteVehicles', data)
}
this.vehicleStore.setData('positioninfo', msg)
}
handleSettingAck(msg) {
let { type, code } = msg
if (!SETTING_TYPE[type]) return
let ackType = code === 0 ? 'success' : 'error'
let text = SETTING_TYPE[type];
if (type == 4) {
text = '指令'
}
Message[ackType](`${text}设置${code === 0 ? '成功' : '失败'}`)
if (type == 6 && code === 0) {
let data = {
type: '/setting/req',
msg: {
type: 6
}
}
this.send(data)
}
}
dealObstaclesAck(msg) {
let { code } = msg
let type = code === 0 ? 'success' : 'error'
let tip = code === 0 ? '成功' : '失败'
Message[type](`发送车端${tip}`)
}
dealhmiData(result) {
const { type, msg } = result
let [, , name] = type.split('/')
// console.log(name, msg)
switch (type) {
// 车辆状态信息
case '/vehicle/statusinfo':
const { pattern, connStatus } = msg
this.stateStore.setData('wifi', connStatus)
this.vehicleStore.setData('pattern', pattern)
this.vehicleStore.setData(name, msg)
break
// 车辆故障码信息
case '/vehicle/errorCode':
this.vehicleStore.dealErrorCode(msg)
break
// 车辆模式切换按钮-发送模式切换回复
case '/pattern/ack':
this.vehicleStore.setData('patternAck', msg)
break
// 弹框确认信息
case '/info/popup':
this.vehicleStore.dealInfoPop(msg)
break
// 设置信息结果返回
case '/setting/ack':
this.handleSettingAck(msg)
break
case '/setting/terminalAck':
this.vehicleStore.dealTerminalAck(msg)
break
case '/setting/ignoreObstaclesAck':
this.dealObstaclesAck(msg)
break
default:
this.vehicleStore.setData(name, msg)
break
}
}
dealmapHmiData(result) {
const { type, msg } = result
let [, , name] = type.split('/')
switch (type) {
// 地图不平整度文件回复
case '/setting/roughnessFileAck':
const { type, fileName } = msg
if (type === 1) {
this.mapStore.setData(name, fileName)
}
break
default:
this.mapStore.setData(name, msg)
break
}
}
startTime() {
const self = this
const worker = this.root.worker.worker.time
worker.postMessage('start')
worker.onmessage = ({ data }) => {
self.stateStore.setData('currentTime', data)
}
}
postParentMessage(data) {
if (data) {
let loginInfo = data
if (window.top && loginInfo && data.code === 0) {
window.top.postMessage({
type: 'token',
data: {
ip: window.location.host,
loginInfo,
}
}, '*')
}
try {
localStorage.setItem(window.location.host, JSON.stringify(loginInfo))
} catch (error) {
this.isTraceless = true
console.log(error)
Message.error('请退出无痕模式!')
}
}
}
dealloginData(result) {
const { type, msg } = result
switch (type) {
// 车端登录成功,有连结进来 车端就会发这个消息
case '/truckmonitor/start':
// 收到车端登录成功的消息,则不需要判断收到心跳的次数
// 需要判断是否再登录页面,如果再登录页面,不需要处理,如果不在登录页面,则需要发送同步信息
this.isSendSync = false
this.judgeCurrentPath()
this.dealTruckMointor({
visible: false,
type: 1
})
break
case '/truckmonitor/exit':
router.push({
name: 'login'
})
this.dealTruckMointor({
visible: true,
msg: '车端程序未运行',
type: 1
})
this.stateStore.setData('loginAck', msg)
break
// 登录
case '/login/ack':
console.log('收到登录信息', new Date())
this.stateStore.setData('loginAck', msg)
this.postParentMessage(msg)
// this.startTime()
break
// 退出
case '/btn/quitApp':
if (window.state === 'logout') {
window.state = null
localStorage.removeItem(window.location.host)
this.close()
router.push({
name: 'login'
})
}
}
}
async dealData(data) {
this.resetTimeout()
let result = await (new Response(data)).text()
if (typeof result === 'string' && result !== 'heart beat') {
try {
result = JSON.parse(result)
} catch (err) {
console.log('err----', result)
}
}
const { type, msg } = result
// console.log(`接收消息${type}`, new Date().format('yyyy-MM-dd hh:mm:ss'))
if (!type) return
// console.log(type)
let keys = Object.keys(TOPIC_CLASS)
for (let i = 0; i < keys.length; i++) {
let key = keys[i]
if (TOPIC_CLASS[key].includes(type)) {
this[`deal${key}Data`](result, key)
break
}
}
}
close() {
this.isClosed = true
this.heartTimer && clearTimeout(this.heartTimer)
this.serverTimer && clearTimeout(this.serverTimer)
this.reconnectTimer && clearTimeout(this.reconnectTimer)
this.sendFixHeartTimer && clearInterval(this.sendFixHeartTimer)
this.timeoutTimer && clearInterval(this.timeoutTimer)
this.heartTimer = null
this.serverTimer = null
this.reconnectTimer = null
this.sendFixHeartTimer = null
this.ws.close()
this.root.sock = null
this.root.worker = null
this.root.indexDB = null
}
}
\ No newline at end of file
import { useStateStore } from '@/store/StateStore'
import { useVehicleStore } from '@/store/VehicleStore.js'
import { useMapStore } from '@/store/MapStore.js'
import router from '../../router'
import { storeToRefs } from 'pinia'
import { Message } from 'view-ui-plus'
const pingIntervalSec = 5000 // 心跳链接时间
const NETWORK_COUNT = 3
const TRUCK_MONITOR_EXIT = 15 * 60
const TOPIC_CLASS = {
vehicle: ['/vehicle/positioninfo'],
map: ['/map/obstacles', '/map/lanenode', '/map/lane', '/map/runablearea', '/map/centerline', '/map/diggingworkarea', '/map/dumparea', '/map/barricade', '/map/stationarea', '/map/parkspot', '/map/staticobjs', '/map/electronicFence', 'mapVersionAck'],
path: ['/vehicle/currPathinfo', '/vehicle/nextPathinfo', '/singleTrack/morepoint', '/singleTrack/morePath', '/vehicle/basicinfo', '/vehicle/dynamicPathinfo'], // 车辆循迹路径信息,当前路径;车辆循迹路径信息,下一段路径;单机循迹预览多路径;单机循迹预览单路径; 车辆动态路权信息
hmi: [
'/vehicle/debuginfo', // 车辆调试信息
'/vehicle/statusinfo', // 车辆状态信息
'/vehicle/errorCode', // 车辆故障码信息
'/vehicle/pairinfo', // 车辆配对信息
'/info/commandInfo', // 车辆提示信息(左下角)
'/info/tipInfo', // 车辆弹框提示信息
'/vehicle/paraminfo', // 车辆全局参数信息
'/command/debugack', //
'/pattern/ack', // 车辆模式切换按钮-发送模式切换回复
'/task/retranTimer', // 车辆任务重传定时器消息
'/setting/singleTrackAck', // 设置单机循迹
'/product',
'/info/popup', // 弹框确认信息
'/setting/basicinfo', // 基本设置数据信息
'/setting/light', // 灯光
'/setting/safe', // 安全
'/setting/controlInfo', // 启动参数
'/setting/ack', // 设置信息结果返回
'/setting/terminal', // 终端对应关系列表
'/setting/terminalAck',
'/setting/ignoreObstaclesAck', // 忽略障碍物回复消息
],
mapHmi: [
'/setting/map', // 地图管理图层
'/setting/mapControlAck', // 地图设置回复
'/setting/roughnessFileAck', // 地图不平整度文件回复
],
login: [
'/login/ack', // 登录信息
'/btn/quitApp', // 退出
'/truckmonitor/start', // 车端登录成功,有连结进来 车端就会发这个消息
'/truckmonitor/exit', // 车端程序退出,不关闭websocket
],
heart: ['heartbeat']
}
const SETTING_TYPE = {
1: '基本信息',
2: '灯光信息',
3: '安全信息',
4: '启动控制信息',
5: '地图管理信息',
6: '单机循迹信息',
// 7: '终端打印信息'
}
export default class Socket {
constructor(root) {
this.ws = new WebSocket(`wss://${document.location.serviceIP}`)
this.stateStore = useStateStore()
this.vehicleStore = useVehicleStore()
this.mapStore = useMapStore()
this.heartTimer = null // 心跳定时器
this.serverTimer = null // 服务器超时定时器
this.sendFixHeartTimer = null // 定时发送心跳
this.timeoutTimer = null // 超时定时器
this.lockReconnect = false // 是否真正建立连接
this.reconnectTimer = null // 重连倒计时
this.isSendSync = false // 是否要重连成功之后,发送同步消息
this.heartCount = 0 // 重连之后,如果没有收到车端登录的消息,需要发送同步消息
this.root = root
}
getConnection() {
const self = this
return new Promise((resolve, reject) => {
self.ws = new WebSocket(`ws://${document.location.serviceIP}`)
self.ws.onopen = function () {
// 开启心跳
self.start()
self.sendFixHeart()
self.resetTimeout()
self.count = 0
resolve(self.ws)
}
self.ws.onerror = function (err) {
console.log(err)
self.isSendSync = true
self.reconnect()
}
self.ws.onclose = function () {
!self.isClosed && self.reconnect()
console.log('closed', new Date())
}
self.ws.onmessage = function (msg) {
self.dealData(msg.data)
// 收到服务器信息,心跳重置
self.reset()
}
})
}
start() {
this.heartTimer && clearTimeout(this.heartTimer)
this.serverTimer && clearTimeout(this.serverTimer)
this.heartTimer = setTimeout(() => {
this.send({
type: 'heartbeat',
msg: 'heart beat'
})
// 超时关闭
this.serverTimer = setTimeout(() => {
this.ws.close()
}, 15000)
}, pingIntervalSec)
}
// 15s定时发送心跳
sendFixHeart() {
this.sendFixHeartTimer && clearInterval(this.sendFixHeartTimer)
this.sendFixHeartTimer = setInterval(() => {
this.send({
type: 'heartbeat',
msg: 'heart beat'
})
}, 15000)
}
resetTimeout() {
const self = this
if (this.timeoutTimer) {
this.timeoutCount = 0
this.stateStore.dealNetworkTip({ type: false })
clearInterval(this.timeoutTimer)
}
this.timeoutTimer = setInterval(() => {
if (this.timeoutCount > NETWORK_COUNT) {
this.stateStore.dealNetworkTip({ type: true, count: this.timeoutCount })
}
this.timeoutCount++
// 超时一定时间,认为车端程序未启动,跳转登录页面
if (self.timeoutCount > TRUCK_MONITOR_EXIT) {
this.dealTruckMointor({
visible: true,
msg: '车端程序未运行',
type: 1
})
router.push({
name: 'login'
})
}
}, 1000)
}
reset() {
this.start()
}
reconnect() {
if (this.lockReconnect) return
const self = this
// this.resetTimeout()
console.log('reconnect-----')
if (this.count > 5) {
Message.error('当前网络不稳定')
this.count = 0
// return
}
this.count++
this.lockReconnect = true
this.reconnectTimer && clearTimeout(this.reconnectTimer)
this.reconnectTimer = setTimeout(() => {
this.getConnection().then(sock => {
self.root.sock = sock
})
this.lockReconnect = false
}, 4000)
}
send(msg) {
msg = JSON.stringify(msg)
console.log(`发送消息${msg}`, new Date())
this.ws && this.ws.send(msg)
}
sendLoginMsg() {
let loginForm = this.stateStore.loginForm || localStorage.getItem(`${location.host}loginForm`)
if (typeof loginForm === 'string') {
loginForm = JSON.parse(loginForm)
}
this.send({
type: '/login/req',
msg: loginForm
})
}
judgeCurrentPath() {
let currentPath = router.currentRoute.value.path
if (currentPath.includes('basic-message')) {
this.sendLoginMsg()
}
}
dealTruckMointor(msg) {
this.stateStore.setTruckMonitor(msg)
}
// 初始化indexDB里的地图图层数据
initMapLayer(layerName, version) {
let db = this.root.indexDB
db.getStoreData(layerName, version).then(res => {
if (res) {
this.mapStore.setData(layerName, res.msg)
} else {
let data = {
type: 'syncMapData',
msg: [layerName]
}
this.send(data)
}
}).catch(error => {
console.warn(error)
})
}
sendVehicleSyncMsg() {
let syncVehicleData = {
type: 'syncVehicleData',
msg: {}
}
this.send(syncVehicleData)
}
async dealMapVersion(msg) {
console.log('msg', msg)
let storeLayers = window.localStorage.getItem('layers')
let syncMapLayers = [], initLayers = []
if (storeLayers) {
let layers = JSON.parse(storeLayers)
for (let i = 0; i < msg.length; i++) {
let item = msg[i]
let { layerName, layerVersion } = item
let oldLayer = layers.find(item => item.layerName === layerName)
if (oldLayer) {
let { layerVersion: oldLayerVersion } = oldLayer
if (oldLayerVersion !== layerVersion) {
syncMapLayers.push(layerName)
} else {
initLayers.push({ layerName, layerVersion })
}
} else {
syncMapLayers.push(layerName)
}
}
} else {
syncMapLayers = msg.map(item => item.layerName)
}
if (syncMapLayers.length) {
await this.root.indexDB.initStore(syncMapLayers)
if (initLayers.length) {
await this.root.indexDB.compareTable(initLayers)
initLayers.forEach(({ layerName, layerVersion }) => {
this.initMapLayer(layerName, layerVersion)
})
}
let data = {
type: 'syncMapData',
msg: syncMapLayers
}
this.send(data)
} else {
if (initLayers.length) {
await this.root.indexDB.compareTable(initLayers)
initLayers.forEach(({ layerName, layerVersion }) => {
this.initMapLayer(layerName, layerVersion)
})
}
this.sendVehicleSyncMsg()
}
localStorage.setItem('layers', JSON.stringify(msg))
}
dealheartData() {
if (this.isSendSync && this.heartCount > 2) {
// 没有收到车端退出的消息时,收到3次心跳,就发送同步消息
if (this.heartCount > 2) {
this.judgeCurrentPath()
this.isSendSync = false
this.heartCount = 0
} else {
this.heartCount++
}
}
}
dealmapData(result, key) {
const self = this
const { type, msg, version } = result
let worker = this.root.worker.worker[key]
switch (type) {
case 'mapVersionAck':
this.dealMapVersion(msg)
break
default:
let strData = window.atob(msg);
worker.postMessage({ type, msg: strData, version })
break
}
worker.onmessage = ({ data }) => {
const { mapName, result, mapVersion } = data
self.mapStore.setData(mapName, result)
self.root.indexDB.storeData(mapName, result, mapVersion)
self.sendVehicleSyncMsg()
}
}
dealpathData(result, key) {
const { type, msg } = result
let [, , name] = type.split('/')
const worker = this.root.worker.worker[key]
switch (type) {
case '/vehicle/basicinfo':
const { avoidancePath, obstacle } = msg
avoidancePath && worker.postMessage({ name: 'avoidancePath', msg: avoidancePath })
obstacle && worker.postMessage({ name: 'obstacle', msg: obstacle })
this.vehicleStore.setBasicInfo(msg)
break
case '/vehicle/nextPathinfo':
let { available, path } = msg
this.mapStore.setData('nextPathAvailable', available)
path && worker.postMessage({ name, msg: path })
break
default:
msg && worker.postMessage({ name, msg })
break
}
worker.onmessage = ({ data }) => {
const { name, dealedData } = data
this.mapStore.setData(name === 'obstacle' ? 'obstacleData' : name, dealedData)
}
}
dealvehicleData(result, key) {
const { type, msg } = result
const vehicleStoreToRefs = storeToRefs(this.vehicleStore)
const { paraminfo, positioninfo } = vehicleStoreToRefs
const { deviceId } = paraminfo.value
const worker = this.root.worker.worker[key]
const prePositionInfo = JSON.parse(JSON.stringify(positioninfo.value))
worker.postMessage({ type, msg, deviceId, pre: prePositionInfo })
const diffWorker = this.root.worker.worker['vehicleDiff']
diffWorker.postMessage({ pre: prePositionInfo, cur: msg })
worker.onmessage = ({ data }) => {
const { vehicles, safeObj, trajObj, gps, currentBasic } = data
this.stateStore.setData('gps', gps)
currentBasic && this.vehicleStore.setData('currentBasic', currentBasic)
this.vehicleStore.$patch({
vehiclesPositioninfo: vehicles,
safeObj: safeObj,
trajObj: trajObj
})
}
diffWorker.onmessage = ({ data }) => {
this.vehicleStore.setData('deleteVehicles', data)
}
this.vehicleStore.setData('positioninfo', msg)
}
handleSettingAck(msg) {
let { type, code } = msg
if (!SETTING_TYPE[type]) return
let ackType = code === 0 ? 'success' : 'error'
let text = SETTING_TYPE[type];
if (type == 4) {
text = '指令'
}
Message[ackType](`${text}设置${code === 0 ? '成功' : '失败'}`)
if (type == 6 && code === 0) {
let data = {
type: '/setting/req',
msg: {
type: 6
}
}
this.send(data)
}
}
dealObstaclesAck(msg) {
let { code } = msg
let type = code === 0 ? 'success' : 'error'
let tip = code === 0 ? '成功' : '失败'
Message[type](`发送车端${tip}`)
}
dealhmiData(result) {
const { type, msg } = result
let [, , name] = type.split('/')
// console.log(name, msg)
switch (type) {
// 车辆状态信息
case '/vehicle/statusinfo':
const { pattern, connStatus } = msg
this.stateStore.setData('wifi', connStatus)
this.vehicleStore.setData('pattern', pattern)
this.vehicleStore.setData(name, msg)
break
// 车辆故障码信息
case '/vehicle/errorCode':
this.vehicleStore.dealErrorCode(msg)
break
// 车辆模式切换按钮-发送模式切换回复
case '/pattern/ack':
this.vehicleStore.setData('patternAck', msg)
break
// 弹框确认信息
case '/info/popup':
this.vehicleStore.dealInfoPop(msg)
break
// 设置信息结果返回
case '/setting/ack':
this.handleSettingAck(msg)
break
case '/setting/terminalAck':
this.vehicleStore.dealTerminalAck(msg)
break
case '/setting/ignoreObstaclesAck':
this.dealObstaclesAck(msg)
break
default:
this.vehicleStore.setData(name, msg)
break
}
}
dealmapHmiData(result) {
const { type, msg } = result
let [, , name] = type.split('/')
switch (type) {
// 地图不平整度文件回复
case '/setting/roughnessFileAck':
const { type, fileName } = msg
if (type === 1) {
this.mapStore.setData(name, fileName)
}
break
default:
this.mapStore.setData(name, msg)
break
}
}
startTime() {
const self = this
const worker = this.root.worker.worker.time
worker.postMessage('start')
worker.onmessage = ({ data }) => {
self.stateStore.setData('currentTime', data)
}
}
postParentMessage(data) {
if (data) {
let loginInfo = data
if (window.top && loginInfo && data.code === 0) {
window.top.postMessage({
type: 'token',
data: {
ip: window.location.host,
loginInfo,
}
}, '*')
}
try {
localStorage.setItem(window.location.host, JSON.stringify(loginInfo))
} catch (error) {
this.isTraceless = true
console.log(error)
Message.error('请退出无痕模式!')
}
}
}
dealloginData(result) {
const { type, msg } = result
switch (type) {
// 车端登录成功,有连结进来 车端就会发这个消息
case '/truckmonitor/start':
// 收到车端登录成功的消息,则不需要判断收到心跳的次数
// 需要判断是否再登录页面,如果再登录页面,不需要处理,如果不在登录页面,则需要发送同步信息
this.isSendSync = false
this.judgeCurrentPath()
this.dealTruckMointor({
visible: false,
type: 1
})
break
case '/truckmonitor/exit':
router.push({
name: 'login'
})
this.dealTruckMointor({
visible: true,
msg: '车端程序未运行',
type: 1
})
this.stateStore.setData('loginAck', msg)
break
// 登录
case '/login/ack':
console.log('收到登录信息', new Date())
this.stateStore.setData('loginAck', msg)
this.postParentMessage(msg)
// this.startTime()
break
// 退出
case '/btn/quitApp':
if (window.state === 'logout') {
window.state = null
localStorage.removeItem(window.location.host)
this.close()
router.push({
name: 'login'
})
}
}
}
async dealData(data) {
this.resetTimeout()
let result = await (new Response(data)).text()
if (typeof result === 'string' && result !== 'heart beat') {
try {
result = JSON.parse(result)
} catch (err) {
console.log('err----', result)
}
}
const { type, msg } = result
// console.log(`接收消息${type}`, new Date().format('yyyy-MM-dd hh:mm:ss'))
if (!type) return
// console.log(type)
let keys = Object.keys(TOPIC_CLASS)
for (let i = 0; i < keys.length; i++) {
let key = keys[i]
if (TOPIC_CLASS[key].includes(type)) {
this[`deal${key}Data`](result, key)
break
}
}
}
close() {
this.isClosed = true
this.heartTimer && clearTimeout(this.heartTimer)
this.serverTimer && clearTimeout(this.serverTimer)
this.reconnectTimer && clearTimeout(this.reconnectTimer)
this.sendFixHeartTimer && clearInterval(this.sendFixHeartTimer)
this.timeoutTimer && clearInterval(this.timeoutTimer)
this.heartTimer = null
this.serverTimer = null
this.reconnectTimer = null
this.sendFixHeartTimer = null
this.ws.close()
this.root.sock = null
this.root.worker = null
this.root.indexDB = null
}
}
\ No newline at end of file
import { useStateStore } from '@/store/StateStore'
import { useVehicleStore } from '@/store/VehicleStore.js'
import { useMapStore } from '@/store/MapStore.js'
import router from '../../router'
import { storeToRefs } from 'pinia'
import { Message } from 'view-ui-plus'
const pingIntervalSec = 5000 // 心跳链接时间
const NETWORK_COUNT = 3
const TRUCK_MONITOR_EXIT = 15 * 60
const TOPIC_CLASS = {
vehicle: ['/vehicle/positioninfo'],
map: ['/map/obstacles', '/map/lanenode', '/map/lane', '/map/runablearea', '/map/centerline', '/map/diggingworkarea', '/map/dumparea', '/map/barricade', '/map/stationarea', '/map/parkspot', '/map/staticobjs', '/map/electronicFence', 'mapVersionAck'],
path: ['/vehicle/currPathinfo', '/vehicle/nextPathinfo', '/singleTrack/morepoint', '/singleTrack/morePath', '/vehicle/basicinfo', '/vehicle/dynamicPathinfo'], // 车辆循迹路径信息,当前路径;车辆循迹路径信息,下一段路径;单机循迹预览多路径;单机循迹预览单路径; 车辆动态路权信息
hmi: [
'/vehicle/debuginfo', // 车辆调试信息
'/vehicle/statusinfo', // 车辆状态信息
'/vehicle/errorCode', // 车辆故障码信息
'/vehicle/pairinfo', // 车辆配对信息
'/info/commandInfo', // 车辆提示信息(左下角)
'/info/tipInfo', // 车辆弹框提示信息
'/vehicle/paraminfo', // 车辆全局参数信息
'/command/debugack', //
'/pattern/ack', // 车辆模式切换按钮-发送模式切换回复
'/task/retranTimer', // 车辆任务重传定时器消息
'/setting/singleTrackAck', // 设置单机循迹
'/product',
'/info/popup', // 弹框确认信息
'/setting/basicinfo', // 基本设置数据信息
'/setting/light', // 灯光
'/setting/safe', // 安全
'/setting/controlInfo', // 启动参数
'/setting/ack', // 设置信息结果返回
'/setting/terminal', // 终端对应关系列表
'/setting/terminalAck',
'/setting/ignoreObstaclesAck', // 忽略障碍物回复消息
],
mapHmi: [
'/setting/map', // 地图管理图层
'/setting/mapControlAck', // 地图设置回复
'/setting/roughnessFileAck', // 地图不平整度文件回复
],
login: [
'/login/ack', // 登录信息
'/btn/quitApp', // 退出
'/truckmonitor/start', // 车端登录成功,有连结进来 车端就会发这个消息
'/truckmonitor/exit', // 车端程序退出,不关闭websocket
],
heart: ['heartbeat']
}
const SETTING_TYPE = {
1: '基本信息',
2: '灯光信息',
3: '安全信息',
4: '启动控制信息',
5: '地图管理信息',
6: '单机循迹信息',
// 7: '终端打印信息'
}
export default class Socket {
constructor(root) {
this.ws = new WebSocket(`ws://${document.location.serviceIP}`)
this.stateStore = useStateStore()
this.vehicleStore = useVehicleStore()
this.mapStore = useMapStore()
this.heartTimer = null // 心跳定时器
this.serverTimer = null // 服务器超时定时器
this.sendFixHeartTimer = null // 定时发送心跳
this.timeoutTimer = null // 超时定时器
this.lockReconnect = false // 是否真正建立连接
this.reconnectTimer = null // 重连倒计时
this.isSendSync = false // 是否要重连成功之后,发送同步消息
this.heartCount = 0 // 重连之后,如果没有收到车端登录的消息,需要发送同步消息
this.root = root
}
getConnection() {
const self = this
return new Promise((resolve, reject) => {
self.ws = new WebSocket(`ws://${document.location.serviceIP}`)
self.ws.onopen = function () {
// 开启心跳
self.start()
self.sendFixHeart()
self.resetTimeout()
self.count = 0
resolve(self.ws)
}
self.ws.onerror = function (err) {
console.log(err)
self.isSendSync = true
self.reconnect()
}
self.ws.onclose = function () {
!self.isClosed && self.reconnect()
console.log('closed', new Date())
}
self.ws.onmessage = function (msg) {
self.dealData(msg.data)
// 收到服务器信息,心跳重置
self.reset()
}
})
}
start() {
this.heartTimer && clearTimeout(this.heartTimer)
this.serverTimer && clearTimeout(this.serverTimer)
this.heartTimer = setTimeout(() => {
this.send({
type: 'heartbeat',
msg: 'heart beat'
})
// 超时关闭
this.serverTimer = setTimeout(() => {
this.ws.close()
}, 15000)
}, pingIntervalSec)
}
// 15s定时发送心跳
sendFixHeart() {
this.sendFixHeartTimer && clearInterval(this.sendFixHeartTimer)
this.sendFixHeartTimer = setInterval(() => {
this.send({
type: 'heartbeat',
msg: 'heart beat'
})
}, 15000)
}
resetTimeout() {
const self = this
if (this.timeoutTimer) {
this.timeoutCount = 0
this.stateStore.dealNetworkTip({ type: false })
clearInterval(this.timeoutTimer)
}
this.timeoutTimer = setInterval(() => {
if (this.timeoutCount > NETWORK_COUNT) {
this.stateStore.dealNetworkTip({ type: true, count: this.timeoutCount })
}
this.timeoutCount++
// 超时一定时间,认为车端程序未启动,跳转登录页面
if (self.timeoutCount > TRUCK_MONITOR_EXIT) {
this.dealTruckMointor({
visible: true,
msg: '车端程序未运行',
type: 1
})
router.push({
name: 'login'
})
}
}, 1000)
}
reset() {
this.start()
}
reconnect() {
if (this.lockReconnect) return
const self = this
// this.resetTimeout()
console.log('reconnect-----')
if (this.count > 5) {
Message.error('当前网络不稳定')
this.count = 0
// return
}
this.count++
this.lockReconnect = true
this.reconnectTimer && clearTimeout(this.reconnectTimer)
this.reconnectTimer = setTimeout(() => {
this.getConnection().then(sock => {
self.root.sock = sock
})
this.lockReconnect = false
}, 4000)
}
send(msg) {
msg = JSON.stringify(msg)
console.log(`发送消息${msg}`, new Date())
this.ws && this.ws.send(msg)
}
sendLoginMsg() {
let loginForm = this.stateStore.loginForm || localStorage.getItem(`${location.host}loginForm`)
if (typeof loginForm === 'string') {
loginForm = JSON.parse(loginForm)
}
this.send({
type: '/login/req',
msg: loginForm
})
}
judgeCurrentPath() {
let currentPath = router.currentRoute.value.path
if (currentPath.includes('basic-message')) {
this.sendLoginMsg()
}
}
dealTruckMointor(msg) {
this.stateStore.setTruckMonitor(msg)
}
// 初始化indexDB里的地图图层数据
initMapLayer(layerName, version) {
let db = this.root.indexDB
db.getStoreData(layerName, version).then(res => {
if (res) {
this.mapStore.setData(layerName, res.msg)
} else {
let data = {
type: 'syncMapData',
msg: [layerName]
}
this.send(data)
}
}).catch(error => {
console.warn(error)
})
}
sendVehicleSyncMsg() {
let syncVehicleData = {
type: 'syncVehicleData',
msg: {}
}
this.send(syncVehicleData)
}
async dealMapVersion(msg) {
console.log('msg', msg)
let storeLayers = window.localStorage.getItem('layers')
let syncMapLayers = [], initLayers = []
if (storeLayers) {
let layers = JSON.parse(storeLayers)
for (let i = 0; i < msg.length; i++) {
let item = msg[i]
let { layerName, layerVersion } = item
let oldLayer = layers.find(item => item.layerName === layerName)
if (oldLayer) {
let { layerVersion: oldLayerVersion } = oldLayer
if (oldLayerVersion !== layerVersion) {
syncMapLayers.push(layerName)
} else {
initLayers.push({ layerName, layerVersion })
}
} else {
syncMapLayers.push(layerName)
}
}
} else {
syncMapLayers = msg.map(item => item.layerName)
}
if (syncMapLayers.length) {
await this.root.indexDB.initStore(syncMapLayers)
if (initLayers.length) {
await this.root.indexDB.compareTable(initLayers)
initLayers.forEach(({ layerName, layerVersion }) => {
this.initMapLayer(layerName, layerVersion)
})
}
let data = {
type: 'syncMapData',
msg: syncMapLayers
}
this.send(data)
} else {
if (initLayers.length) {
await this.root.indexDB.compareTable(initLayers)
initLayers.forEach(({ layerName, layerVersion }) => {
this.initMapLayer(layerName, layerVersion)
})
}
this.sendVehicleSyncMsg()
}
localStorage.setItem('layers', JSON.stringify(msg))
}
dealheartData() {
if (this.isSendSync && this.heartCount > 2) {
// 没有收到车端退出的消息时,收到3次心跳,就发送同步消息
if (this.heartCount > 2) {
this.judgeCurrentPath()
this.isSendSync = false
this.heartCount = 0
} else {
this.heartCount++
}
}
}
dealmapData(result, key) {
const self = this
const { type, msg, version } = result
let worker = this.root.worker.worker[key]
switch (type) {
case 'mapVersionAck':
this.dealMapVersion(msg)
break
default:
let strData = window.atob(msg);
worker.postMessage({ type, msg: strData, version })
break
}
worker.onmessage = ({ data }) => {
const { mapName, result, mapVersion } = data
self.mapStore.setData(mapName, result)
self.root.indexDB.storeData(mapName, result, mapVersion)
self.sendVehicleSyncMsg()
}
}
dealpathData(result, key) {
const { type, msg } = result
let [, , name] = type.split('/')
const worker = this.root.worker.worker[key]
switch (type) {
case '/vehicle/basicinfo':
const { avoidancePath, obstacle } = msg
avoidancePath && worker.postMessage({ name: 'avoidancePath', msg: avoidancePath })
obstacle && worker.postMessage({ name: 'obstacle', msg: obstacle })
this.vehicleStore.setBasicInfo(msg)
break
case '/vehicle/nextPathinfo':
let { available, path } = msg
this.mapStore.setData('nextPathAvailable', available)
path && worker.postMessage({ name, msg: path })
break
default:
msg && worker.postMessage({ name, msg })
break
}
worker.onmessage = ({ data }) => {
const { name, dealedData } = data
this.mapStore.setData(name === 'obstacle' ? 'obstacleData' : name, dealedData)
}
}
dealvehicleData(result, key) {
const { type, msg } = result
const vehicleStoreToRefs = storeToRefs(this.vehicleStore)
const { paraminfo, positioninfo } = vehicleStoreToRefs
const { deviceId } = paraminfo.value
const worker = this.root.worker.worker[key]
const prePositionInfo = JSON.parse(JSON.stringify(positioninfo.value))
worker.postMessage({ type, msg, deviceId, pre: prePositionInfo })
const diffWorker = this.root.worker.worker['vehicleDiff']
diffWorker.postMessage({ pre: prePositionInfo, cur: msg })
worker.onmessage = ({ data }) => {
const { vehicles, safeObj, trajObj, gps, currentBasic } = data
this.stateStore.setData('gps', gps)
currentBasic && this.vehicleStore.setData('currentBasic', currentBasic)
this.vehicleStore.$patch({
vehiclesPositioninfo: vehicles,
safeObj: safeObj,
trajObj: trajObj
})
}
diffWorker.onmessage = ({ data }) => {
this.vehicleStore.setData('deleteVehicles', data)
}
this.vehicleStore.setData('positioninfo', msg)
}
handleSettingAck(msg) {
let { type, code } = msg
if (!SETTING_TYPE[type]) return
let ackType = code === 0 ? 'success' : 'error'
let text = SETTING_TYPE[type];
if (type == 4) {
text = '指令'
}
Message[ackType](`${text}设置${code === 0 ? '成功' : '失败'}`)
if (type == 6 && code === 0) {
let data = {
type: '/setting/req',
msg: {
type: 6
}
}
this.send(data)
}
}
dealObstaclesAck(msg) {
let { code } = msg
let type = code === 0 ? 'success' : 'error'
let tip = code === 0 ? '成功' : '失败'
Message[type](`发送车端${tip}`)
}
dealhmiData(result) {
const { type, msg } = result
let [, , name] = type.split('/')
// console.log(name, msg)
switch (type) {
// 车辆状态信息
case '/vehicle/statusinfo':
const { pattern, connStatus } = msg
this.stateStore.setData('wifi', connStatus)
this.vehicleStore.setData('pattern', pattern)
this.vehicleStore.setData(name, msg)
break
// 车辆故障码信息
case '/vehicle/errorCode':
this.vehicleStore.dealErrorCode(msg)
break
// 车辆模式切换按钮-发送模式切换回复
case '/pattern/ack':
this.vehicleStore.setData('patternAck', msg)
break
// 弹框确认信息
case '/info/popup':
this.vehicleStore.dealInfoPop(msg)
break
// 设置信息结果返回
case '/setting/ack':
this.handleSettingAck(msg)
break
case '/setting/terminalAck':
this.vehicleStore.dealTerminalAck(msg)
break
case '/setting/ignoreObstaclesAck':
this.dealObstaclesAck(msg)
break
default:
this.vehicleStore.setData(name, msg)
break
}
}
dealmapHmiData(result) {
const { type, msg } = result
let [, , name] = type.split('/')
switch (type) {
// 地图不平整度文件回复
case '/setting/roughnessFileAck':
const { type, fileName } = msg
if (type === 1) {
this.mapStore.setData(name, fileName)
}
break
default:
this.mapStore.setData(name, msg)
break
}
}
startTime() {
const self = this
const worker = this.root.worker.worker.time
worker.postMessage('start')
worker.onmessage = ({ data }) => {
self.stateStore.setData('currentTime', data)
}
}
postParentMessage(data) {
if (data) {
let loginInfo = data
if (window.top && loginInfo && data.code === 0) {
window.top.postMessage({
type: 'token',
data: {
ip: window.location.host,
loginInfo,
}
}, '*')
}
try {
localStorage.setItem(window.location.host, JSON.stringify(loginInfo))
} catch (error) {
this.isTraceless = true
console.log(error)
Message.error('请退出无痕模式!')
}
}
}
dealloginData(result) {
const { type, msg } = result
switch (type) {
// 车端登录成功,有连结进来 车端就会发这个消息
case '/truckmonitor/start':
// 收到车端登录成功的消息,则不需要判断收到心跳的次数
// 需要判断是否再登录页面,如果再登录页面,不需要处理,如果不在登录页面,则需要发送同步信息
this.isSendSync = false
this.judgeCurrentPath()
this.dealTruckMointor({
visible: false,
type: 1
})
break
case '/truckmonitor/exit':
router.push({
name: 'login'
})
this.dealTruckMointor({
visible: true,
msg: '车端程序未运行',
type: 1
})
this.stateStore.setData('loginAck', msg)
break
// 登录
case '/login/ack':
console.log('收到登录信息', new Date())
this.stateStore.setData('loginAck', msg)
this.postParentMessage(msg)
// this.startTime()
break
// 退出
case '/btn/quitApp':
if (window.state === 'logout') {
window.state = null
localStorage.removeItem(window.location.host)
this.close()
router.push({
name: 'login'
})
}
}
}
async dealData(data) {
this.resetTimeout()
let result = await (new Response(data)).text()
if (typeof result === 'string' && result !== 'heart beat') {
try {
result = JSON.parse(result)
} catch (err) {
console.log('err----', result)
}
}
const { type, msg } = result
// console.log(`接收消息${type}`, new Date().format('yyyy-MM-dd hh:mm:ss'))
if (!type) return
// console.log(type)
let keys = Object.keys(TOPIC_CLASS)
for (let i = 0; i < keys.length; i++) {
let key = keys[i]
if (TOPIC_CLASS[key].includes(type)) {
this[`deal${key}Data`](result, key)
break
}
}
}
close() {
this.isClosed = true
this.heartTimer && clearTimeout(this.heartTimer)
this.serverTimer && clearTimeout(this.serverTimer)
this.reconnectTimer && clearTimeout(this.reconnectTimer)
this.sendFixHeartTimer && clearInterval(this.sendFixHeartTimer)
this.timeoutTimer && clearInterval(this.timeoutTimer)
this.heartTimer = null
this.serverTimer = null
this.reconnectTimer = null
this.sendFixHeartTimer = null
this.ws.close()
this.root.sock = null
this.root.worker = null
this.root.indexDB = null
}
}
\ No newline at end of file
import { useStateStore } from '@/store/StateStore'
import { useVehicleStore } from '@/store/VehicleStore.js'
import { useMapStore } from '@/store/MapStore.js'
import router from '../../router'
import { storeToRefs } from 'pinia'
import { Message } from 'view-ui-plus'
const pingIntervalSec = 5000 // 心跳链接时间
const NETWORK_COUNT = 3
const TRUCK_MONITOR_EXIT = 15 * 60
const TOPIC_CLASS = {
vehicle: ['/vehicle/positioninfo'],
map: ['/map/obstacles', '/map/lanenode', '/map/lane', '/map/runablearea', '/map/centerline', '/map/diggingworkarea', '/map/dumparea', '/map/barricade', '/map/stationarea', '/map/parkspot', '/map/staticobjs', '/map/electronicFence', 'mapVersionAck'],
path: ['/vehicle/currPathinfo', '/vehicle/nextPathinfo', '/singleTrack/morepoint', '/singleTrack/morePath', '/vehicle/basicinfo', '/vehicle/dynamicPathinfo'], // 车辆循迹路径信息,当前路径;车辆循迹路径信息,下一段路径;单机循迹预览多路径;单机循迹预览单路径; 车辆动态路权信息
hmi: [
'/vehicle/debuginfo', // 车辆调试信息
'/vehicle/statusinfo', // 车辆状态信息
'/vehicle/errorCode', // 车辆故障码信息
'/vehicle/pairinfo', // 车辆配对信息
'/info/commandInfo', // 车辆提示信息(左下角)
'/info/tipInfo', // 车辆弹框提示信息
'/vehicle/paraminfo', // 车辆全局参数信息
'/command/debugack', //
'/pattern/ack', // 车辆模式切换按钮-发送模式切换回复
'/task/retranTimer', // 车辆任务重传定时器消息
'/setting/singleTrackAck', // 设置单机循迹
'/product',
'/info/popup', // 弹框确认信息
'/setting/basicinfo', // 基本设置数据信息
'/setting/light', // 灯光
'/setting/safe', // 安全
'/setting/controlInfo', // 启动参数
'/setting/ack', // 设置信息结果返回
'/setting/terminal', // 终端对应关系列表
'/setting/terminalAck',
'/setting/ignoreObstaclesAck', // 忽略障碍物回复消息
],
mapHmi: [
'/setting/map', // 地图管理图层
'/setting/mapControlAck', // 地图设置回复
'/setting/roughnessFileAck', // 地图不平整度文件回复
],
login: [
'/login/ack', // 登录信息
'/btn/quitApp', // 退出
'/truckmonitor/start', // 车端登录成功,有连结进来 车端就会发这个消息
'/truckmonitor/exit', // 车端程序退出,不关闭websocket
],
heart: ['heartbeat']
}
const SETTING_TYPE = {
1: '基本信息',
2: '灯光信息',
3: '安全信息',
4: '启动控制信息',
5: '地图管理信息',
6: '单机循迹信息',
// 7: '终端打印信息'
}
export default class Socket {
constructor(root) {
this.ws = new WebSocket(`ws://${document.location.serviceIP}`)
this.stateStore = useStateStore()
this.vehicleStore = useVehicleStore()
this.mapStore = useMapStore()
this.heartTimer = null // 心跳定时器
this.serverTimer = null // 服务器超时定时器
this.sendFixHeartTimer = null // 定时发送心跳
this.timeoutTimer = null // 超时定时器
this.lockReconnect = false // 是否真正建立连接
this.reconnectTimer = null // 重连倒计时
this.isSendSync = false // 是否要重连成功之后,发送同步消息
this.heartCount = 0 // 重连之后,如果没有收到车端登录的消息,需要发送同步消息
this.root = root
}
getConnection() {
const self = this
return new Promise((resolve, reject) => {
self.ws = new WebSocket(`ws://${document.location.serviceIP}`)
self.ws.onopen = function () {
// 开启心跳
self.start()
self.sendFixHeart()
self.resetTimeout()
self.count = 0
resolve(self.ws)
}
self.ws.onerror = function (err) {
console.log(err)
self.isSendSync = true
self.reconnect()
}
self.ws.onclose = function () {
!self.isClosed && self.reconnect()
console.log('closed', new Date())
}
self.ws.onmessage = function (msg) {
self.dealData(msg.data)
// 收到服务器信息,心跳重置
self.reset()
}
})
}
start() {
this.heartTimer && clearTimeout(this.heartTimer)
this.serverTimer && clearTimeout(this.serverTimer)
this.heartTimer = setTimeout(() => {
this.send({
type: 'heartbeat',
msg: 'heart beat'
})
// 超时关闭
this.serverTimer = setTimeout(() => {
this.ws.close()
}, 15000)
}, pingIntervalSec)
}
// 15s定时发送心跳
sendFixHeart() {
this.sendFixHeartTimer && clearInterval(this.sendFixHeartTimer)
this.sendFixHeartTimer = setInterval(() => {
this.send({
type: 'heartbeat',
msg: 'heart beat'
})
}, 15000)
}
resetTimeout() {
const self = this
if (this.timeoutTimer) {
this.timeoutCount = 0
this.stateStore.dealNetworkTip({ type: false })
clearInterval(this.timeoutTimer)
}
this.timeoutTimer = setInterval(() => {
if (this.timeoutCount > NETWORK_COUNT) {
this.stateStore.dealNetworkTip({ type: true, count: this.timeoutCount })
}
this.timeoutCount++
// 超时一定时间,认为车端程序未启动,跳转登录页面
if (self.timeoutCount > TRUCK_MONITOR_EXIT) {
this.dealTruckMointor({
visible: true,
msg: '车端程序未运行',
type: 1
})
router.push({
name: 'login'
})
}
}, 1000)
}
reset() {
this.start()
}
reconnect() {
if (this.lockReconnect) return
const self = this
// this.resetTimeout()
console.log('reconnect-----')
if (this.count > 5) {
Message.error('当前网络不稳定')
this.count = 0
// return
}
this.count++
this.lockReconnect = true
this.reconnectTimer && clearTimeout(this.reconnectTimer)
this.reconnectTimer = setTimeout(() => {
this.getConnection().then(sock => {
self.root.sock = sock
})
this.lockReconnect = false
}, 4000)
}
send(msg) {
msg = JSON.stringify(msg)
console.log(`发送消息${msg}`, new Date())
this.ws && this.ws.send(msg)
}
sendLoginMsg() {
let loginForm = this.stateStore.loginForm || localStorage.getItem(`${location.host}loginForm`)
if (typeof loginForm === 'string') {
loginForm = JSON.parse(loginForm)
}
this.send({
type: '/login/req',
msg: loginForm
})
}
judgeCurrentPath() {
let currentPath = router.currentRoute.value.path
if (currentPath.includes('basic-message')) {
this.sendLoginMsg()
}
}
dealTruckMointor(msg) {
this.stateStore.setTruckMonitor(msg)
}
// 初始化indexDB里的地图图层数据
initMapLayer(layerName, version) {
let db = this.root.indexDB
db.getStoreData(layerName, version).then(res => {
if (res) {
this.mapStore.setData(layerName, res.msg)
} else {
let data = {
type: 'syncMapData',
msg: [layerName]
}
this.send(data)
}
}).catch(error => {
console.warn(error)
})
}
sendVehicleSyncMsg() {
let syncVehicleData = {
type: 'syncVehicleData',
msg: {}
}
this.send(syncVehicleData)
}
async dealMapVersion(msg) {
console.log('msg', msg)
let storeLayers = window.localStorage.getItem('layers')
let syncMapLayers = [], initLayers = []
if (storeLayers) {
let layers = JSON.parse(storeLayers)
for (let i = 0; i < msg.length; i++) {
let item = msg[i]
let { layerName, layerVersion } = item
let oldLayer = layers.find(item => item.layerName === layerName)
if (oldLayer) {
let { layerVersion: oldLayerVersion } = oldLayer
if (oldLayerVersion !== layerVersion) {
syncMapLayers.push(layerName)
} else {
initLayers.push({ layerName, layerVersion })
}
} else {
syncMapLayers.push(layerName)
}
}
} else {
syncMapLayers = msg.map(item => item.layerName)
}
if (syncMapLayers.length) {
await this.root.indexDB.initStore(syncMapLayers)
if (initLayers.length) {
await this.root.indexDB.compareTable(initLayers)
initLayers.forEach(({ layerName, layerVersion }) => {
this.initMapLayer(layerName, layerVersion)
})
}
let data = {
type: 'syncMapData',
msg: syncMapLayers
}
this.send(data)
} else {
if (initLayers.length) {
await this.root.indexDB.compareTable(initLayers)
initLayers.forEach(({ layerName, layerVersion }) => {
this.initMapLayer(layerName, layerVersion)
})
}
this.sendVehicleSyncMsg()
}
localStorage.setItem('layers', JSON.stringify(msg))
}
dealheartData() {
if (this.isSendSync && this.heartCount > 2) {
// 没有收到车端退出的消息时,收到3次心跳,就发送同步消息
if (this.heartCount > 2) {
this.judgeCurrentPath()
this.isSendSync = false
this.heartCount = 0
} else {
this.heartCount++
}
}
}
dealmapData(result, key) {
const self = this
const { type, msg, version } = result
let worker = this.root.worker.worker[key]
switch (type) {
case 'mapVersionAck':
this.dealMapVersion(msg)
break
default:
let strData = window.atob(msg);
worker.postMessage({ type, msg: strData, version })
break
}
worker.onmessage = ({ data }) => {
const { mapName, result, mapVersion } = data
self.mapStore.setData(mapName, result)
self.root.indexDB.storeData(mapName, result, mapVersion)
self.sendVehicleSyncMsg()
}
}
dealpathData(result, key) {
const { type, msg } = result
let [, , name] = type.split('/')
const worker = this.root.worker.worker[key]
switch (type) {
case '/vehicle/basicinfo':
const { avoidancePath, obstacle } = msg
avoidancePath && worker.postMessage({ name: 'avoidancePath', msg: avoidancePath })
obstacle && worker.postMessage({ name: 'obstacle', msg: obstacle })
this.vehicleStore.setBasicInfo(msg)
break
case '/vehicle/nextPathinfo':
let { available, path } = msg
this.mapStore.setData('nextPathAvailable', available)
path && worker.postMessage({ name, msg: path })
break
default:
msg && worker.postMessage({ name, msg })
break
}
worker.onmessage = ({ data }) => {
const { name, dealedData } = data
this.mapStore.setData(name === 'obstacle' ? 'obstacleData' : name, dealedData)
}
}
dealvehicleData(result, key) {
const { type, msg } = result
const vehicleStoreToRefs = storeToRefs(this.vehicleStore)
const { paraminfo, positioninfo } = vehicleStoreToRefs
const { deviceId } = paraminfo.value
const worker = this.root.worker.worker[key]
const prePositionInfo = JSON.parse(JSON.stringify(positioninfo.value))
worker.postMessage({ type, msg, deviceId, pre: prePositionInfo })
const diffWorker = this.root.worker.worker['vehicleDiff']
diffWorker.postMessage({ pre: prePositionInfo, cur: msg })
worker.onmessage = ({ data }) => {
const { vehicles, safeObj, trajObj, gps, currentBasic } = data
this.stateStore.setData('gps', gps)
currentBasic && this.vehicleStore.setData('currentBasic', currentBasic)
this.vehicleStore.$patch({
vehiclesPositioninfo: vehicles,
safeObj: safeObj,
trajObj: trajObj
})
}
diffWorker.onmessage = ({ data }) => {
this.vehicleStore.setData('deleteVehicles', data)
}
this.vehicleStore.setData('positioninfo', msg)
}
handleSettingAck(msg) {
let { type, code } = msg
if (!SETTING_TYPE[type]) return
let ackType = code === 0 ? 'success' : 'error'
let text = SETTING_TYPE[type];
if (type == 4) {
text = '指令'
}
Message[ackType](`${text}设置${code === 0 ? '成功' : '失败'}`)
if (type == 6 && code === 0) {
let data = {
type: '/setting/req',
msg: {
type: 6
}
}
this.send(data)
}
}
dealObstaclesAck(msg) {
let { code } = msg
let type = code === 0 ? 'success' : 'error'
let tip = code === 0 ? '成功' : '失败'
Message[type](`发送车端${tip}`)
}
dealhmiData(result) {
const { type, msg } = result
let [, , name] = type.split('/')
// console.log(name, msg)
switch (type) {
// 车辆状态信息
case '/vehicle/statusinfo':
const { pattern, connStatus } = msg
this.stateStore.setData('wifi', connStatus)
this.vehicleStore.setData('pattern', pattern)
this.vehicleStore.setData(name, msg)
break
// 车辆故障码信息
case '/vehicle/errorCode':
this.vehicleStore.dealErrorCode(msg)
break
// 车辆模式切换按钮-发送模式切换回复
case '/pattern/ack':
this.vehicleStore.setData('patternAck', msg)
break
// 弹框确认信息
case '/info/popup':
this.vehicleStore.dealInfoPop(msg)
break
// 设置信息结果返回
case '/setting/ack':
this.handleSettingAck(msg)
break
case '/setting/terminalAck':
this.vehicleStore.dealTerminalAck(msg)
break
case '/setting/ignoreObstaclesAck':
this.dealObstaclesAck(msg)
break
default:
this.vehicleStore.setData(name, msg)
break
}
}
dealmapHmiData(result) {
const { type, msg } = result
let [, , name] = type.split('/')
switch (type) {
// 地图不平整度文件回复
case '/setting/roughnessFileAck':
const { type, fileName } = msg
if (type === 1) {
this.mapStore.setData(name, fileName)
}
break
default:
this.mapStore.setData(name, msg)
break
}
}
startTime() {
const self = this
const worker = this.root.worker.worker.time
worker.postMessage('start')
worker.onmessage = ({ data }) => {
self.stateStore.setData('currentTime', data)
}
}
postParentMessage(data) {
if (data) {
let loginInfo = data
if (window.top && loginInfo && data.code === 0) {
window.top.postMessage({
type: 'token',
data: {
ip: window.location.host,
loginInfo,
}
}, '*')
}
try {
localStorage.setItem(window.location.host, JSON.stringify(loginInfo))
} catch (error) {
this.isTraceless = true
console.log(error)
Message.error('请退出无痕模式!')
}
}
}
dealloginData(result) {
const { type, msg } = result
switch (type) {
// 车端登录成功,有连结进来 车端就会发这个消息
case '/truckmonitor/start':
// 收到车端登录成功的消息,则不需要判断收到心跳的次数
// 需要判断是否再登录页面,如果再登录页面,不需要处理,如果不在登录页面,则需要发送同步信息
this.isSendSync = false
this.judgeCurrentPath()
this.dealTruckMointor({
visible: false,
type: 1
})
break
case '/truckmonitor/exit':
router.push({
name: 'login'
})
this.dealTruckMointor({
visible: true,
msg: '车端程序未运行',
type: 1
})
this.stateStore.setData('loginAck', msg)
break
// 登录
case '/login/ack':
console.log('收到登录信息', new Date())
this.stateStore.setData('loginAck', msg)
this.postParentMessage(msg)
// this.startTime()
break
// 退出
case '/btn/quitApp':
if (window.state === 'logout') {
window.state = null
localStorage.removeItem(window.location.host)
this.close()
router.push({
name: 'login'
})
}
}
}
async dealData(data) {
this.resetTimeout()
let result = await (new Response(data)).text()
if (typeof result === 'string' && result !== 'heart beat') {
try {
result = JSON.parse(result)
} catch (err) {
console.log('err----', result)
}
}
const { type, msg } = result
// console.log(`接收消息${type}`, new Date().format('yyyy-MM-dd hh:mm:ss'))
if (!type) return
// console.log(type)
let keys = Object.keys(TOPIC_CLASS)
for (let i = 0; i < keys.length; i++) {
let key = keys[i]
if (TOPIC_CLASS[key].includes(type)) {
this[`deal${key}Data`](result, key)
break
}
}
}
close() {
this.isClosed = true
this.heartTimer && clearTimeout(this.heartTimer)
this.serverTimer && clearTimeout(this.serverTimer)
this.reconnectTimer && clearTimeout(this.reconnectTimer)
this.sendFixHeartTimer && clearInterval(this.sendFixHeartTimer)
this.timeoutTimer && clearInterval(this.timeoutTimer)
this.heartTimer = null
this.serverTimer = null
this.reconnectTimer = null
this.sendFixHeartTimer = null
this.ws.close()
this.root.sock = null
this.root.worker = null
this.root.indexDB = null
}
}
\ No newline at end of file
const namergbaMap = {
'aliceblue': 'rgba(240,248,255)',
'antiquewhite': 'rgba(250,235,215)',
'aqua': 'rgba(0,255,255)',
'auqamarin': 'rgba(127,255,170)',
'azure': 'rgba(240,255,255)',
'beige': 'rgba(107,142,35)',
'bisque': 'rgba(255,228,196)',
'black': 'rgba(0,0,0)',
'blanchedalmond': 'rgba(255,235,205)',
'blue': 'rgba(0,0,255)',
'blueviolet': 'rgba(138,43,226)',
'brown': 'rgba(165,42,42)',
'brulywood': 'rgba(222,184,135)',
'cadetblue': 'rgba(95,158,160)',
'chartreuse': 'rgba(127,255,0)',
'chocolate': 'rgba(210,105,30)',
'coral': 'rgba(255,127,80)',
'cornflowerblue': 'rgba(100,149,237)',
'cornislk': 'rgba(255,248,220)',
'crimson': 'rgba(220,20,60)',
'cyan': 'rgba(0,255,255)',
'darkblue': 'rgba(0,0,139)',
'darkcyan': 'rgba(0,139,139)',
'darkgray': 'rgba(169,169,169)',
'darkgreen': 'rgba(0,100,0)',
'darkkhaki': 'rgba(189,183,107)',
'darkmagenta': 'rgba(139,0,139)',
'darkorange': 'rgba(255,140,0)',
'darkorchid': 'rgba(153,50,204)',
'darkred': 'rgba(139,0,0)',
'darksalmon': 'rgba(233,150,122)',
'darkseagreen': 'rgba(143,188,143)',
'darkslateblue': 'rgba(72,61,139)',
'darkslategray': 'rgba(47,79,79)',
'darkturquoise': 'rgba(0,206,209)',
'darkvoilet': 'rgba(148,0,211)',
'deeppink': 'rgba(255,20,147)',
'deepskyblue': 'rgba(0,191,255)',
'dimgray': 'rgba(105,105,105)',
'doderblue': 'rgba(30,144,255)',
'firebrick': 'rgba(178,34,34)',
'floralwhite': 'rgba(255,250,240)',
'forestgreen': 'rgba(34,139,34)',
'fuchsia': 'rgba(255,0,255)',
'gainsboro': 'rgba(220,220,220)',
'ghostwhite': 'rgba(248,248,255)',
'gold': 'rgba(255,215,0)',
'goldenrod': 'rgba(218,165,32)',
'gray': 'rgba(128,128,128)',
'green': 'rgba(0,128,0)',
'greenyellow': 'rgba(173,255,47)',
'honeydew': 'rgba(240,255,240)',
'hotpink': 'rgba(255,105,180)',
'indianred': 'rgba(205,92,92)',
'indigo': 'rgba(75,0,130)',
'ivory': 'rgba(255,255,240)',
'khaki': 'rgba(240,230,140)',
'lavender': 'rgba(230,230,250)',
'lavenderblush': 'rgba(255,240,245)',
'lawngreen': 'rgba(124,252,0)',
'lemonchiffon': 'rgba(255,250,205)',
'lightblue': 'rgba(173,216,230)',
'lightcoral': 'rgba(240,128,128)',
'lightcyan': 'rgba(225,255,255)',
'lightgoldenrodyellow': 'rgba(250,250,210)',
'lightgreen': 'rgba(144,238,144)',
'lightgrey': 'rgba(211,211,211)',
'lightpink': 'rgba(255,182,193)',
'lightsalmon': 'rgba(255,160,122)',
'lightseagreen': 'rgba(32,178,170)',
'lightskyblue': 'rgba(135,206,250)',
'lightslategray': 'rgba(119,136,153)',
'lightsteelblue': 'rgba(176,196,222)',
'lightyellow': 'rgba(255,255,224)',
'lime': 'rgba(0,255,0)',
'limegreen': 'rgba(50,205,50)',
'linen': 'rgba(250,240,230)',
'magenta': 'rgba(255,0,255)',
'maroon': 'rgba(128,0,0)',
'mediumaquamarine': 'rgba(0,250,154)',
'mediumblue': 'rgba(0,0,205)',
'mediumorchid': 'rgba(186,85,211)',
'mediumpurple': 'rgba(147,112,219)',
'mediumslateblue': 'rgba(123,104,238)',
'mediumspringgreen': 'rgba(245,255,250)',
'mediumturquoise': 'rgba(72,209,204)',
'mediumvioletred': 'rgba(199,21,133)',
'midnightblue': 'rgba(25,25,112)',
'mintcream': 'rgba(0,255,127)',
'mistyrose': 'rgba(255,228,225)',
'moccasin': 'rgba(255,228,181)',
'navajowhite': 'rgba(255,222,173)',
'navy': 'rgba(0,0,128)',
'oldlace': 'rgba(253,245,230)',
'olive': 'rgba(128,128,0)',
'olivedrab': 'rgba(85,107,47)',
'orange': 'rgba(255,165,0)',
'orangered': 'rgba(255,69,0)',
'orchid': 'rgba(218,112,214)',
'palegodenrod': 'rgba(238,232,170)',
'palegreen': 'rgba(152,251,152)',
'paleturquoise': 'rgba(175,238,238)',
'palevioletred': 'rgba(219,112,147)',
'papayawhip': 'rgba(255,239,213)',
'peachpuff': 'rgba(255,218,185)',
'peru': 'rgba(205,133,63)',
'pink': 'rgba(255,192,203)',
'plum': 'rgba(221,160,221)',
'powderblue': 'rgba(176,224,230)',
'purple': 'rgba(128,0,128)',
'red': 'rgba(255,0,0)',
'rosybrown': 'rgba(188,143,143)',
'royalblue': 'rgba(65,105,225)',
'saddlebrown': 'rgba(139,69,19)',
'salmon': 'rgba(250,128,114)',
'sandybrown': 'rgba(244,164,96)',
'seagreen': 'rgba(46,139,87)',
'seashell': 'rgba(255,245,238)',
'sienna': 'rgba(160,82,45)',
'silver': 'rgba(192,192,192)',
'skyblue': 'rgba(135,206,235)',
'slateblue': 'rgba(106,90,205)',
'slategray': 'rgba(112,128,144)',
'snow': 'rgba(255,250,250)',
'springgreen': 'rgba(60,179,113)',
'steelblue': 'rgba(70,130,180)',
'tan': 'rgba(210,180,140)',
'teal': 'rgba(0,128,128)',
'thistle': 'rgba(216,191,216)',
'tomato': 'rgba(255,99,71)',
'turquoise': 'rgba(64,224,208)',
'violet': 'rgba(238,130,238)',
'wheat': 'rgba(245,222,179)',
'white': 'rgba(255,255,255)',
'whitesmoke': 'rgba(245,245,245)',
'yellow': 'rgba(255,255,0)'
}
export { namergbaMap }
\ No newline at end of file
<template>
<div class="map-contain">
<canvas id="map" ref="root"></canvas>
<img src="/image/compass.png" class="compass" ref="compass"
:style="`transform: rotateZ(${compassRotationAngle}rad)`" alt="" @click="changeCompass">
<div class="navigate-preview">
<img src="/image/navigate.png" class="navigate" @click="changeNavigate" alt="" />
</div>
</div>
</template>
<script setup>
import { markRaw, onMounted, watch, ref, computed, onBeforeUnmount, nextTick } from 'vue'
import { useMapStore } from '@/store/MapStore.js'
import { useVehicleStore } from '@/store/VehicleStore'
import { storeToRefs } from 'pinia'
import { vehicleScale } from '../js/tools'
import DeckNew from '@/tools/Deck';
import * as turf from '@turf/turf'
const safe_color = '#FFA500'
const BASE_RAD = 1.5707963267948966
const MAP_ZOOM = 17
const LAYER_PROPOITY = {
runablearea: 1,
staticobjs: 2,
diggingarea: 3,
diggingworkarea: 3,
dumparea: 4,
barricade: 5,
electronicFence: 6,
wetArea: 7,
lane: 8,
obstacles: 9,
stationarea: 10,
parkspot: 11,
lanenode: 12,
currPathinfo: 13,
nextPathinfo: 14,
avoidancePath: 15,
traj: 16,
obstacle: 17
}
const vehicles = {}
let ismousedown = false
let ismousemove = false
let resetTime = null
let obstaclePolygons = []
let dynamicPaths = []
const reNavigateTime = 2.5 * 1000
const props = defineProps({
isShowBasic: Boolean,
clearSinglePointEnable: Boolean,
reloadData: Boolean
})
let map = markRaw({})
let currentPosition = markRaw([])
const root = ref(null)
const mapLayers = markRaw({})
const safeObjects = markRaw({})
const safeTrajs = markRaw({})
const singlePaths = markRaw([])
const navigate = ref(true)
const currentRotation = ref(0)
const markerLayerGroupLists = markRaw({})
const isSinglePreview = ref(false)
const compassRotationAngle = ref(0)
const compass = ref(null)
const mapStore = useMapStore()
const mapStoreToRefs = storeToRefs(mapStore)
const {
lanenode,
lane,
runablearea,
centerline,
obstacles,
diggingworkarea,
dumparea,
barricade,
stationarea,
parkspot,
staticobjs,
electronicFence,
morepoint, // 单机循迹单路径
morepath, // 单机循迹多路径
nextPathinfo,
currPathinfo,
nextPathAvailable,
avoidancePath, // 绕障路径
obstacleData,
dynamicPathinfo, // 车辆动态路权信息
} = mapStoreToRefs
const vehicleStore = useVehicleStore()
const vehicleStoreToRefs = storeToRefs(vehicleStore)
const { vehiclesPositioninfo, safeObj, trajObj, paraminfo, deleteVehicles } = vehicleStoreToRefs
const deviceId = computed(() => {
return paraminfo.value.deviceId
})
const emits = defineEmits(['handleSend'])
onMounted(() => {
//阻止鼠标右键
document.oncontextmenu = f => {
event.returnValue = false
}
let currentCoordinate = []
let bool = false
map = new DeckNew({
initialViewState: {
latitude: document.location.mapCenter[0],
longitude: document.location.mapCenter[1],
zoom: MAP_ZOOM,
// pickingRadius:5,
controller: true,
},
controller: true,
// parent: document.getElementById('mapMain'),
canvas: 'map',
//如果deck滚动条滚动或者拖动地图时,不进行地图角度和等级矫正
onInteractionStateChange: f => {
if (!currentPosition) return
if (f.isZooming || f.isDragging) {
navigate.value = false
clearTimeout(resetTime)
resetTime = window.setTimeout(() => {
if (!navigate.value) {
navigate.value = true
dealVehicleRotate()
currentRotation.value = BASE_RAD
clearTimeout(resetTime)
resetTime = null;
}
}, reNavigateTime)
}
}
})
map.setPitch(40)
emits('handleSend', {
type: 'syncMapVersion',
msg: {}
})
})
function changeNavigate() {
navigate.value = true
dealMapRotate(currentRotation.value, currentPosition, map.getZoom())
}
// 指南针
function changeCompass() {
dealVehicleRotate(0)
currentRotation.value = BASE_RAD
if (navigate.value) {
setTimeout(() => {
dealVehicleRotate()
}, 3 * 1000)
}
}
function dealVehicleRotate(rotate) {
let keys = Object.keys(vehicles)
let target = keys.find(item => item === deviceId.value)
let currentTarget = vehicles[target]
let vehicleHeading = BASE_RAD
let currentOldHeading = currentTarget && currentTarget.options.vehicleHeading
if (typeof rotate === 'number') {
dealMapRotate(0, currentPosition, map.getZoom())
} else {
vehicleHeading = currentOldHeading
currentRotation.value = vehicleHeading
dealMapRotate(vehicleHeading, currentPosition, map.getZoom())
}
}
function dealMapLayer(value, color, layerName) {
if (!value || !map) return
let result = JSON.parse(value)
if (!result) return
let attr = {};
if (layerName === 'barricade') {
attr = {
getLineColor: map.convertColor('#0066ff', 1),
// width: 5
}
} else {
attr = {
getLineColor: feature => {
return layerName === 'lane' ? map.convertColor('#eee', 0.5) : map.convertColor(feature.properties.color, 1)
},
getFillColor: feature => {
let opacity = 0.7;
if (layerName === 'runablearea') {
opacity = 0.5
}
return layerName === 'electronicFence' ? map.convertColor(feature.properties.color, opacity) : color ? map.convertColor(color, opacity) : map.convertColor(feature.properties.color, opacity)
}
}
}
map.createGeoJsonLayer(result, attr, layerName)
mapStore.setData(layerName, null)
}
function dealMapPath(value, layerName, color) {
if (value && value.length) {
let data = JSON.parse(JSON.stringify(value))
let path = map.createPathLayer([
{
path: [...map.coorReverse(data)],
name: layerName,
}
], {
getColor: f => map.convertColor('#ffff00'),
}, layerName)
mapLayers[layerName] = layerName
const lineGeo = turf.lineString([...map.coorReverse(JSON.parse(JSON.stringify(data)))])
let bufferLine = turf.buffer(lineGeo, 6.5, { units: "meters" })
const obj = {
type: "FeatureCollection",
features: [bufferLine]
}
const attr = {
getFillColor: f => map.convertColor(color, 0.6),
getLineColor: f => map.convertColor(color, 1)
}
// map.setLayerSortName('bufferLayer', layerName + 'Buffer')
map.createGeoJsonLayer(obj, attr, layerName + 'Buffer')
mapLayers[`${layerName}Buffer`] = `${layerName}Buffer`
} else {
// console.log(map)
value != null && map.removeLayer('map', layerName)
value != null && map.removeLayer('map', layerName + 'Buffer')
}
}
function dealDivIcon(value, layerName) {
if (!value || !map) return
let result = JSON.parse(value)
if (!result) return
const { features } = result
for (let i = 0; i < features.length; i++) {
let feature = features[i]
let { name, id } = feature.properties
let { coordinates } = feature.geometry
let markerCoor = null
if (layerName === 'lanenode') {
markerCoor = [coordinates[1], coordinates[0]]
} else {
let center = turf.center(feature)
markerCoor = [center.geometry.coordinates[1], center.geometry.coordinates[0]]
}
let texData = {
name: name,
id: id,
coordinates: [markerCoor[1], markerCoor[0]],
}
if (markerLayerGroupLists[`${layerName}${id}`]) {
markerLayerGroupLists[`${layerName}${id}`] = texData
} else {
markerLayerGroupLists[`${layerName}${id}`] = texData
}
}
map.createTextLayer([...Object.values(markerLayerGroupLists)], {
getSize: 15
}, 'text')
// map.setLayerSortName('marker', 'marker')
map.setLayerSortName('markerLayer', 'marker')
map.creatCircle(Object.values(markerLayerGroupLists), {
getFillColor: f => map.convertColor('#2aa1fa', 1),
getLineColor: f => map.convertColor('#2aa1fa', 0.5),
getLineWidth: 0.5,
getRadius: 1.5
}, 'marker')
mapStore.setData(layerName, null)
}
function removeLayer(obj, vehicleID) {
if (obj[vehicleID]) {
for (let item in obj[vehicleID]) {
map.removeLayer('map', obj[vehicleID][item])
}
}
}
function dealSafe(vehicleID, safe) {
if (!map) return
const { enable, data, dealedData, vehicleType, vehicleLatitude, vehicleLongtitude } = safe
removeLayer(safeObjects, vehicleID)
safeObjects[vehicleID] = {}
if (!enable) return
let safeType = [1, 20, 21].includes(vehicleType) ? 'circle' : 'rectangle'
let zIndex = 16
for (let i = 0; i < dealedData.length; i++) {
let item = JSON.parse(JSON.stringify(dealedData[i]))
map.setLayerSortName('polygonLayer', vehicleID + i)
if (safeType === 'rectangle') {
map.creatPolygon({
coordinates: map.coorReverse(item),
}, {
getFillColor: d => map.convertColor(safe_color, 0.4),
getLineColor: d => map.convertColor(safe_color),
getLineWidth: 0.4
}, vehicleID + i)
} else {
map.creatCircle({ coordinates: [vehicleLongtitude, vehicleLatitude] }, {
getFillColor: f => map.convertColor(safe_color, 0.4),
getLineColor: f => map.convertColor(safe_color),
getLineWidth: 0.4,
getRadius: data[i]
}, vehicleID + i)
}
safeObjects[vehicleID][i] = vehicleID + i
}
}
function dealTraj(vehicleID, traj) {
if (!traj || !map) return
const { trajPathWid, radius, dealedData, vehicleType, vehicleLatitude, vehicleLongtitude } = traj
removeLayer(safeTrajs, vehicleID)
safeTrajs[vehicleID] = {}
// if (!dealedData) return
// if ((dealedData && !dealedData.length) || !radius)) return
if ([1, 20, 21].includes(vehicleType && !radius)) return;
const zIndex = LAYER_PROPOITY['traj']
if ([1, 20, 21].includes(vehicleType) && radius) {
map.creatCircle({ coordinates: [vehicleLongtitude, vehicleLatitude] }, {
getFillColor: f => map.convertColor('#FFA500'),
getLineColor: f => map.convertColor('#FFA500'),
getLineWidth: 1,
getRadius: radius
}, vehicleID)
safeTrajs[vehicleID]['circle'] = vehicleID
} else {
if (dealedData && !dealedData.length) return;
if (dealedData == 'undefined') return;
const lineGeo = turf.lineString([...map.coorReverse(JSON.parse(JSON.stringify(dealedData)))])
// safeTrajs[vehicleID]['path'] = path
let bufferLine = turf.buffer(lineGeo, trajPathWid / 2, { units: "meters" })
const data = {
type: "FeatureCollection",
features: [bufferLine]
}
const attr = {
getFillColor: f => map.convertColor(safe_color, 0.4),
getLineColor: f => map.convertColor(safe_color, 1)
}
safeTrajs[vehicleID]['pathBuffer'] = vehicleID + 'Buffer'
map.setLayerSortName('bufferLayer', vehicleID + 'Buffer', 1)
map.createGeoJsonLayer(data, attr, vehicleID + 'Buffer')
}
}
function dealMapRotate(rotationAngle, center, zoom) {
let rotate = 0
if (rotationAngle) {
rotate = turf.radiansToDegrees(rotationAngle - BASE_RAD)
}
if (compass.value) {
compassRotationAngle.value = rotationAngle
map.setRotation(rotate)
}
center && center.length && map.flyTo(center, zoom, { animate: false })
}
function dealVehiclePosition(value) {
if (!map || !value.length) return
let zoom = map.getZoom()
for (let i = 0; i < value.length; i++) {
const item = value[i]
const { vehicleLatitude, vehicleLongtitude, vehicleHeading, vehicleID, image } = item
let vehiclePosition = [vehicleLatitude, vehicleLongtitude]
let baseObj = {
name: vehicleID,
rotate: vehicleHeading,
coordinates: [vehicleLongtitude, vehicleLatitude],
image: image,
options: {
vehicleHeading: vehicleHeading
}
}
if (vehicles[vehicleID]) {
vehicles[vehicleID] = baseObj
} else {
vehicles[vehicleID] = baseObj
}
if (vehicleID === deviceId.value) {
currentPosition = vehiclePosition
if (navigate.value) {
currentRotation.value = vehicleHeading
dealMapRotate(vehicleHeading, vehiclePosition, zoom <= MAP_ZOOM ? MAP_ZOOM : zoom)
}
}
}
initCar()
}
function initCar() {
map.clearLayer('car')
const nameText = JSON.parse(JSON.stringify(Object.values(vehicles))).map(item => {
item.coordinates = [item.coordinates[0], item.coordinates[1], 10]
return item
})
map.createTextLayer(nameText, {
getSize: 15,
getColor: f => [255, 255, 255, 255],
fontWeight: 400,
fontSettings: {
buffer: 2,
sdf: true,
smoothing: 0.5
},
}, 'carName')
let car = {};
Object.values(vehicles).forEach(item => {
if (car.hasOwnProperty(item.image)) {
car[item.image].push(item)
} else {
car[item.image] = [item]
}
})
for (let key in car) {
// map.setLayerSortName('carLayer', key)
let imageURL = `image/${key}.glb`
map.createCarModel(car[key], { scenegraph: imageURL }, key)
}
}
function dealSingleDatas(datas) {
const { color, coor } = datas
let data = JSON.parse(JSON.stringify(value))
const lineGeo = turf.lineString([...map.coorReverse(JSON.parse(JSON.stringify(data)))])
// safeTrajs[vehicleID]['path'] = path
let bufferLine = turf.buffer(lineGeo, 2, { units: "meters" })
const obj = {
type: "FeatureCollection",
features: [bufferLine]
}
const attr = {
getFillColor: f => map.convertColor(color, 0.4),
getLineColor: f => map.convertColor(color, 1)
}
map.createGeoJsonLayer(obj, attr, layerName)
singlePaths.push(layerName)
}
function dealObtacleData(value) {
if (value && value.length) {
for (let i = 0; i < value.length; i++) {
let obstacle = value[i]
let { typeColor, coordinate, centerLon, centerLat, centerRadius, centerColor } = obstacle
map.creatPolygon({
coordinates: JSON.parse(JSON.stringify(coordinate)),
}, {
getFillColor: d => map.convertColor(typeColor),
getLineColor: d => map.convertColor(typeColor),
getLineWidth: 3
}, 'obstacle')
map.setLayerSortName('buffer', 'obstacleBuffer')
map.creatCircle({ coordinates: [centerLon, centerLat] }, {
getFillColor: f => map.convertColor(centerColor, 0.5),
getLineColor: f => map.convertColor(centerColor, 0.5),
getLineWidth: 0,
getRadius: centerRadius
}, 'obstacleBuffer')
}
}
}
function dealDynamicDatas(datas) {
let name = 'dynamicLine';
const { color, path } = datas
const lineGeo = turf.lineString([...map.coorReverse(JSON.parse(JSON.stringify(path)))])
// safeTrajs[vehicleID]['path'] = path
let bufferLine = turf.buffer(lineGeo, 2, { units: "meters" })
const obj = {
type: "FeatureCollection",
features: [bufferLine]
}
const attr = {
// getFillColor: f => map.convertColor(color, 0.3),
getLineColor: f => map.convertColor(color, 0.3),
getLineWidth: 30
}
map.createGeoJsonLayer(obj, attr, name)
}
// 直接监听props.isShowBasic 不会触发
watch(() => props.isShowBasic, () => {
nextTick(() => {
// map.invalidateSize(true)
})
})
watch(lanenode, (value) => {
dealDivIcon(value, 'lanenode', '#ff0000')
}, { immediate: true, deep: true })
watch(lane, (value) => {
dealMapLayer(value, '#fff', 'lane')
}, { immediate: true, deep: true })
watch(runablearea, (value) => {
dealMapLayer(value, '#98a0a0', 'runablearea')
}, { immediate: true, deep: true })
watch(centerline, (value) => {
dealMapLayer(value, '#eee', 'centerline')
}, { immediate: true, deep: true })
watch(obstacles, (value) => {
dealDivIcon(value, 'obstacles')
dealMapLayer(value, '#6f9bdd', 'obstacles')
}, { immediate: true, deep: true })
watch(diggingworkarea, (value) => {
dealDivIcon(value, 'diggingworkarea')
dealMapLayer(value, '#886e26', 'diggingworkarea')
}, { immediate: true, deep: true })
watch(dumparea, (value) => {
dealDivIcon(value, 'dumparea')
dealMapLayer(value, '#886e26', 'dumparea')
}, { immediate: true, deep: true })
watch(barricade, (value) => {
dealMapLayer(value, '#0000ff', 'barricade')
}, { immediate: true, deep: true })
watch(stationarea, (value) => {
dealDivIcon(value, 'stationarea')
dealMapLayer(value, '#90ee90', 'stationarea')
}, { immediate: true, deep: true })
watch(parkspot, (value) => {
dealDivIcon(value, 'parkspot')
dealMapLayer(value, '#7e8185', 'parkspot')
}, { immediate: true, deep: true })
watch(staticobjs, (value) => {
dealMapLayer(value, '#98a0a0', 'staticobjs')
}, { immediate: true, deep: true })
watch(electronicFence, (value) => {
dealDivIcon(value, 'electronicFence')
dealMapLayer(value, '#d51d20', 'electronicFence')
}, { immediate: true, deep: true })
watch(nextPathinfo, (value) => {
dealMapPath(value, 'nextPath', nextPathAvailable.value === 1 ? '#19ae19' : '#8B4513')
}, { immediate: true, deep: true })
watch(currPathinfo, (value) => {
dealMapPath(value, 'currentPath', '#19ae19')
}, { immediate: true, deep: true })
watch(avoidancePath, (value) => {
dealMapPath(value, 'avoidancePath', 'blue')
}, { immediate: true, deep: true })
watch(vehiclesPositioninfo, (value) => {
dealVehiclePosition(value)
}, {
immediate: true,
deep: true
})
watch(safeObj, (value) => {
Object.keys(value).forEach(item => {
dealSafe(item, value[item])
})
}, {
immediate: true,
deep: true
})
watch(trajObj, (value) => {
Object.keys(value).forEach(item => {
dealTraj(item, value[item])
})
}, {
immediate: true,
deep: true
})
watch(obstacleData, (value) => {
dealObtacleData(value)
}, { immediate: true, deep: true })
watch(deleteVehicles, (data) => {
if (!map) return
if (data && data.length) {
data.forEach(item => {
// map.removeLayer(vehicles[item])
removeLayer(safeTrajs, item)
removeLayer(safeObjects, item)
delete vehicles[item]
initCar()
})
}
}, {
immediate: true
})
watch(() => props.clearSinglePointEnable, (enable) => {
if (!enable) return
isSinglePreview.value = true
singlePaths.forEach(item => map.removeLayer('map', item))
}, { deep: true })
watch([morepoint, morepath], (pointData, pathData) => {
if (isSinglePreview.value && (pointData || pathData)) {
let datas = pointData.concat(pathData)
for (let i = 0; i < datas.length; i++) {
dealSingleDatas(datas[i])
}
}
}, { deep: true })
watch(dynamicPathinfo, (pathData) => {
if (pathData.length > 0) {
for (let i = 0; i < pathData.length; i++) {
dealDynamicDatas(pathData[i])
}
}
}, { deep: true })
watch(deviceId, (value) => {
if (value && map) {
dealMapRotate(currentRotation.value, currentPosition, map.getZoom())
}
}, { deep: true })
function doUnmount() {
// Object.keys(mapLayers).map(layer => map.removeLayer(mapLayers[layer]))
map.remove()
map = null
}
watch(() => props.reloadData, (value) => {
if (value) {
doUnmount()
}
})
onBeforeUnmount(() => {
doUnmount()
})
</script>
<style lang="less" scope>
.map-contain {
height: 100%;
overflow: hidden;
}
#map {
width: 200% !important;
height: 200% !important;
top: -50%;
left: -50%;
background: #171A23;
position: relative !important;
}
.navigate-preview {
background: #4A75A9;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.25);
border-radius: 10px;
opacity: 1;
display: flex;
align-items: center;
justify-content: center;
}
.compass,
.navigate-preview {
position: absolute;
width: 4vw;
height: 4vw;
z-index: 999;
}
.compass {
bottom: 88px;
max-width: 60px;
max-height: 60px;
}
.navigate-preview {
bottom: 24px;
max-width: 60px;
max-height: 60px;
}
</style>
<style lang="less">
.icon-node {
width: 120px !important;
white-space: nowrap;
color: #676565;
// padding-left: -2em !important;
z-index: 1;
font-size: 12px !important;
font-family: "Open Sans Regular", "Arial Unicode MS Regular", sans-serif;
text-transform: uppercase;
text-align: center;
-webkit-text-stroke-display: block;
-webkit-text-stroke-width: 1px;
-webkit-text-stroke-color: rgba(33, 33, 33, 0.8);
}
@media screen and (min-width: 1700px) {
.icon-node {
font-size: 18px !important;
}
}
.icon-node-name {
display: inline-block;
transform: scale(.6);
padding-left: -20px !important;
}
.hide {
display: none !important;
}
.middle-line {
// 为svg设置虚线,数字越大,虚线段越长,
// 设置2个参数,参数1: 虚线的长度 参数2: 虚线和虚线之间的间隔, 如果只设置一个参数,说明两个值一样大小
stroke-dasharray: 15 20;
z-index: 999;
}
.leaflet-tooltip {
color: #fff !important;
background-color: rgba(255, 255, 255, 0) !important;
border: 0 !important;
box-shadow: rgba(0, 0, 0, 0);
font-size: 12px !important;
}
.leaflet-control-attribution {
display: none !important;
}
</style>
\ No newline at end of file
<template>
<div class="map-contain">
<canvas id="map" ref="root"></canvas>
<img src="/image/compass.png" class="compass" ref="compass"
:style="`transform: rotateZ(${compassRotationAngle}rad)`" alt="" @click="changeCompass">
<div class="navigate-preview">
<img src="/image/navigate.png" class="navigate" @click="changeNavigate" alt="" />
</div>
</div>
</template>
<script setup>
import { markRaw, onMounted, watch, ref, computed, onBeforeUnmount, nextTick } from 'vue'
import { useMapStore } from '@/store/MapStore.js'
import { useVehicleStore } from '@/store/VehicleStore'
import { storeToRefs } from 'pinia'
import { vehicleScale } from '../js/tools'
import DeckNew from '@/tools/Deck';
import * as turf from '@turf/turf'
const safe_color = '#FFA500'
const BASE_RAD = 1.5707963267948966
const MAP_ZOOM = 17
const LAYER_PROPOITY = {
runablearea: 1,
staticobjs: 2,
diggingarea: 3,
diggingworkarea: 3,
dumparea: 4,
barricade: 5,
electronicFence: 6,
wetArea: 7,
lane: 8,
obstacles: 9,
stationarea: 10,
parkspot: 11,
lanenode: 12,
currPathinfo: 13,
nextPathinfo: 14,
avoidancePath: 15,
traj: 16,
obstacle: 17
}
const vehicles = {}
let ismousedown = false
let ismousemove = false
let resetTime = null
let obstaclePolygons = []
let dynamicPaths = []
const reNavigateTime = 2.5 * 1000
const props = defineProps({
isShowBasic: Boolean,
clearSinglePointEnable: Boolean,
reloadData: Boolean
})
let map = markRaw({})
let currentPosition = markRaw([])
const root = ref(null)
const mapLayers = markRaw({})
const safeObjects = markRaw({})
const safeTrajs = markRaw({})
const singlePaths = markRaw([])
const navigate = ref(true)
const currentRotation = ref(0)
const markerLayerGroupLists = markRaw({})
const isSinglePreview = ref(false)
const compassRotationAngle = ref(0)
const compass = ref(null)
const mapStore = useMapStore()
const mapStoreToRefs = storeToRefs(mapStore)
const {
lanenode,
lane,
runablearea,
centerline,
obstacles,
diggingworkarea,
dumparea,
barricade,
stationarea,
parkspot,
staticobjs,
electronicFence,
morepoint, // 单机循迹单路径
morepath, // 单机循迹多路径
nextPathinfo,
currPathinfo,
nextPathAvailable,
avoidancePath, // 绕障路径
obstacleData,
dynamicPathinfo, // 车辆动态路权信息
} = mapStoreToRefs
const vehicleStore = useVehicleStore()
const vehicleStoreToRefs = storeToRefs(vehicleStore)
const { vehiclesPositioninfo, safeObj, trajObj, paraminfo, deleteVehicles } = vehicleStoreToRefs
const deviceId = computed(() => {
return paraminfo.value.deviceId
})
const emits = defineEmits(['handleSend'])
onMounted(() => {
//阻止鼠标右键
document.oncontextmenu = f => {
event.returnValue = false
}
let currentCoordinate = []
let bool = false
map = new DeckNew({
initialViewState: {
latitude: document.location.mapCenter[0],
longitude: document.location.mapCenter[1],
zoom: MAP_ZOOM,
// pickingRadius:5,
controller: true,
},
controller: true,
// parent: document.getElementById('mapMain'),
canvas: 'map',
//如果deck滚动条滚动或者拖动地图时,不进行地图角度和等级矫正
onInteractionStateChange: f => {
if (!currentPosition) return
if (f.isZooming || f.isDragging) {
navigate.value = false
clearTimeout(resetTime)
resetTime = window.setTimeout(() => {
if (!navigate.value) {
navigate.value = true
dealVehicleRotate()
currentRotation.value = BASE_RAD
clearTimeout(resetTime)
resetTime = null;
}
}, reNavigateTime)
}
}
})
map.setPitch(40)
emits('handleSend', {
type: 'syncMapVersion',
msg: {}
})
})
function changeNavigate() {
navigate.value = true
dealMapRotate(currentRotation.value, currentPosition, map.getZoom())
}
// 指南针
function changeCompass() {
dealVehicleRotate(0)
currentRotation.value = BASE_RAD
if (navigate.value) {
setTimeout(() => {
dealVehicleRotate()
}, 3 * 1000)
}
}
function dealVehicleRotate(rotate) {
let keys = Object.keys(vehicles)
let target = keys.find(item => item === deviceId.value)
let currentTarget = vehicles[target]
let vehicleHeading = BASE_RAD
let currentOldHeading = currentTarget && currentTarget.options.vehicleHeading
if (typeof rotate === 'number') {
dealMapRotate(0, currentPosition, map.getZoom())
} else {
vehicleHeading = currentOldHeading
currentRotation.value = vehicleHeading
dealMapRotate(vehicleHeading, currentPosition, map.getZoom())
}
}
function dealMapLayer(value, color, layerName) {
if (!value || !map) return
let result = JSON.parse(value)
if (!result) return
let attr = {};
if (layerName === 'barricade') {
attr = {
getLineColor: map.convertColor('#0066ff', 1),
// width: 5
}
} else {
attr = {
getLineColor: feature => {
return layerName === 'lane' ? map.convertColor('#eee', 0.5) : map.convertColor(feature.properties.color, 1)
},
getFillColor: feature => {
let opacity = 0.7;
if (layerName === 'runablearea') {
opacity = 0.5
}
return layerName === 'electronicFence' ? map.convertColor(feature.properties.color, opacity) : color ? map.convertColor(color, opacity) : map.convertColor(feature.properties.color, opacity)
}
}
}
map.createGeoJsonLayer(result, attr, layerName)
mapStore.setData(layerName, null)
}
function dealMapPath(value, layerName, color) {
if (value && value.length) {
let data = JSON.parse(JSON.stringify(value))
let path = map.createPathLayer([
{
path: [...map.coorReverse(data)],
name: layerName,
}
], {
getColor: f => map.convertColor('#ffff00'),
}, layerName)
mapLayers[layerName] = layerName
const lineGeo = turf.lineString([...map.coorReverse(JSON.parse(JSON.stringify(data)))])
let bufferLine = turf.buffer(lineGeo, 6.5, { units: "meters" })
const obj = {
type: "FeatureCollection",
features: [bufferLine]
}
const attr = {
getFillColor: f => map.convertColor(color, 0.6),
getLineColor: f => map.convertColor(color, 1)
}
// map.setLayerSortName('bufferLayer', layerName + 'Buffer')
map.createGeoJsonLayer(obj, attr, layerName + 'Buffer')
mapLayers[`${layerName}Buffer`] = `${layerName}Buffer`
} else {
// console.log(map)
value != null && map.removeLayer('map', layerName)
value != null && map.removeLayer('map', layerName + 'Buffer')
}
}
function dealDivIcon(value, layerName) {
if (!value || !map) return
let result = JSON.parse(value)
if (!result) return
const { features } = result
for (let i = 0; i < features.length; i++) {
let feature = features[i]
let { name, id } = feature.properties
let { coordinates } = feature.geometry
let markerCoor = null
if (layerName === 'lanenode') {
markerCoor = [coordinates[1], coordinates[0]]
} else {
let center = turf.center(feature)
markerCoor = [center.geometry.coordinates[1], center.geometry.coordinates[0]]
}
let texData = {
name: name,
id: id,
coordinates: [markerCoor[1], markerCoor[0]],
}
if (markerLayerGroupLists[`${layerName}${id}`]) {
markerLayerGroupLists[`${layerName}${id}`] = texData
} else {
markerLayerGroupLists[`${layerName}${id}`] = texData
}
}
map.createTextLayer([...Object.values(markerLayerGroupLists)], {
getSize: 15
}, 'text')
// map.setLayerSortName('marker', 'marker')
map.setLayerSortName('markerLayer', 'marker')
map.creatCircle(Object.values(markerLayerGroupLists), {
getFillColor: f => map.convertColor('#2aa1fa', 1),
getLineColor: f => map.convertColor('#2aa1fa', 0.5),
getLineWidth: 0.5,
getRadius: 1.5
}, 'marker')
mapStore.setData(layerName, null)
}
function removeLayer(obj, vehicleID) {
if (obj[vehicleID]) {
for (let item in obj[vehicleID]) {
map.removeLayer('map', obj[vehicleID][item])
}
}
}
function dealSafe(vehicleID, safe) {
if (!map) return
const { enable, data, dealedData, vehicleType, vehicleLatitude, vehicleLongtitude } = safe
removeLayer(safeObjects, vehicleID)
safeObjects[vehicleID] = {}
if (!enable) return
let safeType = [1, 20, 21].includes(vehicleType) ? 'circle' : 'rectangle'
let zIndex = 16
for (let i = 0; i < dealedData.length; i++) {
let item = JSON.parse(JSON.stringify(dealedData[i]))
map.setLayerSortName('polygonLayer', vehicleID + i)
if (safeType === 'rectangle') {
map.creatPolygon({
coordinates: map.coorReverse(item),
}, {
getFillColor: d => map.convertColor(safe_color, 0.4),
getLineColor: d => map.convertColor(safe_color),
getLineWidth: 0.4
}, vehicleID + i)
} else {
map.creatCircle({ coordinates: [vehicleLongtitude, vehicleLatitude] }, {
getFillColor: f => map.convertColor(safe_color, 0.4),
getLineColor: f => map.convertColor(safe_color),
getLineWidth: 0.4,
getRadius: data[i]
}, vehicleID + i)
}
safeObjects[vehicleID][i] = vehicleID + i
}
}
function dealTraj(vehicleID, traj) {
if (!traj || !map) return
const { trajPathWid, radius, dealedData, vehicleType, vehicleLatitude, vehicleLongtitude } = traj
removeLayer(safeTrajs, vehicleID)
safeTrajs[vehicleID] = {}
// if (!dealedData) return
// if ((dealedData && !dealedData.length) || !radius)) return
if ([1, 20, 21].includes(vehicleType && !radius)) return;
const zIndex = LAYER_PROPOITY['traj']
if ([1, 20, 21].includes(vehicleType) && radius) {
map.creatCircle({ coordinates: [vehicleLongtitude, vehicleLatitude] }, {
getFillColor: f => map.convertColor('#FFA500'),
getLineColor: f => map.convertColor('#FFA500'),
getLineWidth: 1,
getRadius: radius
}, vehicleID)
safeTrajs[vehicleID]['circle'] = vehicleID
} else {
if (dealedData && !dealedData.length) return;
if (dealedData == undefined) return;
const lineGeo = turf.lineString([...map.coorReverse(JSON.parse(JSON.stringify(dealedData)))])
// safeTrajs[vehicleID]['path'] = path
let bufferLine = turf.buffer(lineGeo, trajPathWid / 2, { units: "meters" })
const data = {
type: "FeatureCollection",
features: [bufferLine]
}
const attr = {
getFillColor: f => map.convertColor(safe_color, 0.4),
getLineColor: f => map.convertColor(safe_color, 1)
}
safeTrajs[vehicleID]['pathBuffer'] = vehicleID + 'Buffer'
map.setLayerSortName('bufferLayer', vehicleID + 'Buffer', 1)
map.createGeoJsonLayer(data, attr, vehicleID + 'Buffer')
}
}
function dealMapRotate(rotationAngle, center, zoom) {
let rotate = 0
if (rotationAngle) {
rotate = turf.radiansToDegrees(rotationAngle - BASE_RAD)
}
if (compass.value) {
compassRotationAngle.value = rotationAngle
map.setRotation(rotate)
}
center && center.length && map.flyTo(center, zoom, { animate: false })
}
function dealVehiclePosition(value) {
if (!map || !value.length) return
let zoom = map.getZoom()
for (let i = 0; i < value.length; i++) {
const item = value[i]
const { vehicleLatitude, vehicleLongtitude, vehicleHeading, vehicleID, image } = item
let vehiclePosition = [vehicleLatitude, vehicleLongtitude]
let baseObj = {
name: vehicleID,
rotate: vehicleHeading,
coordinates: [vehicleLongtitude, vehicleLatitude],
image: image,
options: {
vehicleHeading: vehicleHeading
}
}
if (vehicles[vehicleID]) {
vehicles[vehicleID] = baseObj
} else {
vehicles[vehicleID] = baseObj
}
if (vehicleID === deviceId.value) {
currentPosition = vehiclePosition
if (navigate.value) {
currentRotation.value = vehicleHeading
dealMapRotate(vehicleHeading, vehiclePosition, zoom <= MAP_ZOOM ? MAP_ZOOM : zoom)
}
}
}
initCar()
}
function initCar() {
map.clearLayer('car')
const nameText = JSON.parse(JSON.stringify(Object.values(vehicles))).map(item => {
item.coordinates = [item.coordinates[0], item.coordinates[1], 10]
return item
})
map.createTextLayer(nameText, {
getSize: 15,
getColor: f => [255, 255, 255, 255],
fontWeight: 400,
fontSettings: {
buffer: 2,
sdf: true,
smoothing: 0.5
},
}, 'carName')
let car = {};
Object.values(vehicles).forEach(item => {
if (car.hasOwnProperty(item.image)) {
car[item.image].push(item)
} else {
car[item.image] = [item]
}
})
for (let key in car) {
// map.setLayerSortName('carLayer', key)
let imageURL = `image/${key}.glb`
map.createCarModel(car[key], { scenegraph: imageURL }, key)
}
}
function dealSingleDatas(datas) {
const { color, coor } = datas
let data = JSON.parse(JSON.stringify(value))
const lineGeo = turf.lineString([...map.coorReverse(JSON.parse(JSON.stringify(data)))])
// safeTrajs[vehicleID]['path'] = path
let bufferLine = turf.buffer(lineGeo, 2, { units: "meters" })
const obj = {
type: "FeatureCollection",
features: [bufferLine]
}
const attr = {
getFillColor: f => map.convertColor(color, 0.4),
getLineColor: f => map.convertColor(color, 1)
}
map.createGeoJsonLayer(obj, attr, layerName)
singlePaths.push(layerName)
}
function dealObtacleData(value) {
if (value && value.length) {
for (let i = 0; i < value.length; i++) {
let obstacle = value[i]
let { typeColor, coordinate, centerLon, centerLat, centerRadius, centerColor } = obstacle
map.creatPolygon({
coordinates: JSON.parse(JSON.stringify(coordinate)),
}, {
getFillColor: d => map.convertColor(typeColor),
getLineColor: d => map.convertColor(typeColor),
getLineWidth: 3
}, 'obstacle')
map.setLayerSortName('buffer', 'obstacleBuffer')
map.creatCircle({ coordinates: [centerLon, centerLat] }, {
getFillColor: f => map.convertColor(centerColor, 0.5),
getLineColor: f => map.convertColor(centerColor, 0.5),
getLineWidth: 0,
getRadius: centerRadius
}, 'obstacleBuffer')
}
}
}
function dealDynamicDatas(datas) {
let name = 'dynamicLine';
const { color, path } = datas
const lineGeo = turf.lineString([...map.coorReverse(JSON.parse(JSON.stringify(path)))])
// safeTrajs[vehicleID]['path'] = path
let bufferLine = turf.buffer(lineGeo, 2, { units: "meters" })
const obj = {
type: "FeatureCollection",
features: [bufferLine]
}
const attr = {
// getFillColor: f => map.convertColor(color, 0.3),
getLineColor: f => map.convertColor(color, 0.3),
getLineWidth: 30
}
map.createGeoJsonLayer(obj, attr, name)
}
// 直接监听props.isShowBasic 不会触发
watch(() => props.isShowBasic, () => {
nextTick(() => {
// map.invalidateSize(true)
})
})
watch(lanenode, (value) => {
dealDivIcon(value, 'lanenode', '#ff0000')
}, { immediate: true, deep: true })
watch(lane, (value) => {
dealMapLayer(value, '#fff', 'lane')
}, { immediate: true, deep: true })
watch(runablearea, (value) => {
dealMapLayer(value, '#98a0a0', 'runablearea')
}, { immediate: true, deep: true })
watch(centerline, (value) => {
dealMapLayer(value, '#eee', 'centerline')
}, { immediate: true, deep: true })
watch(obstacles, (value) => {
dealDivIcon(value, 'obstacles')
dealMapLayer(value, '#6f9bdd', 'obstacles')
}, { immediate: true, deep: true })
watch(diggingworkarea, (value) => {
dealDivIcon(value, 'diggingworkarea')
dealMapLayer(value, '#886e26', 'diggingworkarea')
}, { immediate: true, deep: true })
watch(dumparea, (value) => {
dealDivIcon(value, 'dumparea')
dealMapLayer(value, '#886e26', 'dumparea')
}, { immediate: true, deep: true })
watch(barricade, (value) => {
dealMapLayer(value, '#0000ff', 'barricade')
}, { immediate: true, deep: true })
watch(stationarea, (value) => {
dealDivIcon(value, 'stationarea')
dealMapLayer(value, '#90ee90', 'stationarea')
}, { immediate: true, deep: true })
watch(parkspot, (value) => {
dealDivIcon(value, 'parkspot')
dealMapLayer(value, '#7e8185', 'parkspot')
}, { immediate: true, deep: true })
watch(staticobjs, (value) => {
dealMapLayer(value, '#98a0a0', 'staticobjs')
}, { immediate: true, deep: true })
watch(electronicFence, (value) => {
dealDivIcon(value, 'electronicFence')
dealMapLayer(value, '#d51d20', 'electronicFence')
}, { immediate: true, deep: true })
watch(nextPathinfo, (value) => {
dealMapPath(value, 'nextPath', nextPathAvailable.value === 1 ? '#19ae19' : '#8B4513')
}, { immediate: true, deep: true })
watch(currPathinfo, (value) => {
dealMapPath(value, 'currentPath', '#19ae19')
}, { immediate: true, deep: true })
watch(avoidancePath, (value) => {
dealMapPath(value, 'avoidancePath', 'blue')
}, { immediate: true, deep: true })
watch(vehiclesPositioninfo, (value) => {
dealVehiclePosition(value)
}, {
immediate: true,
deep: true
})
watch(safeObj, (value) => {
Object.keys(value).forEach(item => {
dealSafe(item, value[item])
})
}, {
immediate: true,
deep: true
})
watch(trajObj, (value) => {
Object.keys(value).forEach(item => {
dealTraj(item, value[item])
})
}, {
immediate: true,
deep: true
})
watch(obstacleData, (value) => {
dealObtacleData(value)
}, { immediate: true, deep: true })
watch(deleteVehicles, (data) => {
if (!map) return
if (data && data.length) {
data.forEach(item => {
// map.removeLayer(vehicles[item])
removeLayer(safeTrajs, item)
removeLayer(safeObjects, item)
delete vehicles[item]
initCar()
})
}
}, {
immediate: true
})
watch(() => props.clearSinglePointEnable, (enable) => {
if (!enable) return
isSinglePreview.value = true
singlePaths.forEach(item => map.removeLayer('map', item))
}, { deep: true })
watch([morepoint, morepath], (pointData, pathData) => {
if (isSinglePreview.value && (pointData || pathData)) {
let datas = pointData.concat(pathData)
for (let i = 0; i < datas.length; i++) {
dealSingleDatas(datas[i])
}
}
}, { deep: true })
watch(dynamicPathinfo, (pathData) => {
if (pathData.length > 0) {
for (let i = 0; i < pathData.length; i++) {
dealDynamicDatas(pathData[i])
}
}
}, { deep: true })
watch(deviceId, (value) => {
if (value && map) {
dealMapRotate(currentRotation.value, currentPosition, map.getZoom())
}
}, { deep: true })
function doUnmount() {
// Object.keys(mapLayers).map(layer => map.removeLayer(mapLayers[layer]))
map.remove()
map = null
}
watch(() => props.reloadData, (value) => {
if (value) {
doUnmount()
}
})
onBeforeUnmount(() => {
doUnmount()
})
</script>
<style lang="less" scope>
.map-contain {
height: 100%;
overflow: hidden;
}
#map {
width: 200% !important;
height: 200% !important;
top: -50%;
left: -50%;
background: #171A23;
position: relative !important;
}
.navigate-preview {
background: #4A75A9;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.25);
border-radius: 10px;
opacity: 1;
display: flex;
align-items: center;
justify-content: center;
}
.compass,
.navigate-preview {
position: absolute;
width: 4vw;
height: 4vw;
z-index: 999;
}
.compass {
bottom: 88px;
max-width: 60px;
max-height: 60px;
}
.navigate-preview {
bottom: 24px;
max-width: 60px;
max-height: 60px;
}
</style>
<style lang="less">
.icon-node {
width: 120px !important;
white-space: nowrap;
color: #676565;
// padding-left: -2em !important;
z-index: 1;
font-size: 12px !important;
font-family: "Open Sans Regular", "Arial Unicode MS Regular", sans-serif;
text-transform: uppercase;
text-align: center;
-webkit-text-stroke-display: block;
-webkit-text-stroke-width: 1px;
-webkit-text-stroke-color: rgba(33, 33, 33, 0.8);
}
@media screen and (min-width: 1700px) {
.icon-node {
font-size: 18px !important;
}
}
.icon-node-name {
display: inline-block;
transform: scale(.6);
padding-left: -20px !important;
}
.hide {
display: none !important;
}
.middle-line {
// 为svg设置虚线,数字越大,虚线段越长,
// 设置2个参数,参数1: 虚线的长度 参数2: 虚线和虚线之间的间隔, 如果只设置一个参数,说明两个值一样大小
stroke-dasharray: 15 20;
z-index: 999;
}
.leaflet-tooltip {
color: #fff !important;
background-color: rgba(255, 255, 255, 0) !important;
border: 0 !important;
box-shadow: rgba(0, 0, 0, 0);
font-size: 12px !important;
}
.leaflet-control-attribution {
display: none !important;
}
</style>
\ No newline at end of file
......@@ -238,8 +238,8 @@ function dealMapPath(value, layerName, color) {
mapLayers[`${layerName}Buffer`] = `${layerName}Buffer`
} else {
// console.log(map)
value!=null&&map.removeLayer('map', layerName)
value!=null&&map.removeLayer('map', layerName + 'Buffer')
value != null && map.removeLayer('map', layerName)
value != null && map.removeLayer('map', layerName + 'Buffer')
}
}
......@@ -319,7 +319,7 @@ function dealSafe(vehicleID, safe) {
getLineWidth: 0.4
}, vehicleID + i)
} else {
map.creatCircle({coordinates:[vehicleLongtitude, vehicleLatitude]}, {
map.creatCircle({ coordinates: [vehicleLongtitude, vehicleLatitude] }, {
getFillColor: f => map.convertColor(safe_color, 0.4),
getLineColor: f => map.convertColor(safe_color),
getLineWidth: 0.4,
......@@ -341,7 +341,7 @@ function dealTraj(vehicleID, traj) {
const zIndex = LAYER_PROPOITY['traj']
if ([1, 20, 21].includes(vehicleType) && radius) {
map.creatCircle({coordinates:[vehicleLongtitude, vehicleLatitude]}, {
map.creatCircle({ coordinates: [vehicleLongtitude, vehicleLatitude] }, {
getFillColor: f => map.convertColor('#FFA500'),
getLineColor: f => map.convertColor('#FFA500'),
getLineWidth: 1,
......@@ -350,6 +350,7 @@ function dealTraj(vehicleID, traj) {
safeTrajs[vehicleID]['circle'] = vehicleID
} else {
if (dealedData && !dealedData.length) return;
if (dealedData == undefined) return;
const lineGeo = turf.lineString([...map.coorReverse(JSON.parse(JSON.stringify(dealedData)))])
// safeTrajs[vehicleID]['path'] = path
let bufferLine = turf.buffer(lineGeo, trajPathWid / 2, { units: "meters" })
......@@ -481,7 +482,7 @@ function dealObtacleData(value) {
}, 'obstacle')
map.setLayerSortName('buffer', 'obstacleBuffer')
map.creatCircle({coordinates:[centerLon, centerLat]}, {
map.creatCircle({ coordinates: [centerLon, centerLat] }, {
getFillColor: f => map.convertColor(centerColor, 0.5),
getLineColor: f => map.convertColor(centerColor, 0.5),
getLineWidth: 0,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment