From 31ad252341e3614daab677a9cf2e37f62489484e Mon Sep 17 00:00:00 2001 From: "sean.zhou" Date: Fri, 18 Nov 2022 18:35:12 +0800 Subject: [PATCH] initial v1.3.0 --- package.json | 3 + src/App.vue | 10 +- src/api/device-cmd/index.ts | 12 +- src/api/device-setting/index.ts | 24 ++ src/api/manage.ts | 6 + src/api/wayline.ts | 74 ++++- src/components/GMap.vue | 26 +- src/components/TaskPanel.vue | 182 ---------- .../device-log/DeviceLogDetailModal.vue | 4 +- src/components/g-map/DeviceSettingBox.vue | 242 ++++++++++++++ src/components/g-map/DeviceSettingPopover.vue | 106 ++++++ src/components/g-map/DockControlPanel.vue | 102 ++++-- src/components/g-map/useDeviceSetting.ts | 56 ++++ src/components/g-map/useDockControl.ts | 16 +- src/components/livestream-agora.vue | 176 ++++++---- src/components/livestream-others.vue | 175 ++++++---- .../task/CreatePlan.vue} | 135 ++++++-- src/components/task/TaskPanel.vue | 249 ++++++++++++++ src/components/task/use-format-task.ts | 35 ++ .../task/use-task-progress-event.ts | 19 ++ src/components/wayline-panel.vue | 123 ------- src/event-bus/index.ts | 5 +- src/hooks/use-g-map-tsa.ts | 25 +- src/pages/page-pilot/pilot-home.vue | 12 +- src/pages/page-web/projects/dock.vue | 2 +- src/pages/page-web/projects/task.vue | 28 +- src/pages/page-web/projects/wayline.vue | 78 ++++- src/pages/page-web/projects/workspace.vue | 57 ++-- src/router/index.ts | 2 +- src/store/index.ts | 37 +-- src/types/airport-tsa.ts | 24 ++ src/types/device-cmd.ts | 63 ++++ src/types/device-setting.ts | 148 +++++++++ src/types/device.ts | 19 +- src/types/enums.ts | 2 +- src/types/task.ts | 97 ++++++ src/types/wayline.ts | 29 +- src/utils/device-cmd.ts | 126 ++++++- src/utils/device-setting.ts | 193 +++++++++++ src/utils/error-code/index.ts | 310 ++++++++++++++++++ 40 files changed, 2385 insertions(+), 647 deletions(-) create mode 100644 src/api/device-setting/index.ts delete mode 100644 src/components/TaskPanel.vue create mode 100644 src/components/g-map/DeviceSettingBox.vue create mode 100644 src/components/g-map/DeviceSettingPopover.vue create mode 100644 src/components/g-map/useDeviceSetting.ts rename src/{pages/page-web/projects/create-plan.vue => components/task/CreatePlan.vue} (63%) create mode 100644 src/components/task/TaskPanel.vue create mode 100644 src/components/task/use-format-task.ts create mode 100644 src/components/task/use-task-progress-event.ts delete mode 100644 src/components/wayline-panel.vue create mode 100644 src/types/device-setting.ts create mode 100644 src/types/task.ts create mode 100644 src/utils/device-setting.ts create mode 100644 src/utils/error-code/index.ts diff --git a/package.json b/package.json index 2cf085e..2b5640f 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "ant-design-vue/es/empty/style/css", "ant-design-vue/es/form/style/css", "ant-design-vue/es/image/style/css", + "ant-design-vue/es/input-number/style/css", "ant-design-vue/es/input/style/css", "ant-design-vue/es/layout/style/css", "ant-design-vue/es/menu/style/css", @@ -94,7 +95,9 @@ "ant-design-vue/es/tag/style/css", "ant-design-vue/es/tooltip/style/css", "ant-design-vue/es/tree/style/css", + "ant-design-vue/es/upload/style/css", "axios", + "lodash", "mitt", "moment", "reconnecting-websocket", diff --git a/src/App.vue b/src/App.vue index ef1496f..453e2a5 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,5 +1,5 @@ - - - - diff --git a/src/components/devices/device-log/DeviceLogDetailModal.vue b/src/components/devices/device-log/DeviceLogDetailModal.vue index c203ce3..9f36302 100644 --- a/src/components/devices/device-log/DeviceLogDetailModal.vue +++ b/src/components/devices/device-log/DeviceLogDetailModal.vue @@ -8,7 +8,7 @@
- + 下载机场日志
- + 下载飞行器日志 +
+
设备属性设置
+
+ +
+
+
{{ deviceSetting[DeviceSettingKeyEnum.NIGHT_LIGHTS_MODE_SET].label }}
+
{{ deviceSetting[DeviceSettingKeyEnum.NIGHT_LIGHTS_MODE_SET].value }}
+
+
+ + + Edit + +
+
+ +
+
+
{{ deviceSetting[DeviceSettingKeyEnum.HEIGHT_LIMIT_SET].label }}
+
{{ deviceSetting[DeviceSettingKeyEnum.HEIGHT_LIMIT_SET].value }}
+
+
+ + + Edit + +
+
+ +
+
+
{{ deviceSetting[DeviceSettingKeyEnum.DISTANCE_LIMIT_SET].label }}
+
{{ deviceSetting[DeviceSettingKeyEnum.DISTANCE_LIMIT_SET].value }}
+
+
+ + + Edit + +
+
+ +
+
+
{{ deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_HORIZON].label }}
+
{{ deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_HORIZON].value }}
+
+
+ + + Edit + +
+
+ +
+
+
{{ deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_UPSIDE].label }}
+
{{ deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_UPSIDE].value }}
+
+
+ + + Edit + +
+
+ +
+
+
{{ deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_DOWNSIDE].label }}
+
{{ deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_DOWNSIDE].value }}
+
+
+ + + Edit + +
+
+
+
+ + + + + diff --git a/src/components/g-map/DeviceSettingPopover.vue b/src/components/g-map/DeviceSettingPopover.vue new file mode 100644 index 0000000..72e0b0f --- /dev/null +++ b/src/components/g-map/DeviceSettingPopover.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/src/components/g-map/DockControlPanel.vue b/src/components/g-map/DockControlPanel.vue index 284d9ca..645ee20 100644 --- a/src/components/g-map/DockControlPanel.vue +++ b/src/components/g-map/DockControlPanel.vue @@ -2,24 +2,32 @@
- 远程调试 {{ props.sn}} + 设备操作 {{ props.sn}}
+ +
-
-
-
{{ cmdItem.label }}
-
{{ cmdItem.status }}
-
-
- - {{ cmdItem.operateText }} - -
-
+
+ 远程调试 + +
+
+
+
+
{{ cmdItem.label }}
+
{{ cmdItem.status }}
+
+
+ + {{ cmdItem.operateText }} + +
+
+
@@ -35,6 +43,7 @@ import { DeviceInfoType } from '/@/types/device' import { cmdList as baseCmdList, DeviceCmdItem } from '/@/types/device-cmd' import { useMyStore } from '/@/store' import { updateDeviceCmdInfoByOsd, updateDeviceCmdInfoByExecuteInfo } from '/@/utils/device-cmd' +import DeviceSettingBox from './DeviceSettingBox.vue' const props = defineProps<{ sn: string, @@ -71,14 +80,34 @@ function closeControlPanel () { } // dock 控制指令 +const debugStatus = ref(false) + +async function onDeviceStatusChange (status: boolean) { + let result = false + if (status) { + result = await dockDebugOnOff(props.sn, true) + } else { + result = await dockDebugOnOff(props.sn, false) + } + if (!result) { + if (status) { + debugStatus.value = false + } else { + debugStatus.value = true + } + } +} + const { sendDockControlCmd, + dockDebugOnOff } = useDockControl() async function sendControlCmd (cmdItem: DeviceCmdItem, index: number) { const success = await sendDockControlCmd({ sn: props.sn, - cmd: cmdItem.cmdKey + cmd: cmdItem.cmdKey, + action: cmdItem.action }, true) if (success) { // updateDeviceSingleCmdInfo(cmdList.value[index]) @@ -103,26 +132,39 @@ async function sendControlCmd (cmdItem: DeviceCmdItem, index: number) { } .control-cmd-wrapper{ - display: flex; - flex-wrap: wrap; - justify-content: space-between; - padding: 4px 10px; - .control-cmd-item{ - width: 220px; - height: 58px; + .control-cmd-header{ + font-size: 14px; + font-weight: 600; + padding: 10px 10px 0px; + + .debug-btn{ + margin-left: 10px; + border:1px solid #585858; + } + } + + .control-cmd-box{ display: flex; - align-items: center; + flex-wrap: wrap; justify-content: space-between; - border: 1px solid #666; - margin: 4px 0; - padding: 0 8px; - - .control-cmd-item-left{ + padding: 4px 10px; + .control-cmd-item{ + width: 220px; + height: 58px; display: flex; - flex-direction: column; - - .item-label{ - font-weight: 700; + align-items: center; + justify-content: space-between; + border: 1px solid #666; + margin: 4px 0; + padding: 0 8px; + + .control-cmd-item-left{ + display: flex; + flex-direction: column; + + .item-label{ + font-weight: 700; + } } } } diff --git a/src/components/g-map/useDeviceSetting.ts b/src/components/g-map/useDeviceSetting.ts new file mode 100644 index 0000000..e2cea30 --- /dev/null +++ b/src/components/g-map/useDeviceSetting.ts @@ -0,0 +1,56 @@ +import { message } from 'ant-design-vue' +import { putDeviceProps, PutDevicePropsBody } from '/@/api/device-setting' +import { DeviceSettingKeyEnum, DeviceSettingFormModel, ObstacleAvoidanceStatusEnum, NightLightsStateEnum, DistanceLimitStatusEnum } from '/@/types/device-setting' + +export function useDeviceSetting () { + // 生成参数 + function genDevicePropsBySettingKey (key: DeviceSettingKeyEnum, fromModel: DeviceSettingFormModel) { + const body = {} as PutDevicePropsBody + if (key === DeviceSettingKeyEnum.NIGHT_LIGHTS_MODE_SET) { + body.night_lights_state = fromModel.nightLightsState ? NightLightsStateEnum.OPEN : NightLightsStateEnum.CLOSE + } else if (key === DeviceSettingKeyEnum.HEIGHT_LIMIT_SET) { + body.height_limit = fromModel.heightLimit + } else if (key === DeviceSettingKeyEnum.DISTANCE_LIMIT_SET) { + body.distance_limit_status = {} + if (fromModel.distanceLimitStatus.state) { + body.distance_limit_status.state = DistanceLimitStatusEnum.SET + body.distance_limit_status.distance_limit = fromModel.distanceLimitStatus.distanceLimit + } else { + body.distance_limit_status.state = DistanceLimitStatusEnum.UNSET + } + } else if (key === DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_HORIZON) { + body.obstacle_avoidance = { + horizon: fromModel.obstacleAvoidanceHorizon ? ObstacleAvoidanceStatusEnum.OPEN : ObstacleAvoidanceStatusEnum.CLOSE + } + } else if (key === DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_UPSIDE) { + body.obstacle_avoidance = { + upside: fromModel.obstacleAvoidanceUpside ? ObstacleAvoidanceStatusEnum.OPEN : ObstacleAvoidanceStatusEnum.CLOSE + } + } else if (key === DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_DOWNSIDE) { + body.obstacle_avoidance = { + downside: fromModel.obstacleAvoidanceDownside ? ObstacleAvoidanceStatusEnum.OPEN : ObstacleAvoidanceStatusEnum.CLOSE + } + } + return body + } + + // 设置设备属性 + async function setDeviceProps (sn: string, body: PutDevicePropsBody) { + try { + const { code, message: msg } = await putDeviceProps(sn, body) + if (code === 0) { + // message.success('指令发送成功') + return true + } + throw (msg) + } catch (e) { + message.error('设备属性设置失败') + return false + } + } + + return { + genDevicePropsBySettingKey, + setDeviceProps + } +} diff --git a/src/components/g-map/useDockControl.ts b/src/components/g-map/useDockControl.ts index 94e302f..5e8c38c 100644 --- a/src/components/g-map/useDockControl.ts +++ b/src/components/g-map/useDockControl.ts @@ -1,7 +1,7 @@ import { message } from 'ant-design-vue' import { ref } from 'vue' import { postSendCmd } from '/@/api/device-cmd' -import { DeviceCmd } from '/@/types/device-cmd' +import { DeviceCmd, DeviceCmdItemAction } from '/@/types/device-cmd' export function useDockControl () { const controlPanelVisible = ref(false) @@ -12,22 +12,30 @@ export function useDockControl () { // 远程调试开关 async function dockDebugOnOff (sn: string, on: boolean) { - const success = await sendDockControlCmd({ + const result = await sendDockControlCmd({ sn: sn, cmd: on ? DeviceCmd.DebugModeOpen : DeviceCmd.DebugModeClose }, false) - if (success) { + if (result) { setControlPanelVisible(on) } + return result } // 发送指令 async function sendDockControlCmd (params: { sn: string, cmd: DeviceCmd + action?: DeviceCmdItemAction }, tip = true) { try { - const { code, message: msg } = await postSendCmd({ dock_sn: params.sn, device_cmd: params.cmd }) + let body = undefined as any + if (params.action !== undefined) { + body = { + action: params.action + } + } + const { code, message: msg } = await postSendCmd({ dock_sn: params.sn, device_cmd: params.cmd }, body) if (code === 0) { tip && message.success('指令发送成功') return true diff --git a/src/components/livestream-agora.vue b/src/components/livestream-agora.vue index 41463f3..b1c0d05 100644 --- a/src/components/livestream-agora.vue +++ b/src/components/livestream-agora.vue @@ -3,15 +3,23 @@

Live streaming source selection

+ +
+ AppId: + Token: + Channel:
- Play + Switch Lens + Play Stop Update Clarity - Refresh Live Capacity
@@ -91,8 +105,9 @@ import AgoraRTC, { IAgoraRTCClient, IAgoraRTCRemoteUser } from 'agora-rtc-sdk-ng' import { message } from 'ant-design-vue' import { onMounted, reactive } from 'vue' +import { uuidv4 } from '../utils/uuid' import { CURRENT_CONFIG as config } from '/@/api/http/config' -import { getLiveCapacity, setLivestreamQuality, startLivestream, stopLivestream } from '/@/api/manage' +import { changeLivestreamLens, getLiveCapacity, setLivestreamQuality, startLivestream, stopLivestream } from '/@/api/manage' import { getRoot } from '/@/root' const root = getRoot() @@ -120,6 +135,12 @@ const clarityList = [ } ] +interface SelectOption { + value: any, + label: string, + more?: any +} + let agoraClient = {} as IAgoraRTCClient const agoraPara = reactive({ appid: config.agoraAPPID, @@ -130,13 +151,16 @@ const agoraPara = reactive({ }) const dronePara = reactive({ livestreamSource: [], - droneList: [] as any[], - cameraList: [] as any[], - videoList: [] as any[], - droneSelected: '', - cameraSelected: '', - videoSelected: '', - claritySelected: 0 + droneList: [] as SelectOption[], + cameraList: [] as SelectOption[], + videoList: [] as SelectOption[], + droneSelected: undefined as string | undefined, + cameraSelected: undefined as string | undefined, + videoSelected: undefined as string | undefined, + claritySelected: 0, + lensList: [] as string[], + lensSelected: undefined as string | undefined, + isDockLive: false }) const livePara = reactive({ url: '', @@ -144,14 +168,15 @@ const livePara = reactive({ videoId: '', liveState: false }) +const nonSwitchable = 'normal' const onRefresh = async () => { dronePara.droneList = [] dronePara.cameraList = [] dronePara.videoList = [] - dronePara.droneSelected = '' - dronePara.cameraSelected = '' - dronePara.videoSelected = '' + dronePara.droneSelected = undefined + dronePara.cameraSelected = undefined + dronePara.videoSelected = undefined await getLiveCapacity({}) .then(res => { if (res.code === 0) { @@ -166,18 +191,20 @@ const onRefresh = async () => { if (dronePara.livestreamSource) { dronePara.livestreamSource.forEach((ele: any) => { - dronePara.droneList.push({ label: ele.name + '-' + ele.sn, value: ele.sn }) + dronePara.droneList.push({ label: ele.name + '-' + ele.sn, value: ele.sn, more: ele.cameras_list }) }) } } }) .catch(error => { + message.error(error) console.error(error) }) } onMounted(() => { onRefresh() + agoraPara.token = encodeURIComponent(agoraPara.token) agoraClient = AgoraRTC.createClient({ mode: 'live', codec: 'vp8' }) // Subscribe when a remote user publishes a stream agoraClient.on('user-joined', async (user: IAgoraRTCRemoteUser) => { @@ -191,7 +218,7 @@ onMounted(() => { const remoteVideoTrack = user.videoTrack! // Dynamically create a container in the form of a DIV element for playing the remote video track. const remotePlayerContainer: any = document.getElementById('player') - // remotePlayerContainer.id = agoraPara.uid + remotePlayerContainer.id = user.uid.toString() remoteVideoTrack.play(remotePlayerContainer) } }) @@ -199,6 +226,10 @@ onMounted(() => { console.log('unpublish live:', user) message.info('unpublish live') }) + agoraClient.on('exception', async (e: any) => { + console.log(e) + message.error(e.msg) + }) }) const handleError = (err: any) => { @@ -207,7 +238,9 @@ const handleError = (err: any) => { const handleJoinChannel = (uid: any) => { agoraPara.uid = uid } - +const encodeToken = (e: any) => { + agoraPara.token = encodeURIComponent(agoraPara.token) +} const onStart = async () => { const that = this console.log( @@ -222,13 +255,12 @@ const onStart = async () => { if ( dronePara.droneSelected == null || dronePara.cameraSelected == null || - dronePara.videoSelected == null || dronePara.claritySelected == null ) { message.warn('waring: not select live para!!!') return } - agoraClient.setClientRole('audience', { level: 1 }) + agoraClient.setClientRole('audience', { level: 2 }) if (agoraClient.connectionState === 'DISCONNECTED') { agoraClient .join(agoraPara.appid, agoraPara.channel, agoraPara.token) @@ -236,11 +268,8 @@ const onStart = async () => { livePara.videoId = dronePara.droneSelected + '/' + - dronePara.cameraSelected + - '/' + - dronePara.videoSelected + dronePara.cameraSelected + '/' + (dronePara.videoSelected || nonSwitchable + '-0') console.log(agoraPara) - agoraPara.token = encodeURIComponent(agoraPara.token) livePara.url = 'channel=' + @@ -259,6 +288,9 @@ const onStart = async () => { video_quality: dronePara.claritySelected }) .then(res => { + if (res.code !== 0) { + return + } livePara.liveState = true }) .catch(err => { @@ -269,9 +301,8 @@ const onStop = async () => { livePara.videoId = dronePara.droneSelected + '/' + - dronePara.cameraSelected + - '/' + - dronePara.videoSelected + dronePara.cameraSelected + '/' + (dronePara.videoSelected || nonSwitchable + '-0') + stopLivestream({ video_id: livePara.videoId }).then(res => { @@ -279,52 +310,52 @@ const onStop = async () => { message.success(res.message) } livePara.liveState = false + dronePara.lensSelected = '' console.log('stop play livestream') }) } -const onDroneSelect = (val: any) => { - dronePara.droneSelected = val - if (dronePara.droneSelected) { - const droneTemp = dronePara.livestreamSource - dronePara.cameraList = [] +const onDroneSelect = (val: SelectOption) => { + dronePara.cameraList = [] + dronePara.videoList = [] + dronePara.lensList = [] - droneTemp.forEach((ele: any) => { - const drone = ele - if (drone.cameras_list && drone.sn === dronePara.droneSelected) { - const cameraListTemp = drone.cameras_list - cameraListTemp.forEach((ele: any) => { - dronePara.cameraList.push({ label: ele.name, value: ele.index }) - }) - } - }) + dronePara.cameraSelected = undefined + dronePara.videoSelected = undefined + dronePara.lensSelected = undefined + dronePara.droneSelected = val.value + if (!val.more) { + return } + val.more.forEach((ele: any) => { + dronePara.cameraList.push({ label: ele.name, value: ele.index, more: ele.videos_list }) + }) } -const onCameraSelect = (val: any) => { - dronePara.cameraSelected = val +const onCameraSelect = (val: SelectOption) => { + dronePara.cameraSelected = val.value + dronePara.videoSelected = undefined + dronePara.lensSelected = undefined + dronePara.videoList = [] + dronePara.lensList = [] + if (!val.more) { + return + } - if (dronePara.cameraSelected) { - const droneTemp = dronePara.livestreamSource - droneTemp.forEach((ele: any) => { - const drone = ele - if (drone.sn === dronePara.droneSelected) { - const cameraListTemp = drone.cameras_list - cameraListTemp.forEach((ele: any) => { - const camera = ele - if (camera.index === dronePara.cameraSelected) { - const videoListTemp = camera.videos_list - dronePara.videoList = [] - videoListTemp.forEach((ele: any) => { - dronePara.videoList.push({ label: ele.type, value: ele.index }) - }) - dronePara.videoSelected = dronePara.videoList[0]?.value - } - }) - } - }) + val.more.forEach((ele: any) => { + dronePara.videoList.push({ label: ele.type, value: ele.index, more: ele.switch_video_types }) + }) + if (dronePara.videoList.length === 0) { + return } + const firstVideo: SelectOption = dronePara.videoList[0] + dronePara.videoSelected = firstVideo.value + dronePara.lensList = firstVideo.more + dronePara.lensSelected = firstVideo.label + dronePara.isDockLive = dronePara.lensList.length > 0 } -const onVideoSelect = (val: any) => { - dronePara.videoSelected = val +const onVideoSelect = (val: SelectOption) => { + dronePara.videoSelected = val.value + dronePara.lensList = val.more + dronePara.lensSelected = val.label } const onClaritySelect = (val: any) => { dronePara.claritySelected = val @@ -343,6 +374,21 @@ const onUpdateQuality = () => { } }) } + +const onSwitch = () => { + if (dronePara.lensSelected === undefined || dronePara.lensSelected === nonSwitchable) { + message.info('The ' + nonSwitchable + ' lens cannot be switched, please select the lens to be switched.', 8) + return + } + changeLivestreamLens({ + video_id: livePara.videoId, + video_type: dronePara.lensSelected + }).then(res => { + if (res.code === 0) { + message.success('Switching live camera successfully.') + } + }) +} diff --git a/src/components/task/use-format-task.ts b/src/components/task/use-format-task.ts new file mode 100644 index 0000000..167a581 --- /dev/null +++ b/src/components/task/use-format-task.ts @@ -0,0 +1,35 @@ +import { DEFAULT_PLACEHOLDER } from '/@/utils/constants' +import { Task } from '/@/api/wayline' +import { TaskStatusColor, TaskStatusMap, TaskTypeMap, OutOfControlActionMap } from '/@/types/task' + +export function useFormatTask () { + function formatTaskType (task: Task) { + return TaskTypeMap[task.task_type] || DEFAULT_PLACEHOLDER + } + + function formatTaskTime (time: string) { + return time || DEFAULT_PLACEHOLDER + } + + function formatLostAction (task: Task) { + return OutOfControlActionMap[task.out_of_control_action] || DEFAULT_PLACEHOLDER + } + + function formatTaskStatus (task: Task) { + const statusObj = { + text: '', + color: '' + } + const { status } = task + statusObj.text = TaskStatusMap[status] + statusObj.color = TaskStatusColor[status] + return statusObj + } + + return { + formatTaskType, + formatTaskTime, + formatLostAction, + formatTaskStatus, + } +} diff --git a/src/components/task/use-task-progress-event.ts b/src/components/task/use-task-progress-event.ts new file mode 100644 index 0000000..54d4551 --- /dev/null +++ b/src/components/task/use-task-progress-event.ts @@ -0,0 +1,19 @@ +import EventBus from '/@/event-bus/' +import { onMounted, onBeforeUnmount } from 'vue' +import { TaskProgressInfo } from '/@/types/task' + +export function useTaskProgressEvent (onTaskProgressWs: (data: TaskProgressInfo) => void): void { + function handleTaskProgress (payload: any) { + onTaskProgressWs(payload.data) + // eslint-disable-next-line no-unused-expressions + // console.log('payload', payload.data) + } + + onMounted(() => { + EventBus.on('deviceTaskProgress', handleTaskProgress) + }) + + onBeforeUnmount(() => { + EventBus.off('deviceTaskProgress', handleTaskProgress) + }) +} diff --git a/src/components/wayline-panel.vue b/src/components/wayline-panel.vue deleted file mode 100644 index aad0a49..0000000 --- a/src/components/wayline-panel.vue +++ /dev/null @@ -1,123 +0,0 @@ - - - - - diff --git a/src/event-bus/index.ts b/src/event-bus/index.ts index 8024981..d4d24b3 100644 --- a/src/event-bus/index.ts +++ b/src/event-bus/index.ts @@ -1,8 +1,9 @@ import mitt, { Emitter } from 'mitt' type Events = { - deviceUpgrade: any; - deviceLogUploadProgress: any + deviceUpgrade: any; // 设备升级 + deviceLogUploadProgress: any // 设备日志上传 + deviceTaskProgress: any // 设备任务进度 }; const emitter: Emitter = mitt() diff --git a/src/hooks/use-g-map-tsa.ts b/src/hooks/use-g-map-tsa.ts index 6db59d8..fe3a398 100644 --- a/src/hooks/use-g-map-tsa.ts +++ b/src/hooks/use-g-map-tsa.ts @@ -3,18 +3,19 @@ import { getRoot } from '/@/root' import { ELocalStorageKey } from '/@/types' import { getDeviceBySn } from '/@/api/manage' import { message } from 'ant-design-vue' +import dockIcon from '/@/assets/icons/dock.png' +import rcIcon from '/@/assets/icons/rc.png' +import droneIcon from '/@/assets/icons/drone.png' export function deviceTsaUpdate () { const root = getRoot() const AMap = root.$aMap - const icons: { - [key: string]: string - } = { - 'sub-device': '/@/assets/icons/drone.png', - 'gateway': '/@/assets/icons/rc.png', - 'dock': '/@/assets/icons/dock.png' - } + const icons = new Map([ + ['sub-device', droneIcon], + ['gateway', rcIcon], + ['dock', dockIcon] + ]) const markers = store.state.markerInfo.coverMap const paths = store.state.markerInfo.pathMap @@ -33,12 +34,17 @@ export function deviceTsaUpdate () { function initIcon (type: string) { return new AMap.Icon({ - image: icons[type], - imageSize: new AMap.Size(40, 40) + image: icons.get(type), + imageSize: new AMap.Size(40, 40), + size: new AMap.Size(40, 40) }) } function initMarker (type: string, name: string, sn: string, lng?: number, lat?: number) { + if (AMap === undefined) { + location.reload() + return + } if (markers[sn]) { return } @@ -50,7 +56,6 @@ export function deviceTsaUpdate () { offset: [0, -20], }) root.$map.add(markers[sn]) - // markers[sn].on('moving', function (e: any) { // let path = paths[sn] // if (!path) { diff --git a/src/pages/page-pilot/pilot-home.vue b/src/pages/page-pilot/pilot-home.vue index 1a1d0c4..a897a57 100644 --- a/src/pages/page-pilot/pilot-home.vue +++ b/src/pages/page-pilot/pilot-home.vue @@ -252,6 +252,12 @@ onMounted(() => { apiPilot.onBackClickReg() apiPilot.onStopPlatform() + window.connectCallback = arg => { + connectCallback(arg) + } + window.wsConnectCallback = arg => { + wsConnectCallback(arg) + } device.data.gateway_sn = apiPilot.getRemoteControllerSN() if (device.data.gateway_sn === EStatusValue.DISCONNECT.toString()) { message.warn('Data is not available, please restart the remote control.') @@ -296,12 +302,6 @@ onMounted(() => { bindParam.user_id = res.data.user_id bindParam.workspace_id = res.data.workspace_id }) - window.connectCallback = arg => { - connectCallback(arg) - } - window.wsConnectCallback = arg => { - wsConnectCallback(arg) - } }) const connectCallback = async (arg: any) => { diff --git a/src/pages/page-web/projects/dock.vue b/src/pages/page-web/projects/dock.vue index 72fd32f..0575267 100644 --- a/src/pages/page-web/projects/dock.vue +++ b/src/pages/page-web/projects/dock.vue @@ -9,7 +9,7 @@
-
+
{{ dock.nickname }}
diff --git a/src/pages/page-web/projects/task.vue b/src/pages/page-web/projects/task.vue index e594a8d..3e83954 100644 --- a/src/pages/page-web/projects/task.vue +++ b/src/pages/page-web/projects/task.vue @@ -5,34 +5,42 @@ Task Plan Library - - - + + + - - + +
-
- +
+
diff --git a/src/pages/page-web/projects/wayline.vue b/src/pages/page-web/projects/wayline.vue index b64e8e8..da7db6b 100644 --- a/src/pages/page-web/projects/wayline.vue +++ b/src/pages/page-web/projects/wayline.vue @@ -3,12 +3,24 @@
- Flight Route Library - + Flight Route Library + + + + + + +
- +
@@ -72,14 +84,17 @@ import { reactive } from '@vue/reactivity' import { message } from 'ant-design-vue' import { onMounted, onUpdated, ref } from 'vue' -import { deleteWaylineFile, downloadWaylineFile, getWaylineFiles } from '/@/api/wayline' -import { ELocalStorageKey } from '/@/types' -import { EllipsisOutlined, RocketOutlined, CameraFilled, UserOutlined } from '@ant-design/icons-vue' +import { deleteWaylineFile, downloadWaylineFile, getWaylineFiles, importKmzFile } from '/@/api/wayline' +import { ELocalStorageKey, ERouterName } from '/@/types' +import { EllipsisOutlined, RocketOutlined, CameraFilled, UserOutlined, SelectOutlined } from '@ant-design/icons-vue' import { EDeviceType } from '/@/types/device' import { useMyStore } from '/@/store' import { WaylineFile } from '/@/types/wayline' import { downloadFile } from '/@/utils/common' import { IPage } from '/@/api/http/type' +import { CURRENT_CONFIG } from '/@/api/http/config' +import { load } from '@amap/amap-jsapi-loader' +import { getRoot } from '/@/root' const loading = ref(false) const store = useMyStore() @@ -93,19 +108,28 @@ const waylinesData = reactive({ data: [] as WaylineFile[] }) +const root = getRoot() const workspaceId = localStorage.getItem(ELocalStorageKey.WorkspaceId)! const deleteTip = ref(false) const deleteWaylineId = ref('') const canRefresh = ref(true) +const importVisible = ref(root.$router.currentRoute.value.name === ERouterName.WAYLINE) onMounted(() => { getWaylines() + setTimeout(() => { + const element = document.getElementsByClassName('scrollbar').item(0) as HTMLDivElement + const parent = element?.parentNode as HTMLDivElement + console.info(element, parent) + // console.info(element.scrollHeight, parent.clientHeight) + }, 1000) }) onUpdated(() => { const element = document.getElementsByClassName('scrollbar').item(0) as HTMLDivElement const parent = element?.parentNode as HTMLDivElement setTimeout(() => { + console.info(element, parent) if (element?.scrollHeight < parent?.clientHeight && pagination.total > waylinesData.data.length) { if (canRefresh.value) { pagination.page++ @@ -130,6 +154,7 @@ function getWaylines () { if (res.code !== 0) { return } + waylinesData.data = [] res.data.list.forEach((wayline: WaylineFile) => waylinesData.data.push(wayline)) pagination.total = res.data.pagination.total pagination.page = res.data.pagination.page @@ -151,8 +176,7 @@ function deleteWayline () { deleteWaylineId.value = '' deleteTip.value = false pagination.total-- - waylinesData.data = [] - setTimeout(getWaylines, 500) + getWaylines() }) } @@ -175,12 +199,50 @@ function selectRoute (wayline: WaylineFile) { function onScroll (e: any) { const element = e.srcElement + console.info(element) if (element.scrollTop + element.clientHeight === element.scrollHeight && Math.ceil(pagination.total / pagination.page_size) > pagination.page && canRefresh.value) { pagination.page++ getWaylines() } } +interface FileItem { + uid: string; + name?: string; + status?: string; + response?: string; + url?: string; +} + +interface FileInfo { + file: FileItem; + fileList: FileItem[]; +} +const fileList = ref([]) + +function beforeUpload (file: FileItem) { + fileList.value = [file] + loading.value = true + return true +} +const uploadFile = async () => { + console.info(loading.value) + fileList.value.forEach(async (file: FileItem) => { + const fileData = new FormData() + fileData.append('file', file, file.name) + await importKmzFile(workspaceId, fileData).then((res) => { + if (res.code === 0) { + message.success(`${file.name} file uploaded successfully`) + canRefresh.value = true + getWaylines() + } + }).finally(() => { + loading.value = false + fileList.value = [] + }) + }) +} + diff --git a/src/router/index.ts b/src/router/index.ts index 8b20a35..9e5918d 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,6 +1,6 @@ import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' import { ERouterName } from '/@/types/index' -import CreatePlan from '../pages/page-web/projects/create-plan.vue' +import CreatePlan from '/@/components/task/CreatePlan.vue' import WaylinePanel from '/@/pages/page-web/projects/wayline.vue' import DockPanel from '/@/pages/page-web/projects/dock.vue' import LiveAgora from '/@/components/livestream-agora.vue' diff --git a/src/store/index.ts b/src/store/index.ts index 36a7725..00a49fa 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -4,7 +4,7 @@ import { EDeviceTypeName } from '../types' import { Device, DeviceHms, DeviceOsd, DeviceStatus, DockOsd, GatewayOsd, OSDVisible } from '../types/device' import { getLayers } from '/@/api/layer' import { LayerType } from '/@/types/mapLayer' -import { ETaskStatus, TaskInfo, WaylineFile } from '/@/types/wayline' +import { WaylineFile } from '/@/types/wayline' import { DevicesCmdExecuteInfo } from '/@/types/device-cmd' const initStateFunc = () => ({ @@ -81,11 +81,6 @@ const initStateFunc = () => ({ dockInfo: { } as Device, - taskProgressInfo: { - - } as { - [bid: string]: TaskInfo - }, hmsInfo: {} as { [sn: string]: DeviceHms[] }, @@ -113,16 +108,29 @@ const mutations: MutationTree = { state.deviceState.currentType = EDeviceTypeName.Gateway }, SET_DOCK_INFO (state, info) { + if (Object.keys(info.host).length === 0) { + return + } + if (!state.deviceState.dockInfo[info.sn]) { + state.deviceState.dockInfo[info.sn] = info.host + return + } state.deviceState.currentSn = info.sn state.deviceState.currentType = EDeviceTypeName.Dock const dock = state.deviceState.dockInfo[info.sn] - if (info.host.sdr && state.deviceState.dockInfo[info.sn]) { + if (info.host.sdr) { dock.sdr = info.host.sdr dock.media_file_detail = info.host.media_file_detail return } - const sdr = dock?.sdr - const mediaFileDetail = dock?.media_file_detail + if (info.host.job_number !== undefined) { + if (info.host.drone_battery_maintenance_info) { + dock.drone_battery_maintenance_info = info.host.drone_battery_maintenance_info + } + return + } + const sdr = dock.sdr + const mediaFileDetail = dock.media_file_detail state.deviceState.dockInfo[info.sn] = info.host state.deviceState.dockInfo[info.sn].sdr = sdr state.deviceState.dockInfo[info.sn].media_file_detail = mediaFileDetail @@ -160,17 +168,6 @@ const mutations: MutationTree = { SET_SELECT_DOCK_INFO (state, info) { state.dockInfo = info }, - SET_FLIGHT_TASK_PROGRESS (state, info) { - const taskInfo: TaskInfo = info.output - - if (taskInfo.status === ETaskStatus.OK || taskInfo.status === ETaskStatus.FAILED) { - taskInfo.status = taskInfo.status.concat('(Code:').concat(info.result).concat(')') - setTimeout(() => { - delete state.taskProgressInfo[info.bid] - }, 60000) - } - state.taskProgressInfo[info.bid] = info.output - }, SET_DEVICE_HMS_INFO (state, info) { const hmsList: Array = state.hmsInfo[info.sn] state.hmsInfo[info.sn] = info.host.concat(hmsList ?? []) diff --git a/src/types/airport-tsa.ts b/src/types/airport-tsa.ts index c672f36..9de449e 100644 --- a/src/types/airport-tsa.ts +++ b/src/types/airport-tsa.ts @@ -36,3 +36,27 @@ export enum SupplementLightStateEnum { Close = 0, // 关闭 Open = 1, // 打开 } + +// 机场声光报警状态 +export enum AlarmModeEnum { + CLOSE = 0, // 关闭 + OPEN = 1, // 开启 +} + +// 电池保养 +export enum BatteryStoreModeEnum { + BATTERY_PLAN_STORE = 1, // 电池计划存储策略 + BATTERY_EMERGENCY_STORE = 2, // 电池应急存储策略 +} + +// 飞行器电池保养 +export enum DroneBatteryStateEnum { + NoMaintenanceRequired = 0, // 0-无需保养 + MaintenanceRequired = 1, // 1-待保养 + MaintenanceInProgress = 2, // 2-正在保养 +} + +export enum DroneBatteryModeEnum { + CLOSE = 0, // 关闭 + OPEN = 1, // 开启 +} diff --git a/src/types/device-cmd.ts b/src/types/device-cmd.ts index 1230881..621fd25 100644 --- a/src/types/device-cmd.ts +++ b/src/types/device-cmd.ts @@ -1,3 +1,4 @@ +import { AlarmModeEnum, BatteryStoreModeEnum, DroneBatteryModeEnum } from '/@/types/airport-tsa'; // 机场指令集 export enum DeviceCmd { // 简单指令 @@ -19,16 +20,24 @@ export enum DeviceCmd { PutterClose = 'putter_close', // 推杆闭合 ChargeOpen = 'charge_open', // 打开充电 ChargeClose = 'charge_close', // 关闭充电 + AlarmStateSwitch = 'alarm_state_switch', // 机场声光报警 + BatteryStoreModeSwitch = 'battery_store_mode_switch', // 电池保养 + DroneBatteryModeSwitch = 'battery_maintenance_switch', // 飞行器电池保养 + } +export type DeviceCmdItemAction = AlarmModeEnum | BatteryStoreModeEnum | DroneBatteryModeEnum + export interface DeviceCmdItem{ label: string, // 标题 status: string, // 当前状态 operateText: string, // 按钮文字 cmdKey: DeviceCmd, // 请求指令 oppositeCmdKey?: DeviceCmd, // 相反状态指令 + action?: DeviceCmdItemAction, // 参数 func: string, // 处理函数 loading: boolean // 按钮loading + disabled?: boolean // 按钮disabled } // 机场指令 @@ -114,6 +123,34 @@ export const cmdList: DeviceCmdItem[] = [ func: 'supplementLightStatus', loading: false, }, + { + label: '机场声光报警', + status: '关', + operateText: '打开', + cmdKey: DeviceCmd.AlarmStateSwitch, + action: AlarmModeEnum.OPEN, + func: 'alarmState', + loading: false, + }, + { + label: '机场电池存储模式', + status: '计划', + operateText: '应急', + cmdKey: DeviceCmd.BatteryStoreModeSwitch, + action: BatteryStoreModeEnum.BATTERY_EMERGENCY_STORE, + func: 'batteryStoreMode', + loading: false, + }, + { + label: '飞机电池保养', + status: '--', + operateText: '保养', + cmdKey: DeviceCmd.DroneBatteryModeSwitch, + action: DroneBatteryModeEnum.OPEN, + func: 'droneBatteryMode', + loading: false, + disabled: true, + }, ] export enum DeviceCmdStatusText { @@ -176,6 +213,32 @@ export enum DeviceCmdStatusText { DeviceSupplementLightCloseText = '关闭中...', DeviceSupplementLightCloseFailedText = '开', DeviceSupplementLightCloseBtnText = '打开', + + AlarmStateOpenNormalText = '开', + AlarmStateOpenText = '开启中...', + AlarmStateOpenFailedText = '关', + AlarmStateOpenBtnText = '关闭', + + AlarmStateCloseNormalText = '关', + AlarmStateCloseText = '关闭中...', + AlarmStateCloseFailedText = '开', + AlarmStateCloseBtnText = '打开', + + BatteryStoreModePlanNormalText = '计划', + BatteryStoreModePlanText = '切换中...', + BatteryStoreModePlanFailedText = '应急', + BatteryStoreModePlanBtnText = '应急', + + BatteryStoreModeEmergencyNormalText = '应急', + BatteryStoreModeEmergencyText = '切换中...', + BatteryStoreModeEmergencyFailedText = '计划', + BatteryStoreModeEmergencyBtnText = '计划', + + DroneBatteryModeMaintenanceInProgressText = '保养中', + DroneBatteryModeMaintenanceNotNeedText = '无需保养', + DroneBatteryModeMaintenanceNeedText = '需保养', + DroneBatteryModeOpenBtnText = '保养', + DroneBatteryModeCloseBtnText = '关闭保养', } // cmd ws 消息状态 diff --git a/src/types/device-setting.ts b/src/types/device-setting.ts new file mode 100644 index 0000000..25acf21 --- /dev/null +++ b/src/types/device-setting.ts @@ -0,0 +1,148 @@ +// 夜航灯开关 +export enum NightLightsStateEnum { + CLOSE = 0, // 0-关闭 + OPEN = 1, // 1-打开 +} + +// 限远开关 +export enum DistanceLimitStatusEnum { + UNSET = 0, // 0-未设置 + SET = 1, // 1-已设置 +} + +export interface DistanceLimitStatus { + state?: DistanceLimitStatusEnum; + distance_limit?: number; // 限远 +} + +// 避障 +export enum ObstacleAvoidanceStatusEnum { + CLOSE = 0, // 0-关闭 + OPEN = 1, // 1-开启 +} + +export interface ObstacleAvoidance { + horizon?: ObstacleAvoidanceStatusEnum;// 水平避障开关 + upside?: ObstacleAvoidanceStatusEnum;// 上行方向避障开关 + downside?: ObstacleAvoidanceStatusEnum;// 下行方向避障开关 +} + +// 设备管理设置key +export enum DeviceSettingKeyEnum { + NIGHT_LIGHTS_MODE_SET = 'night_lights_state', // 夜航灯开关 + HEIGHT_LIMIT_SET = 'height_limit', // 限高设置 + DISTANCE_LIMIT_SET = 'distance_limit_status', // 限远开关 + OBSTACLE_AVOIDANCE_HORIZON = 'obstacle_avoidance_horizon', // 水平避障状态 + OBSTACLE_AVOIDANCE_UPSIDE = 'obstacle_avoidance_upside', // 上视避障状态 + OBSTACLE_AVOIDANCE_DOWNSIDE = 'obstacle_avoidance_downside', // 下视避障状态 +} + +export type DeviceSettingType = Record + +export const initDeviceSetting = { + [DeviceSettingKeyEnum.NIGHT_LIGHTS_MODE_SET]: + { + label: '飞行器夜航灯', + value: '', + trueValue: NightLightsStateEnum.CLOSE, + editable: false, + popConfirm: { + visible: false, + loading: false, + // content: '为保证飞行器的作业安全,建议打开夜航灯', + label: '飞行器夜航灯', + }, + settingKey: DeviceSettingKeyEnum.NIGHT_LIGHTS_MODE_SET, + }, + [DeviceSettingKeyEnum.HEIGHT_LIMIT_SET]: + { + label: '限高', + value: '', + trueValue: 120, + editable: false, + popConfirm: { + visible: false, + loading: false, + // content: '限高:20 - 1500m', + // info: '修改限高会影响当前机场的所有作业任务,建议确认作业情况后再进行修改', + label: '限高', + }, + settingKey: DeviceSettingKeyEnum.HEIGHT_LIMIT_SET, + }, + [DeviceSettingKeyEnum.DISTANCE_LIMIT_SET]: + { + label: '限远', + value: '', + trueValue: DistanceLimitStatusEnum.UNSET, + // info: '限远(15 - 8000m)是约束飞行器相对机场的最大作业距离', + editable: false, + popConfirm: { + visible: false, + loading: false, + // content: '限远 (15- 8000m) 是约束飞行器相对机场的最大作业距离', + // info: '修改限远会影响当前机场的所有作业任务,建议确认作业情况后再进行修改', + label: '限远', + + }, + settingKey: DeviceSettingKeyEnum.DISTANCE_LIMIT_SET, + }, + [DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_HORIZON]: + { + label: '水平避障', + value: '', + trueValue: ObstacleAvoidanceStatusEnum.CLOSE, + // info: '飞行器的避障工作状态显示,可以快速开启/关闭飞行器避障,如需进一步设置请在设备运维页面设置', + editable: false, + popConfirm: { + visible: false, + loading: false, + // content: '飞行器避障是保障飞行作业安全的基础功能,建议保持飞行器避障开启', + label: '水平避障', + + }, + settingKey: DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_HORIZON, + }, + [DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_UPSIDE]: + { + label: '上视避障', + value: '', + trueValue: ObstacleAvoidanceStatusEnum.CLOSE, + // info: '飞行器的避障工作状态显示,可以快速开启/关闭飞行器避障,如需进一步设置请在设备运维页面设置', + editable: false, + popConfirm: { + visible: false, + loading: false, + // content: '飞行器避障是保障飞行作业安全的基础功能,建议保持飞行器避障开启', + label: '上视避障', + + }, + settingKey: DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_UPSIDE, + }, + [DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_DOWNSIDE]: + { + label: '下视避障', + value: '', + trueValue: ObstacleAvoidanceStatusEnum.CLOSE, + // info: '飞行器的避障工作状态显示,可以快速开启/关闭飞行器避障,如需进一步设置请在设备运维页面设置', + editable: false, + popConfirm: { + visible: false, + loading: false, + // content: '飞行器避障是保障飞行作业安全的基础功能,建议保持飞行器避障开启', + label: '下视避障', + + }, + settingKey: DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_DOWNSIDE, + }, +} as DeviceSettingType + +export const initDeviceSettingFormModel = { + nightLightsState: false, // 夜航灯开关 + heightLimit: 20, // 限高设置 + distanceLimitStatus: { state: false, distanceLimit: 15 }, // 限远开关 + obstacleAvoidanceHorizon: false, // 飞行器避障-水平开关设置 + obstacleAvoidanceUpside: false, // 飞行器避障-上视开关设置 + obstacleAvoidanceDownside: false, // 飞行器避障-下视开关设置 +} + +export type DeviceSettingFormModel = typeof initDeviceSettingFormModel diff --git a/src/types/device.ts b/src/types/device.ts index 08c285a..733ef3f 100644 --- a/src/types/device.ts +++ b/src/types/device.ts @@ -1,5 +1,6 @@ import { commonColor } from '/@/utils/color' - +import { NightLightsStateEnum, DistanceLimitStatus, ObstacleAvoidance } from './device-setting' +import { AlarmModeEnum, BatteryStoreModeEnum, DroneBatteryStateEnum } from './airport-tsa' export interface DeviceValue { key: string; // 'domain-type-subtype' domain: string; // 表示一个领域,作为一个命名空间,暂时分 飞机类-0, 负载类-1,RC类-2,机场类-3 4种 @@ -235,7 +236,11 @@ export interface DeviceOsd { landing_power: string, remain_flight_time: number, return_home_power: string, - } + }, + night_lights_state?: NightLightsStateEnum;// 夜航灯开关 + height_limit?: number;// 限高设置 + distance_limit_status?: DistanceLimitStatus;// 限远开关 + obstacle_avoidance?: ObstacleAvoidance;// 飞行器避障开关设置 } export interface DockOsd { @@ -294,6 +299,12 @@ export interface DockOsd { device_online_status: number, device_paired: number, }, + alarm_state?: AlarmModeEnum; // 机场声光报警状态 + battery_store_mode?: BatteryStoreModeEnum; // 电池保养(存储)模式 + drone_battery_maintenance_info?: { // 飞行器电池保养信息 + maintenance_state: DroneBatteryStateEnum, // 保养状态 + maintenance_time_left: number, // 电池保养剩余时间(小时) + } } export enum EModeCode { @@ -343,6 +354,10 @@ export enum EDeviceType { H20N = '1-61-0' as any, DJI_Dock_Camera = '1-165-0' as any, L1 = '1-90742-0' as any, + M3E = '0-77-0' as any, + M3D = '0-77-1' as any, + M3E_Camera = '1-66-0' as any, + M3T_Camera = '1-67-0' as any, } export enum EDockModeCode { diff --git a/src/types/enums.ts b/src/types/enums.ts index 00a298c..e0e1995 100644 --- a/src/types/enums.ts +++ b/src/types/enums.ts @@ -90,7 +90,7 @@ export enum EBizCode { MapElementDelete = 'map_element_delete', DeviceOnline = 'device_online', DeviceOffline = 'device_offline', - FlightTaskProgress = 'flighttask_progress', + FlightTaskProgress = 'flighttask_progress', // 机场任务执行进度 DeviceHms = 'device_hms', // 设备指令 diff --git a/src/types/task.ts b/src/types/task.ts new file mode 100644 index 0000000..86d91d3 --- /dev/null +++ b/src/types/task.ts @@ -0,0 +1,97 @@ +import { commonColor } from '/@/utils/color' + +// 任务类型 +export enum TaskType { + Immediate = 0, // 立即执行 + Single = 1, // 单次定时任务 +} + +export const TaskTypeMap = { + [TaskType.Immediate]: 'Immediate', + [TaskType.Single]: 'Timed & One-Time', +} + +// 失控动作 +export enum OutOfControlAction { + ReturnToHome = 0, + Hover = 1, + Land = 2, +} + +export const OutOfControlActionMap = { + [OutOfControlAction.ReturnToHome]: 'Return to Home', + [OutOfControlAction.Hover]: 'Hover', + [OutOfControlAction.Land]: 'Land', +} + +export const OutOfControlActionOptions = [ + { value: OutOfControlAction.ReturnToHome, label: OutOfControlActionMap[OutOfControlAction.ReturnToHome] }, + { value: OutOfControlAction.Hover, label: OutOfControlActionMap[OutOfControlAction.Hover] }, + { value: OutOfControlAction.Land, label: OutOfControlActionMap[OutOfControlAction.Land] }, +] + +// 任务状态 +export enum TaskStatus { + Wait = 1, // 待执行 + Carrying = 2, // 执行中 + Success = 3, // 完成 + CanCel = 4, // 取消 + Fail = 5, // 失败 +} + +export const TaskStatusMap = { + [TaskStatus.Wait]: 'To be performed', + [TaskStatus.Carrying]: 'In progress', + [TaskStatus.Success]: 'Task completed', + [TaskStatus.CanCel]: 'Task canceled', + [TaskStatus.Fail]: 'Task failed', +} + +export const TaskStatusColor = { + [TaskStatus.Wait]: commonColor.BLUE, + [TaskStatus.Carrying]: commonColor.BLUE, + [TaskStatus.Success]: commonColor.NORMAL, + [TaskStatus.CanCel]: commonColor.FAIL, + [TaskStatus.Fail]: commonColor.FAIL, +} + +// 任务执行 ws 消息状态 +export enum TaskProgressStatus { + Sent = 'sent', // 已下发 + inProgress = 'in_progress', // 执行中 + Paused = 'paused', // 暂停 + Rejected = 'rejected', // 拒绝 + Canceled = 'canceled', // 取消或终止 + Timeout = 'timeout', // 超时 + Failed = 'failed', // 失败 + OK = 'ok', // 上传成功 +} + +// 任务进度消息 +export interface TaskProgressInfo { + bid: string, + output:{ + ext: { + current_waypoint_index: number, + media_count: number // 媒体文件 + }, + progress:{ + current_step: number, + percent: number + }, + status: TaskProgressStatus + }, + result: number, +} + +// ws status => log status +export const TaskProgressWsStatusMap = { + [TaskProgressStatus.Sent]: TaskStatus.Carrying, + [TaskProgressStatus.inProgress]: TaskStatus.Carrying, + [TaskProgressStatus.Rejected]: TaskStatus.Fail, + [TaskProgressStatus.OK]: TaskStatus.Success, + [TaskProgressStatus.Failed]: TaskStatus.Fail, + [TaskProgressStatus.Canceled]: TaskStatus.CanCel, + [TaskProgressStatus.Timeout]: TaskStatus.Fail, + [TaskProgressStatus.Paused]: TaskStatus.Wait, +} diff --git a/src/types/wayline.ts b/src/types/wayline.ts index 69746e6..dfe7f91 100644 --- a/src/types/wayline.ts +++ b/src/types/wayline.ts @@ -1,30 +1,15 @@ +// 航线类型 +export enum WaylineType { + NormalWaypointWayline = 0, // 普通航点航线 + AccurateReshootingWayline = 1 // 精准复拍航线 +} + export interface WaylineFile { id: string, name: string, drone_model_key: any, payload_model_keys: string[], - template_types: number[], + template_types: WaylineType[], update_time: number, user_name: string, } - -export interface TaskExt { - current_waypoint_index: number, - media_count: number, -} - -export interface TaskProgress { - current_step: number, - percent: number, -} - -export interface TaskInfo { - status: string, - progress: TaskProgress, - ext: TaskExt, -} - -export enum ETaskStatus { - OK = 'ok', - FAILED = 'failed' -} diff --git a/src/utils/device-cmd.ts b/src/utils/device-cmd.ts index b6c80dd..3ffe75f 100644 --- a/src/utils/device-cmd.ts +++ b/src/utils/device-cmd.ts @@ -1,6 +1,7 @@ +import { DroneBatteryModeEnum, DroneBatteryStateEnum } from './../types/airport-tsa'; import { DeviceInfoType } from '/@/types/device' import { DeviceCmd, DeviceCmdItem, DeviceCmdExecuteInfo, DeviceCmdStatusText, DeviceCmdExecuteStatus } from '/@/types/device-cmd' -import { AirportStorage, CoverStateEnum, PutterStateEnum, ChargeStateEnum, SupplementLightStateEnum } from '/@/types/airport-tsa' +import { AirportStorage, CoverStateEnum, PutterStateEnum, ChargeStateEnum, SupplementLightStateEnum, AlarmModeEnum, BatteryStoreModeEnum } from '/@/types/airport-tsa' import { getBytesObject } from './bytes' import { DEFAULT_PLACEHOLDER } from './constants' @@ -35,6 +36,12 @@ export function updateDeviceCmdInfoByOsd (cmdList: DeviceCmdItem[], deviceInfo: droneFormat(cmdItem, device) } else if (cmdItem.cmdKey === DeviceCmd.SupplementLightOpen || cmdItem.cmdKey === DeviceCmd.SupplementLightClose) { // 补光灯开关 getSupplementLightState(cmdItem, dock) + } else if (cmdItem.cmdKey === DeviceCmd.AlarmStateSwitch) { // 声光报警 + getAlarmState(cmdItem, dock) + } else if (cmdItem.cmdKey === DeviceCmd.BatteryStoreModeSwitch) { // 电池保养 + getBatteryStoreMode(cmdItem, dock) + } else if (cmdItem.cmdKey === DeviceCmd.DroneBatteryModeSwitch) { // 飞行器电池保养 + getDroneBatteryMode(cmdItem, dock) } }) } @@ -166,6 +173,55 @@ function getSupplementLightState (cmdItem: DeviceCmdItem, airportProperties: any } } +// 声光报警 +function getAlarmState (cmdItem: DeviceCmdItem, airportProperties: any) { + const alarmState = airportProperties?.alarm_state + if (alarmState === AlarmModeEnum.CLOSE) { + cmdItem.operateText = DeviceCmdStatusText.AlarmStateCloseBtnText + cmdItem.status = DeviceCmdStatusText.AlarmStateCloseNormalText + cmdItem.action = AlarmModeEnum.OPEN + } else if (alarmState === AlarmModeEnum.OPEN) { + cmdItem.operateText = DeviceCmdStatusText.AlarmStateOpenBtnText + cmdItem.status = DeviceCmdStatusText.AlarmStateOpenNormalText + cmdItem.action = AlarmModeEnum.CLOSE + } +} + +// 机场电池模式 +function getBatteryStoreMode (cmdItem: DeviceCmdItem, airportProperties: any) { + const batteryStoreMode = airportProperties?.battery_store_mode + if (batteryStoreMode === BatteryStoreModeEnum.BATTERY_PLAN_STORE) { + cmdItem.operateText = DeviceCmdStatusText.BatteryStoreModePlanBtnText + cmdItem.status = DeviceCmdStatusText.BatteryStoreModePlanNormalText + cmdItem.action = BatteryStoreModeEnum.BATTERY_EMERGENCY_STORE + } else if (batteryStoreMode === BatteryStoreModeEnum.BATTERY_EMERGENCY_STORE) { + cmdItem.operateText = DeviceCmdStatusText.BatteryStoreModeEmergencyBtnText + cmdItem.status = DeviceCmdStatusText.BatteryStoreModeEmergencyNormalText + cmdItem.action = BatteryStoreModeEnum.BATTERY_PLAN_STORE + } +} + +// 飞行器电池保养 +function getDroneBatteryMode (cmdItem: DeviceCmdItem, airportProperties: any) { + const maintenanceState = airportProperties?.drone_battery_maintenance_info?.maintenance_state + if (maintenanceState === DroneBatteryStateEnum.MaintenanceInProgress) { + cmdItem.operateText = DeviceCmdStatusText.DroneBatteryModeCloseBtnText + cmdItem.status = DeviceCmdStatusText.DroneBatteryModeMaintenanceInProgressText + cmdItem.action = DroneBatteryModeEnum.CLOSE + cmdItem.disabled = false + } else if (maintenanceState === DroneBatteryStateEnum.NoMaintenanceRequired) { + cmdItem.operateText = DeviceCmdStatusText.DroneBatteryModeOpenBtnText + cmdItem.status = DeviceCmdStatusText.DroneBatteryModeMaintenanceNeedText + cmdItem.action = DroneBatteryModeEnum.OPEN + cmdItem.disabled = true + } else if (maintenanceState === DroneBatteryStateEnum.MaintenanceRequired) { + cmdItem.operateText = DeviceCmdStatusText.DroneBatteryModeOpenBtnText + cmdItem.status = DeviceCmdStatusText.DroneBatteryModeMaintenanceNotNeedText + cmdItem.action = DroneBatteryModeEnum.OPEN + cmdItem.disabled = false + } +} + /** * 交换指令 * @param cmd @@ -344,6 +400,74 @@ export function updateDeviceCmdInfoByExecuteInfo (cmdList: DeviceCmdItem[], devi } else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.OK) { cmdItem.loading = false } + } else if (cmdItem.cmdKey === DeviceCmd.AlarmStateSwitch) { // 机场声光报警 + if (cmdItem.action === AlarmModeEnum.CLOSE) { + if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.InProgress) { + cmdItem.status = DeviceCmdStatusText.AlarmStateCloseText + cmdItem.loading = true + } else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.Failed) { + cmdItem.status = DeviceCmdStatusText.AlarmStateCloseFailedText + cmdItem.loading = false + } else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.OK) { + cmdItem.loading = false + } + } else if (cmdItem.action === AlarmModeEnum.OPEN) { + if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.InProgress) { + cmdItem.status = DeviceCmdStatusText.AlarmStateOpenText + cmdItem.loading = true + } else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.Failed) { + cmdItem.status = DeviceCmdStatusText.AlarmStateOpenFailedText + cmdItem.loading = false + } else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.OK) { + cmdItem.loading = false + } + } + } else if (cmdItem.cmdKey === DeviceCmd.BatteryStoreModeSwitch) { // 电池保养 + if (cmdItem.action === BatteryStoreModeEnum.BATTERY_PLAN_STORE) { + if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.InProgress) { + cmdItem.status = DeviceCmdStatusText.BatteryStoreModePlanText + cmdItem.loading = true + } else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.Failed) { + cmdItem.status = DeviceCmdStatusText.BatteryStoreModePlanFailedText + cmdItem.loading = false + } else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.OK) { + cmdItem.loading = false + } + } else if (cmdItem.action === BatteryStoreModeEnum.BATTERY_EMERGENCY_STORE) { + if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.InProgress) { + cmdItem.status = DeviceCmdStatusText.BatteryStoreModeEmergencyText + cmdItem.loading = true + } else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.Failed) { + cmdItem.status = DeviceCmdStatusText.BatteryStoreModeEmergencyFailedText + cmdItem.loading = false + } else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.OK) { + cmdItem.loading = false + } + } + } else if (cmdItem.cmdKey === DeviceCmd.DroneBatteryModeSwitch) { // 飞行器电池保养 + if (cmdItem.action === DroneBatteryModeEnum.OPEN) { + if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.InProgress) { + cmdItem.status = DeviceCmdStatusText.DroneBatteryModeMaintenanceInProgressText + // cmdItem.loading = true + } else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.Failed) { + cmdItem.status = DeviceCmdStatusText.DroneBatteryModeMaintenanceNeedText + // cmdItem.loading = false + } else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.OK) { + cmdItem.status = DeviceCmdStatusText.DroneBatteryModeMaintenanceNotNeedText + // cmdItem.loading = false + } + } else if (cmdItem.action === DroneBatteryModeEnum.CLOSE) { + if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.InProgress) { + cmdItem.status = DeviceCmdStatusText.DroneBatteryModeMaintenanceInProgressText + cmdItem.loading = true + } else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.Failed) { + cmdItem.status = DeviceCmdStatusText.DroneBatteryModeMaintenanceInProgressText + cmdItem.loading = false + } else if (deviceCmdExecuteInfo.output.status === DeviceCmdExecuteStatus.OK) { + cmdItem.status = DeviceCmdStatusText.DroneBatteryModeMaintenanceNeedText + cmdItem.loading = false + } + } } } }) diff --git a/src/utils/device-setting.ts b/src/utils/device-setting.ts new file mode 100644 index 0000000..bd02a2a --- /dev/null +++ b/src/utils/device-setting.ts @@ -0,0 +1,193 @@ +import { DeviceInfoType } from '/@/types/device' +import { DeviceSettingType, DeviceSettingKeyEnum, DistanceLimitStatusEnum, ObstacleAvoidanceStatusEnum, DeviceSettingFormModel, NightLightsStateEnum } from '/@/types/device-setting' +import { DEFAULT_PLACEHOLDER } from './constants' +import { isNil } from 'lodash' + +const Unit_M = ' m' + +/** + * 根据osd 更新信息 + * @param deviceSetting + * @param deviceInfo + * @returns + */ +export function updateDeviceSettingInfoByOsd (deviceSetting: DeviceSettingType, deviceInfo: DeviceInfoType) { + const { device, dock, gateway } = deviceInfo || {} + if (!deviceSetting) { + return + } + // 夜航灯 + let nightLightsState = '' as any + if (isNil(device?.night_lights_state)) { + deviceSetting[DeviceSettingKeyEnum.NIGHT_LIGHTS_MODE_SET].editable = false + deviceSetting[DeviceSettingKeyEnum.NIGHT_LIGHTS_MODE_SET].value = DEFAULT_PLACEHOLDER + nightLightsState = DEFAULT_PLACEHOLDER + } else { + deviceSetting[DeviceSettingKeyEnum.NIGHT_LIGHTS_MODE_SET].editable = true + nightLightsState = device?.night_lights_state + if (nightLightsState === NightLightsStateEnum.CLOSE) { + deviceSetting[DeviceSettingKeyEnum.NIGHT_LIGHTS_MODE_SET].value = '关闭' + } else if (nightLightsState === NightLightsStateEnum.OPEN) { + deviceSetting[DeviceSettingKeyEnum.NIGHT_LIGHTS_MODE_SET].value = '开启' + } else { + deviceSetting[DeviceSettingKeyEnum.NIGHT_LIGHTS_MODE_SET].value = DEFAULT_PLACEHOLDER + } + } + deviceSetting[DeviceSettingKeyEnum.NIGHT_LIGHTS_MODE_SET].trueValue = nightLightsState + + // 限高 + let heightLimit = device?.height_limit as any + if (isNil(heightLimit) || heightLimit === 0) { + heightLimit = DEFAULT_PLACEHOLDER + deviceSetting[DeviceSettingKeyEnum.HEIGHT_LIMIT_SET].editable = false + } else { + deviceSetting[DeviceSettingKeyEnum.HEIGHT_LIMIT_SET].editable = true + } + deviceSetting[DeviceSettingKeyEnum.HEIGHT_LIMIT_SET].trueValue = heightLimit + deviceSetting[DeviceSettingKeyEnum.HEIGHT_LIMIT_SET].value = heightLimit + Unit_M + + // 限远 + let distanceLimitStatus = '' as any + if (isNil(device?.distance_limit_status?.state)) { + distanceLimitStatus = DEFAULT_PLACEHOLDER + deviceSetting[DeviceSettingKeyEnum.DISTANCE_LIMIT_SET].editable = false + deviceSetting[DeviceSettingKeyEnum.DISTANCE_LIMIT_SET].value = DEFAULT_PLACEHOLDER + } else { + deviceSetting[DeviceSettingKeyEnum.DISTANCE_LIMIT_SET].editable = true + distanceLimitStatus = device?.distance_limit_status?.state + if (distanceLimitStatus === DistanceLimitStatusEnum.UNSET) { + deviceSetting[DeviceSettingKeyEnum.DISTANCE_LIMIT_SET].value = '关闭' + } else if (distanceLimitStatus === DistanceLimitStatusEnum.SET) { + const distanceLimit = device?.distance_limit_status?.distance_limit + if (distanceLimit) { + deviceSetting[DeviceSettingKeyEnum.DISTANCE_LIMIT_SET].value = distanceLimit + Unit_M + } else { + deviceSetting[DeviceSettingKeyEnum.DISTANCE_LIMIT_SET].value = DEFAULT_PLACEHOLDER + } + } else { + deviceSetting[DeviceSettingKeyEnum.DISTANCE_LIMIT_SET].value = DEFAULT_PLACEHOLDER + } + } + deviceSetting[DeviceSettingKeyEnum.DISTANCE_LIMIT_SET].trueValue = distanceLimitStatus + + // 避障 + if (isNil(device?.obstacle_avoidance)) { + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_HORIZON].editable = false + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_HORIZON].value = DEFAULT_PLACEHOLDER + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_HORIZON].trueValue = DEFAULT_PLACEHOLDER + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_UPSIDE].editable = false + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_UPSIDE].value = DEFAULT_PLACEHOLDER + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_UPSIDE].trueValue = DEFAULT_PLACEHOLDER + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_DOWNSIDE].editable = false + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_DOWNSIDE].value = DEFAULT_PLACEHOLDER + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_DOWNSIDE].trueValue = DEFAULT_PLACEHOLDER + } else { + const { horizon, upside, downside } = device.obstacle_avoidance || {} + if (isNil(horizon)) { + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_HORIZON].editable = false + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_HORIZON].value = DEFAULT_PLACEHOLDER + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_HORIZON].trueValue = DEFAULT_PLACEHOLDER + } else { + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_HORIZON].editable = false + if (horizon === ObstacleAvoidanceStatusEnum.CLOSE) { + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_HORIZON].value = '关闭' + } else if (horizon === ObstacleAvoidanceStatusEnum.OPEN) { + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_HORIZON].value = '开启' + } else { + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_HORIZON].value = DEFAULT_PLACEHOLDER + } + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_HORIZON].trueValue = horizon + } + + if (isNil(upside)) { + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_UPSIDE].editable = false + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_UPSIDE].value = DEFAULT_PLACEHOLDER + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_UPSIDE].trueValue = DEFAULT_PLACEHOLDER + } else { + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_UPSIDE].editable = false + if (upside === ObstacleAvoidanceStatusEnum.CLOSE) { + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_UPSIDE].value = '关闭' + } else if (upside === ObstacleAvoidanceStatusEnum.OPEN) { + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_UPSIDE].value = '开启' + } else { + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_UPSIDE].value = DEFAULT_PLACEHOLDER + } + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_UPSIDE].trueValue = upside + } + + if (isNil(downside)) { + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_DOWNSIDE].editable = false + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_DOWNSIDE].value = DEFAULT_PLACEHOLDER + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_DOWNSIDE].trueValue = DEFAULT_PLACEHOLDER + } else { + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_DOWNSIDE].editable = false + if (downside === ObstacleAvoidanceStatusEnum.CLOSE) { + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_DOWNSIDE].value = '关闭' + } else if (downside === ObstacleAvoidanceStatusEnum.OPEN) { + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_DOWNSIDE].value = '开启' + } else { + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_DOWNSIDE].value = DEFAULT_PLACEHOLDER + } + deviceSetting[DeviceSettingKeyEnum.OBSTACLE_AVOIDANCE_DOWNSIDE].trueValue = downside + } + } + return deviceSetting +} + +// 更新formModel +export function updateDeviceSettingFormModelByOsd (deviceSettingFormModelFromOsd: DeviceSettingFormModel, deviceInfo: DeviceInfoType) { + const { device, dock, gateway } = deviceInfo || {} + if (!deviceSettingFormModelFromOsd) { + return + } + // 夜航灯 + const nightLightsState = device?.night_lights_state as any + if (!isNil(nightLightsState) && nightLightsState === NightLightsStateEnum.OPEN) { + deviceSettingFormModelFromOsd.nightLightsState = true + } else { + deviceSettingFormModelFromOsd.nightLightsState = false + } + + // 限高 + const heightLimit = device?.height_limit as any + if (isNil(heightLimit) || heightLimit === 0) { + deviceSettingFormModelFromOsd.heightLimit = 20 + } else { + deviceSettingFormModelFromOsd.heightLimit = heightLimit + } + + // 限远 + const distanceLimitStatus = device?.distance_limit_status?.state as any + if (!isNil(distanceLimitStatus) && distanceLimitStatus === DistanceLimitStatusEnum.SET) { + deviceSettingFormModelFromOsd.distanceLimitStatus.state = true + deviceSettingFormModelFromOsd.distanceLimitStatus.distanceLimit = device?.distance_limit_status?.distance_limit || 15 + } else { + deviceSettingFormModelFromOsd.distanceLimitStatus.state = false + deviceSettingFormModelFromOsd.distanceLimitStatus.distanceLimit = 15 + } + + // 避障 + if (isNil(device?.obstacle_avoidance)) { + deviceSettingFormModelFromOsd.obstacleAvoidanceHorizon = false + deviceSettingFormModelFromOsd.obstacleAvoidanceUpside = false + deviceSettingFormModelFromOsd.obstacleAvoidanceDownside = false + } else { + const { horizon, upside, downside } = device.obstacle_avoidance || {} + if (!isNil(horizon) && horizon === ObstacleAvoidanceStatusEnum.OPEN) { + deviceSettingFormModelFromOsd.obstacleAvoidanceHorizon = true + } else { + deviceSettingFormModelFromOsd.obstacleAvoidanceHorizon = false + } + if (!isNil(upside) && upside === ObstacleAvoidanceStatusEnum.OPEN) { + deviceSettingFormModelFromOsd.obstacleAvoidanceUpside = true + } else { + deviceSettingFormModelFromOsd.obstacleAvoidanceUpside = false + } + if (!isNil(downside) && downside === ObstacleAvoidanceStatusEnum.OPEN) { + deviceSettingFormModelFromOsd.obstacleAvoidanceDownside = true + } else { + deviceSettingFormModelFromOsd.obstacleAvoidanceDownside = false + } + } + return deviceSettingFormModelFromOsd +} diff --git a/src/utils/error-code/index.ts b/src/utils/error-code/index.ts new file mode 100644 index 0000000..e4c2679 --- /dev/null +++ b/src/utils/error-code/index.ts @@ -0,0 +1,310 @@ +export interface ErrorCode { + code: number; + msg: string; +} + +/** + * 根据错误码翻译错误信息 + * @param code + * @param errorMsg + * @returns + */ +export function getErrorMessage (code: number, errorMsg?: string): string { + const errorInfo = ERROR_CODE.find((item: ErrorCode) => item.code === code) + return errorInfo ? errorInfo.msg : errorMsg || 'Server error' +} + +// 暂时只添加航线错误 +export const ERROR_CODE = [ + { + code: 314001, + msg: 'The issued route task url is empty', + }, + { + code: 314002, + msg: 'The issued route task md5 is empty', + }, + { + code: 314003, + msg: 'MissionID is invalid', + }, + { + code: 314004, + msg: 'Failed to send flight route task from cloud', + }, + { + code: 314005, + msg: 'Route md5 check failed', + }, + { + code: 314006, + msg: 'Timeout waiting for aircraft to upload route (waiting for gs_state)', + }, + { + code: 314007, + msg: 'Failed to upload route to aircraft', + }, + { + code: 314008, + msg: 'Timeout waiting for the aircraft to enter the route executable state', + }, + { + code: 314009, + msg: 'Failed to open route mission', + }, + { + code: 314010, + msg: 'Route execution failed', + }, + { + code: 316001, + msg: 'Failed to set alternate point', + }, + { + code: 316002, + msg: 'Alternate safety transfer altitude equipment failed', + }, + { + code: 316003, + msg: 'Failed to set takeoff altitude. Remarks: The default safe takeoff height of the aircraft set by the current DJI Dock is: 1.8', + }, + { + code: 316004, + msg: 'Failed to set runaway behavior', + }, + { + code: 316005, + msg: 'Aircraft RTK convergence failed', + }, + { + code: 316013, + msg: 'DJI Dock Moved', + }, + { + code: 316015, + msg: 'The aircraft RTK convergence position is too far from the DJI Dock', + }, + { + code: 316007, + msg: 'Set parameter timeout while waiting for aircraft to be ready', + }, + { + code: 316008, + msg: 'Failed to gain control of aircraft', + }, + { + code: 316009, + msg: 'Aircraft power is low', + }, + { + code: 316010, + msg: 'After power on, the aircraft is not connected for more than 2 minutes (flight control OSD reception timeout)', + }, + { + code: 316011, + msg: 'Landing Position Offset', + }, + + { + code: 317001, + msg: 'Failed to get the number of media files', + }, + + { + code: 319001, + msg: 'The task center is not currently idle', + }, + { + code: 319002, + msg: 'dronenest communication timeout', + }, + { + code: 319999, + msg: 'Unknown error, e.g. restart after crash', + }, + { + code: 321000, + msg: 'Route execution failed, unknown error', + }, + { + code: 321257, + msg: 'The route has already started and cannot be started again', + }, + { + code: 321258, + msg: 'The route cannot be interrupted in this state', + }, + { + code: 321259, + msg: 'The route has not started and cannot end the route', + }, + { + code: 321513, + msg: 'Reach the height limit', + }, + { + code: 321514, + msg: 'Reach the limit', + }, + { + code: 321515, + msg: 'Crossing the restricted flight zone', + }, + { + code: 321516, + msg: 'Low limit', + }, + + { + code: 321517, + msg: 'Obstacle Avoidance', + }, + { + code: 321769, + msg: 'Weak GPS signal', + }, + { + code: 321770, + msg: 'The current gear state cannot be executed, B control seizes the control, and the gear is switched', + }, + { + code: 321771, + msg: 'The home point is not refreshed', + }, + { + code: 321772, + msg: 'The current battery is too low to start the task', + }, + { + code: 321773, + msg: 'Low battery return', + }, + { + code: 321776, + msg: 'RTK not ready', + }, + { + code: 321778, + msg: 'The aircraft is idling on the ground and is not allowed to start the route, thinking that the user is not ready.', + }, + { + code: 322282, + msg: 'User interrupt (B control takeover)', + }, + { + code: 514100, + msg: 'Command not supported', + }, + { + code: 514101, + msg: 'Failed to close putter', + }, + { + code: 514102, + msg: 'Failed to release putter', + }, + { + code: 514103, + msg: 'Aircraft battery is low', + }, + { + code: 514104, + msg: 'Failed to start charging', + }, + { + code: 514105, + msg: 'Failed to stop charging', + }, + { + code: 514106, + msg: 'Failed to restart the aircraft', + }, + { + code: 514107, + msg: 'Failed to open hatch', + }, + { + code: 514108, + msg: 'Failed to close hatch', + }, + { + code: 514109, + msg: 'Failed to open the plane', + }, + { + code: 514110, + msg: 'Failed to close the plane', + }, + { + code: 514111, + msg: 'The aircraft failed to turn on the slow-rotating propeller in the cabin', + }, + { + code: 514112, + msg: 'The aircraft failed to stop the slow-rotating propeller in the cabin', + }, + { + code: 514113, + msg: 'Failed to establish wired connection with aircraft', + }, + { + code: 514114, + msg: 'Get aircraft power status, command timed out, or return code is not 0', + }, + { + code: 514116, + msg: 'The DJI Dock is busy and other control orders are being executed at the DJI Dock', + }, + { + code: 514117, + msg: 'Check hatch status failed', + }, + { + code: 514118, + msg: 'Check putter status failed', + }, + { + code: 514120, + msg: 'DJI Dock and aircraft SDR connection failed', + }, + { + code: 514121, + msg: 'Emergency stop state', + }, + { + code: 514122, + msg: 'Failed to get the charging status of the aircraft (Failed to get the charging status, the flight mission can be executed, affecting charging and remote troubleshooting)', + }, + { + code: 514123, + msg: 'Unable to power on due to low battery', + }, + { + code: 514124, + msg: 'Failed to get battery information', + }, + { + code: 514125, + msg: 'The battery is fully charged and cannot be charged', + }, + { + code: 514145, + msg: 'Can not work while debugging on site', + }, + { + code: 514146, + msg: 'Unable to work in remote debugging', + }, + { + code: 514147, + msg: 'Unable to work in upgrade state', + }, + { + code: 514148, + msg: 'Unable to execute new tasks in job state', + }, + { + code: 514150, + msg: 'DJI Dock is automatically restarting', + }, +]