sean.zhou
2023-05-18 763e39bd71d83a6501ebdebf0d53130797bd5d73
Merge branch 'v1.5.0'
What's new?
1. Add new model: DJI Matrices 350 RTK.
2. Fixed some issues.
15 files modified
194 ■■■■■ changed files
package.json 1 ●●●● patch | view | raw | blame | history
src/components/g-map/DroneControlPanel.vue 32 ●●●● patch | view | raw | blame | history
src/components/g-map/use-drone-control-mqtt-event.ts 17 ●●●●● patch | view | raw | blame | history
src/components/g-map/use-drone-control-ws-event.ts 6 ●●●●● patch | view | raw | blame | history
src/components/g-map/use-manual-control.ts 7 ●●●● patch | view | raw | blame | history
src/components/g-map/use-mqtt.ts 2 ●●●●● patch | view | raw | blame | history
src/components/livestream-agora.vue 36 ●●●● patch | view | raw | blame | history
src/components/livestream-others.vue 2 ●●● patch | view | raw | blame | history
src/components/task/CreatePlan.vue 48 ●●●● patch | view | raw | blame | history
src/pages/page-pilot/pilot-home.vue 15 ●●●● patch | view | raw | blame | history
src/pages/page-web/projects/workspace.vue 1 ●●●● patch | view | raw | blame | history
src/store/index.ts 1 ●●●● patch | view | raw | blame | history
src/types/device.ts 4 ●●●● patch | view | raw | blame | history
src/types/drc.ts 13 ●●●● patch | view | raw | blame | history
src/types/task.ts 9 ●●●● patch | view | raw | blame | history
package.json
@@ -95,6 +95,7 @@
        "ant-design-vue/es/switch/style/css",
        "ant-design-vue/es/table/style/css",
        "ant-design-vue/es/tag/style/css",
        "ant-design-vue/es/time-picker/style/css",
        "ant-design-vue/es/tooltip/style/css",
        "ant-design-vue/es/tree/style/css",
        "ant-design-vue/es/upload/style/css",
src/components/g-map/DroneControlPanel.vue
@@ -118,7 +118,7 @@
              <span>Take off</span>
            </Button>
          </DroneControlPopover>
          <Button :loading="cmdItem.loading" size="small" ghost @click="sendControlCmd(cmdItem, index)">
          <Button :loading="cmdItem.loading" size="small" ghost @click="sendControlCmd(cmdItem, 0)">
          {{ cmdItem.operateText }}
          </Button>
        </div>
@@ -269,7 +269,7 @@
  return store.state.clientId
})
const initCmdList = baseCmdList.find(item => item.cmdKey === DeviceCmd.ReturnHome)
const initCmdList = baseCmdList.find(item => item.cmdKey === DeviceCmd.ReturnHome) as DeviceCmdItem
const cmdItem = ref(initCmdList)
const {
@@ -284,7 +284,10 @@
    action: cmdItem.action
  }, false)
  if (result && flightController.value) {
    message.success('Return home successful')
    exitFlightCOntrol()
  } else {
    message.error('Failed to return home')
  }
  cmdItem.loading = false
}
@@ -346,7 +349,7 @@
  maxSpeed: MAX_SPEED,
  rthAltitude: null as null | number,
  rcLostAction: LostControlActionInCommandFLight.RETURN_HOME,
  exitWaylineWhenRcLost: WaylineLostControlActionInCommandFlight.RETURN_HOME
  exitWaylineWhenRcLost: WaylineLostControlActionInCommandFlight.EXEC_LOST_ACTION
})
function onShowTakeoffToPointPopover () {
@@ -357,7 +360,7 @@
  takeoffToPointPopoverData.securityTakeoffHeight = null
  takeoffToPointPopoverData.rthAltitude = null
  takeoffToPointPopoverData.rcLostAction = LostControlActionInCommandFLight.RETURN_HOME
  takeoffToPointPopoverData.exitWaylineWhenRcLost = WaylineLostControlActionInCommandFlight.RETURN_HOME
  takeoffToPointPopoverData.exitWaylineWhenRcLost = WaylineLostControlActionInCommandFlight.EXEC_LOST_ACTION
}
async function onTakeoffToPointConfirm (confirm: boolean) {
@@ -396,10 +399,10 @@
useMqtt(deviceTopicInfo)
// 飞行控制
const drcState = computed(() => {
  return store.state.deviceState?.dockInfo[props.sn]?.link_osd?.drc_state === DrcStateEnum.CONNECTED
})
const flightController = ref(drcState.value)
// const drcState = computed(() => {
//   return store.state.deviceState?.dockInfo[props.sn]?.link_osd?.drc_state === DrcStateEnum.CONNECTED
// })
const flightController = ref(false)
async function onClickFightControl () {
  if (flightController.value) {
@@ -452,7 +455,7 @@
}
// drc mqtt message
const { drcInfo } = useDroneControlMqttEvent(props.sn)
const { drcInfo, errorInfo } = useDroneControlMqttEvent(props.sn)
const {
  handleKeyup,
@@ -688,6 +691,17 @@
  }
  cameraAimPopoverData.visible = false
}
watch(() => errorInfo, (errorInfo) => {
  if (errorInfo.value) {
    message.error(errorInfo.value)
    console.error(errorInfo.value)
    errorInfo.value = ''
  }
}, {
  immediate: true,
  deep: true
})
</script>
<style lang='scss' scoped>
src/components/g-map/use-drone-control-mqtt-event.ts
@@ -5,6 +5,7 @@
  DRCHsiInfo,
  DRCOsdInfo,
  DRCDelayTimeInfo,
  DrcResponseInfo,
} from '/@/types/drc'
export function useDroneControlMqttEvent (sn: string) {
@@ -12,6 +13,7 @@
  const hsiInfo = ref('')
  const osdInfo = ref('')
  const delayInfo = ref('')
  const errorInfo = ref('')
  function handleHsiInfo (data: DRCHsiInfo) {
    hsiInfo.value = `method: ${DRC_METHOD.HSI_INFO_PUSH}\r\n ${JSON.stringify(data)}\r\n `
@@ -23,6 +25,13 @@
  function handleDelayTimeInfo (data: DRCDelayTimeInfo) {
    delayInfo.value = `method: ${DRC_METHOD.DELAY_TIME_INFO_PUSH}\r\n ${JSON.stringify(data)}\r\n `
  }
  function handleDroneControlErrorInfo (data: DrcResponseInfo) {
    if (!data.result) {
      return
    }
    errorInfo.value = `Drc error code: ${data.result}, seq: ${data.output?.seq}`
  }
  function handleDroneControlMqttEvent (payload: any) {
@@ -43,6 +52,11 @@
        handleDelayTimeInfo(payload.data)
        break
      }
      case DRC_METHOD.DRONE_EMERGENCY_STOP:
      case DRC_METHOD.DRONE_CONTROL: {
        handleDroneControlErrorInfo(payload.data)
        break
      }
    }
    drcInfo.value = hsiInfo.value + osdInfo.value + delayInfo.value
  }
@@ -56,6 +70,7 @@
  })
  return {
    drcInfo: drcInfo
    drcInfo: drcInfo,
    errorInfo: errorInfo
  }
}
src/components/g-map/use-drone-control-ws-event.ts
@@ -19,12 +19,11 @@
    }
    if (data.type === ControlSourceChangeType.Payload && data.sn === payloadSn) {
      payloadControlSource.value = data.control_source
      message.info(`Payload control is changed to ${ payloadControlSource.value }.`)
      return
      message.info(`Payload control is changed to ${payloadControlSource.value}.`)
    }
  }
  function handleProgress(key: string, message: string, error: number) {
  function handleProgress (key: string, message: string, error: number) {
    if (error !== 0) {
      notification.error({
        key: key,
@@ -72,7 +71,6 @@
      }
      case EBizCode.DrcStatusNotify: {
        const { sn: deviceSn, result, message: msg } = payload.data as DrcStatusNotifyMessage
        if (deviceSn !== sn) return
        // handleProgress(EBizCode.DrcStatusNotify, `device(sn: ${deviceSn}) ${msg}`, result)
        break
src/components/g-map/use-manual-control.ts
@@ -30,7 +30,7 @@
export function useManualControl (deviceTopicInfo: DeviceTopicInfo, isCurrentFlightController: Ref<boolean>) {
  const activeCodeKey = ref(null) as Ref<KeyCode | null>
  const mqttHooks = useMqtt(deviceTopicInfo)
  let seq = 0
  function handlePublish (params: DroneControlProtocol) {
    const body = {
      method: DRC_METHOD.DRONE_CONTROL,
@@ -38,8 +38,9 @@
    }
    handleClearInterval()
    myInterval = setInterval(() => {
      body.data.seq = seq++
      seq++
      window.console.log('keyCode>>>>', activeCodeKey.value, body)
      body.data.seq = new Date().getTime()
      mqttHooks?.publishMqtt(deviceTopicInfo.pubTopic, body, { qos: 0 })
    }, 50)
  }
@@ -52,6 +53,7 @@
    const SPEED = 5 //  check
    const HEIGHT = 5 //  check
    const W_SPEED = 20 // 机头角速度
    seq = 0
    switch (keyCode) {
      case 'KeyA':
        if (activeCodeKey.value === keyCode) return
@@ -105,6 +107,7 @@
  function resetControlState () {
    activeCodeKey.value = null
    seq = 0
    handleClearInterval()
  }
src/components/g-map/use-mqtt.ts
@@ -60,6 +60,8 @@
        case DRC_METHOD.DELAY_TIME_INFO_PUSH:
        case DRC_METHOD.HSI_INFO_PUSH:
        case DRC_METHOD.OSD_INFO_PUSH:
        case DRC_METHOD.DRONE_CONTROL:
        case DRC_METHOD.DRONE_EMERGENCY_STOP:
          EventBus.emit('droneControlMqttInfo', payloadObj)
          break
        default:
src/components/livestream-agora.vue
@@ -76,7 +76,6 @@
        class="ml10"
        v-model:value="agoraPara.token"
        placeholder="Token"
        @change="encodeToken"
      ></a-input>
      <span class="ml10">Channel:</span>
      <a-input
@@ -169,7 +168,6 @@
  liveState: false
})
const nonSwitchable = 'normal'
const onRefresh = async () => {
  dronePara.droneList = []
  dronePara.cameraList = []
@@ -204,8 +202,11 @@
onMounted(() => {
  onRefresh()
  agoraPara.token = encodeURIComponent(agoraPara.token)
  agoraClient = AgoraRTC.createClient({ mode: 'live', codec: 'vp8' })
  agoraClient.setClientRole('audience', { level: 2 })
  if (agoraClient.connectionState === 'DISCONNECTED') {
    agoraClient.join(agoraPara.appid, agoraPara.channel, agoraPara.token)
  }
  // Subscribe when a remote user publishes a stream
  agoraClient.on('user-joined', async (user: IAgoraRTCRemoteUser) => {
    message.info('user[' + user.uid + '] join')
@@ -217,9 +218,7 @@
      // Get `RemoteVideoTrack` in the `user` object.
      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 = user.uid.toString()
      remoteVideoTrack.play(remotePlayerContainer)
      remoteVideoTrack.play(document.getElementById('player') as HTMLElement)
    }
  })
  agoraClient.on('user-unpublished', async (user: any) => {
@@ -231,15 +230,11 @@
    message.error(e.msg)
  })
})
const handleError = (err: any) => {
  console.error(err)
}
const handleJoinChannel = (uid: any) => {
  agoraPara.uid = uid
}
const encodeToken = (e: any) => {
  agoraPara.token = encodeURIComponent(agoraPara.token)
}
const onStart = async () => {
  const that = this
@@ -262,8 +257,7 @@
  }
  agoraClient.setClientRole('audience', { level: 2 })
  if (agoraClient.connectionState === 'DISCONNECTED') {
    agoraClient
      .join(agoraPara.appid, agoraPara.channel, agoraPara.token)
    await agoraClient.join(agoraPara.appid, agoraPara.channel, agoraPara.token)
  }
  livePara.videoId =
    dronePara.droneSelected +
@@ -277,7 +271,7 @@
    '&sn=' +
    dronePara.droneSelected +
    '&token=' +
    agoraPara.token +
    encodeURIComponent(agoraPara.token) +
    '&uid=' +
    agoraPara.uid
@@ -298,6 +292,14 @@
    })
}
const onStop = async () => {
  if (
    dronePara.droneSelected == null ||
    dronePara.cameraSelected == null ||
    dronePara.claritySelected == null
  ) {
    message.warn('waring: not select live para!!!')
    return
  }
  livePara.videoId =
    dronePara.droneSelected +
    '/' +
@@ -308,10 +310,10 @@
  }).then(res => {
    if (res.code === 0) {
      message.success(res.message)
      livePara.liveState = false
      dronePara.lensSelected = ''
      console.log('stop play livestream')
    }
    livePara.liveState = false
    dronePara.lensSelected = ''
    console.log('stop play livestream')
  })
}
const onDroneSelect = (val: SelectOption) => {
@@ -350,7 +352,7 @@
  dronePara.videoSelected = firstVideo.value
  dronePara.lensList = firstVideo.more
  dronePara.lensSelected = firstVideo.label
  dronePara.isDockLive = dronePara.lensList.length > 0
  dronePara.isDockLive = dronePara.lensList?.length > 0
}
const onVideoSelect = (val: SelectOption) => {
  dronePara.videoSelected = val.value
src/components/livestream-others.vue
@@ -388,7 +388,7 @@
  videoSelected.value = firstVideo.value
  lensList.value = firstVideo.more
  lensSelected.value = firstVideo.label
  isDockLive.value = lensList.value.length > 0
  isDockLive.value = lensList.value?.length > 0
}
const onVideoSelect = (val: SelectOption) => {
  videoSelected.value = val.value
src/components/task/CreatePlan.vue
@@ -4,8 +4,8 @@
      Create Plan
    </div>
    <div class="content">
      <a-form ref="valueRef" layout="horizontal" :hideRequiredMark="true" :rules="rules" :model="planBody">
        <a-form-item label="Plan Name" name="name" :labelCol="{span: 24}">
      <a-form ref="valueRef" layout="horizontal" :hideRequiredMark="true" :rules="rules" :model="planBody" labelAlign="left">
        <a-form-item label="Plan Name" name="name" :labelCol="{span: 23}">
          <a-input style="background: black;"  placeholder="Please enter plan name" v-model:value="planBody.name"/>
        </a-form-item>
        <!-- 航线 -->
@@ -62,31 +62,29 @@
          </div>
        </a-form-item>
        <!-- 任务类型 -->
        <a-form-item label="Plan Timer" class="plan-timer-form-item">
        <a-form-item label="Plan Timer" class="plan-timer-form-item" :labelCol="{span: 23}">
          <div style="white-space: nowrap;">
            <a-radio-group v-model:value="planBody.task_type" button-style="solid">
              <a-radio-button :value="TaskType.Immediate">Immediate</a-radio-button>
              <a-radio-button :value="TaskType.Single">Timed&One-Time</a-radio-button>
              <a-radio-button v-for="type in TaskTypeOptions" :value="type.value" :key="type.value">{{ type.label }}</a-radio-button>
            </a-radio-group>
          </div>
        </a-form-item>
        <!-- 执行时间 -->
        <a-form-item label="Start Time" v-if="planBody.task_type === TaskType.Single" name="select_execute_time">
        <a-form-item label="Start Time" v-if="planBody.task_type === TaskType.Timed" name="select_execute_time" :labelCol="{span: 23}">
          <a-date-picker
            v-model:value="planBody.select_execute_time"
            format="YYYY-MM-DD HH:mm:ss"
            show-time
              show-time
            placeholder="Select Time"
            style="width: 280px;"
          />
            />
        </a-form-item>
        <!-- RTH Altitude Relative to Dock -->
        <a-form-item label="RTH Altitude Relative to Dock (m)" :labelCol="{span: 24}" name="rth_altitude">
          <a-input v-model:value="planBody.rth_altitude" style="background: black !important;">
          </a-input>
        <a-form-item label="RTH Altitude Relative to Dock (m)" :labelCol="{span: 23}" name="rth_altitude">
          <a-input-number v-model:value="planBody.rth_altitude" :min="20" :max="1500" class="width-100" required>
          </a-input-number>
        </a-form-item>
        <!-- Lost Action -->
        <a-form-item label="Lost Action" :labelCol="{span: 24}" name="out_of_control_action">
        <a-form-item label="Lost Action" :labelCol="{span: 23}" name="out_of_control_action">
          <div style="white-space: nowrap;">
            <a-radio-group v-model:value="planBody.out_of_control_action" button-style="solid">
              <a-radio-button v-for="action in OutOfControlActionOptions" :value="action.value" :key="action.value">
@@ -95,7 +93,7 @@
            </a-radio-group>
          </div>
        </a-form-item>
        <a-form-item style="width: 280px;">
        <a-form-item class="width-100" style="margin-bottom: 40px;">
          <div class="footer">
            <a-button class="mr10" style="background: #3c3c3c;" @click="closePlan">Cancel
            </a-button>
@@ -106,7 +104,7 @@
      </a-form>
    </div>
  </div>
  <div v-if="drawerVisible" style="position: absolute; left: 330px; width: 280px; height: 100vh; float: right; top: 0; z-index: 1000; color: white; background: #282828;">
  <div v-if="drawerVisible" style="position: absolute; left: 335px; width: 280px; height: 100vh; float: right; top: 0; z-index: 1000; color: white; background: #282828;">
    <div>
      <router-view :name="routeName"/>
    </div>
@@ -118,14 +116,14 @@
<script lang="ts" setup>
import { computed, onMounted, onUnmounted, reactive, ref, toRaw, UnwrapRef } from 'vue'
import { CloseOutlined, RocketOutlined, CameraFilled, UserOutlined } from '@ant-design/icons-vue'
import { CloseOutlined, RocketOutlined, CameraFilled, UserOutlined, PlusCircleOutlined, MinusCircleOutlined } from '@ant-design/icons-vue'
import { ELocalStorageKey, ERouterName } from '/@/types'
import { useMyStore } from '/@/store'
import { WaylineType, WaylineFile } from '/@/types/wayline'
import { Device, EDeviceType } from '/@/types/device'
import { createPlan, CreatePlan } from '/@/api/wayline'
import { getRoot } from '/@/root'
import { TaskType, OutOfControlActionOptions, OutOfControlAction } from '/@/types/task'
import { TaskType, OutOfControlActionOptions, OutOfControlAction, TaskTypeOptions } from '/@/types/task'
import moment, { Moment } from 'moment'
import { RuleObject } from 'ant-design-vue/es/form/interface'
@@ -192,9 +190,7 @@
    // console.log('planBody', createPlanBody)
    createPlan(workspaceId, createPlanBody)
      .then(res => {
        setTimeout(() => {
          disabled.value = false
        }, 1500)
        disabled.value = false
      }).finally(() => {
        closePlan()
      })
@@ -231,6 +227,7 @@
  height: 100vh;
  display: flex;
  flex-direction: column;
  width: 285px;
  .header {
    height: 52px;
@@ -242,6 +239,10 @@
    align-items: center;
  }
  ::-webkit-scrollbar {
    display: none;
  }
  .content {
    height: calc(100% - 54px);
    overflow-y: auto;
@@ -250,7 +251,8 @@
      margin: 10px;
    }
    form label, input {
    form label, input, .ant-input, .ant-calendar-range-picker-separator,
    .ant-input:hover, .ant-time-picker .anticon, .ant-calendar-picker .anticon {
      background-color: #232323;
      color: #fff;
    }
@@ -260,12 +262,12 @@
    }
    .plan-timer-form-item {
      // flex-direction: column;
      .ant-radio-button-wrapper{
        background-color: #232323;
        color: #fff;
        width: 80%;
        text-align: center;
        &.ant-radio-button-wrapper-checked{
          background-color: #1890ff;
        }
src/pages/page-pilot/pilot-home.vue
@@ -67,7 +67,7 @@
            <span style="color: #737373">{{ device.data.gateway_sn }}</span>
          </a-col>
        </a-row>
        <a-row style="border-bottom: 1px solid #f4f8f9; height: 45px;" align="middle" v-if="device.data.online_status">
        <a-row style="border-bottom: 1px solid #f4f8f9; height: 45px;" align="middle" v-if="device.data.online_status && device.data.sn">
          <a-col :span="1"></a-col>
          <a-col :span="9">Aircraft Sn</a-col>
          <a-col :span="13" class="flex-align-end flex-column" >
@@ -79,7 +79,7 @@
        <span class="ml5" style="color: #939393;">Settings</span>
      </div>
      <div class="fz16" style="background-color: white; border-radius: 4px;">
        <a-row v-if="device.data.online_status" style="border-bottom: 1px solid #f4f8f9; height: 45px;" align="middle" @click="bindingDevice">
        <a-row v-if="device.data.online_status && device.data.sn" style="border-bottom: 1px solid #f4f8f9; height: 45px;" align="middle" @click="bindingDevice">
          <a-col :span="1"></a-col>
          <a-col :span="11">
            Device Binding
@@ -251,6 +251,10 @@
onMounted(() => {
  apiPilot.onBackClickReg()
  apiPilot.onStopPlatform()
  const oldDevice = localStorage.getItem(ELocalStorageKey.Device)
  if (oldDevice) {
    device.data = JSON.parse(oldDevice)
  }
  window.connectCallback = (arg: any) => {
    connectCallback(arg)
@@ -263,10 +267,7 @@
    message.warn('Data is not available, please restart the remote control.')
    return
  }
  const oldDevice = localStorage.getItem(ELocalStorageKey.Device)
  if (oldDevice) {
    device.data = JSON.parse(oldDevice)
  }
  device.data.sn = apiPilot.getAircraftSN()
  getDeviceInfo()
@@ -486,7 +487,7 @@
}
function getDeviceInfo () {
  if (device.data.sn === EStatusValue.DISCONNECT) {
  if (!device.data.sn || device.data.sn === EStatusValue.DISCONNECT) {
    return
  }
  getDeviceBySn(bindParam.workspace_id, device.data.sn).then(res => {
src/pages/page-web/projects/workspace.vue
@@ -136,6 +136,7 @@
    .main-content {
      flex: 1;
      color: $text-white-basic;
      width: 285px;
    }
  }
src/store/index.ts
@@ -130,7 +130,6 @@
    }
    if (info.host.job_number !== undefined) {
      dock.work_osd = info.host
      return
    }
  },
  SET_DRAW_VISIBLE_INFO (state, bool) {
src/types/device.ts
@@ -23,6 +23,7 @@
  M30 = 67,
  M300 = 60,
  Mavic3EnterpriseAdvanced= 77,
  M350 = 89,
}
// DJI负载类型枚举值
@@ -74,6 +75,7 @@
  M3T: `${DOMAIN.DRONE}-${DRONE_TYPE.Mavic3EnterpriseAdvanced}-${DEVICE_SUB_TYPE.ONE}`,
  M300: `${DOMAIN.DRONE}-${DRONE_TYPE.M300}-${DEVICE_SUB_TYPE.ZERO}`,
  M350: `${DOMAIN.DRONE}-${DRONE_TYPE.M350}-${DEVICE_SUB_TYPE.ZERO}`,
  FPV: `${DOMAIN.PAYLOAD}-${PAYLOAD_TYPE.FPV}-${DEVICE_SUB_TYPE.ZERO}`,
  H20: `${DOMAIN.PAYLOAD}-${PAYLOAD_TYPE.H20}-${DEVICE_SUB_TYPE.ZERO}`,
@@ -107,6 +109,7 @@
  [DEVICE_MODEL_KEY.M3T]: 'Mavic 3T',
  // [DEVICE_MODEL_KEY.M3M]: 'Mavic 3M',
  [DEVICE_MODEL_KEY.M300]: 'M300 RTK',
  [DEVICE_MODEL_KEY.M350]: 'M350 RTK',
  // payload
  [DEVICE_MODEL_KEY.FPV]: 'FPV',
@@ -464,6 +467,7 @@
  M30 = '0-67-0' as any,
  M30T = '0-67-1' as any,
  M300 = '0-60-0' as any,
  M350 = DEVICE_MODEL_KEY.M350 as any,
  Z30 = '1-20-0' as any,
  XT2 = '1-26-0' as any,
  FPV = '1-39-0' as any,
src/types/drc.ts
@@ -13,13 +13,7 @@
  y?: number; // 前进后退方向速度,正值为W指令  负值为S指令 单位:m/s
  h?: number;// 上下高度值,正值为上升指令  负值为下降指令 单位:m
  w?: number; // 机头角速度,正值为顺时针,负值为逆时针 单位:degree/s   (web端暂无此设计)
  step_x?: number; // 水平方向步长
  step_y?: number; // 前后方向步长
  step_h?: number; // 高度方向步长
  step_w?: number; // 机头转向步长
  seq?: number; // 从0计时
  freq?: number; // 指令发送频率
  delay_time?: number; // 指令从发送到设备端接收可容忍的时间 发送频率+链路传输时长
}
// 低延时osd
@@ -69,3 +63,10 @@
  sdr_cmd_delay: number; // sdr链路命令延时,单位:ms
  liveview_delay_list: LiveViewDelayItem[];
}
export interface DrcResponseInfo {
  result: number;
  output: {
    seq: number
  }
}
src/types/task.ts
@@ -3,14 +3,19 @@
// 任务类型
export enum TaskType {
  Immediate = 0, // 立即执行
  Single = 1, // 单次定时任务
  Timed = 1, // 单次定时任务
}
export const TaskTypeMap = {
  [TaskType.Immediate]: 'Immediate',
  [TaskType.Single]: 'Timed & One-Time',
  [TaskType.Timed]: 'Timed',
}
export const TaskTypeOptions = [
  { value: TaskType.Immediate, label: TaskTypeMap[TaskType.Immediate] },
  { value: TaskType.Timed, label: TaskTypeMap[TaskType.Timed] },
]
// 失控动作
export enum OutOfControlAction {
  ReturnToHome = 0,