Browse Source

Merge branch 'v1.7.0'

v1.8.0
sean.zhou 1 year ago
parent
commit
91f0f86730
  1. 18
      src/api/drone-control/drone.ts
  2. 8
      src/api/http/config.ts
  3. 6
      src/api/wayline.ts
  4. 118
      src/components/GMap.vue
  5. 7
      src/components/g-map/DockControlPanel.vue
  6. 91
      src/components/g-map/DroneControlPanel.vue
  7. 6
      src/components/g-map/use-dock-control.ts
  8. 2
      src/components/livestream-agora.vue
  9. 2
      src/components/livestream-others.vue
  10. 157
      src/components/task/CreatePlan.vue
  11. 4
      src/constants/map.ts
  12. 38
      src/directives/drag-window.ts
  13. 6
      src/directives/index.ts
  14. 37
      src/hooks/use-g-map-cover.ts
  15. 4
      src/main.ts
  16. 10
      src/pages/page-pilot/pilot-home.vue
  17. 28
      src/pages/page-pilot/pilot-liveshare.vue
  18. 41
      src/pages/page-web/projects/layer.vue
  19. 14
      src/pages/page-web/projects/livestream.vue
  20. 11
      src/pages/page-web/projects/tsa.vue
  21. 8
      src/router/index.ts
  22. 10
      src/store/index.ts
  23. 9
      src/types/device-cmd.ts
  24. 4
      src/types/device.ts
  25. 17
      src/types/drone-control.ts
  26. 3
      src/types/task.ts
  27. 5
      tsconfig.json

18
src/api/drone-control/drone.ts

@ -18,6 +18,18 @@ export enum LostControlActionInCommandFLight {
Land = 1, // 着陆 Land = 1, // 着陆
RETURN_HOME = 2, // 返航 RETURN_HOME = 2, // 返航
} }
export enum ERthMode {
SMART = 0,
SETTING = 1
}
export enum ECommanderModeLostAction {
CONTINUE = 0,
EXEC_LOST_ACTION = 1
}
export enum ECommanderFlightMode {
SMART = 0,
SETTING = 1
}
export interface PointBody { export interface PointBody {
latitude: number; latitude: number;
longitude: number; longitude: number;
@ -48,7 +60,11 @@ export interface PostTakeoffToPointBody{
max_speed: number; // flyto过程中能达到的最大速度, 单位m/s 跟飞机档位有关 max_speed: number; // flyto过程中能达到的最大速度, 单位m/s 跟飞机档位有关
rc_lost_action: LostControlActionInCommandFLight; // 失控行为 rc_lost_action: LostControlActionInCommandFLight; // 失控行为
rth_altitude: number; // 返航高度 rth_altitude: number; // 返航高度
exit_wayline_when_rc_lost: WaylineLostControlActionInCommandFlight exit_wayline_when_rc_lost: WaylineLostControlActionInCommandFlight;
rth_mode: ERthMode;
commander_mode_lost_action: ECommanderModeLostAction;
commander_flight_mode: ECommanderFlightMode;
commander_flight_height: number;
} }
// 一键起飞 // 一键起飞

8
src/api/http/config.ts

@ -1,17 +1,17 @@
export const CURRENT_CONFIG = { export const CURRENT_CONFIG = {
// license // license
appId: 'Please enter the app id.', // You need to go to the development website to apply. appId: 'Please enter the app id.', // You need to go to the development website to apply.
appKey: 'Please enter the app key.', // You need to go to the development website to apply. appKey: 'Please enter the app key.', // You need to go to the development website to apply.
appLicense: 'Please enter the app license.', // You need to go to the development website to apply. appLicense: 'Please enter the app license.', // You need to go to the development website to apply.
// http // http
baseURL: 'Please enter the backend access address prefix.', // This url must end with "/". Example: 'http://192.168.1.1:6789/' baseURL: 'Please enter the backend access address prefix.', // This url must end with "/". Example: 'http://192.168.1.1:6789/'
websocketURL: 'Please enter the WebSocket access address.', // Example: 'ws://192.168.1.1:6789/api/v1/ws' websocketURL: 'Please enter the WebSocket access address.', // Example: 'ws://192.168.1.1:6789/api/v1/ws'
// livestreaming // livestreaming
// RTMP Note: This IP is the address of the streaming server. If you want to see livestream on web page, you need to convert the RTMP stream to WebRTC stream. // RTMP Note: This IP is the address of the streaming server. If you want to see livestream on web page, you need to convert the RTMP stream to WebRTC stream.
rtmpURL: 'Please enter the rtmp access address.', // Example: 'rtmp://192.168.1.1/live/' rtmpURL: 'Please enter the rtmp access address.', // Example: 'rtmp://192.168.1.1/live/'
// GB28181 Note:If you don't know what these parameters mean, you can go to Pilot2 and select the GB28181 page in the cloud platform. Where the parameters same as these parameters. // GB28181 Note:If you don't know what these parameters mean, you can go to Pilot2 and select the GB28181 page in the cloud platform. Where the parameters same as these parameters.
gbServerIp: 'Please enter the server ip.', gbServerIp: 'Please enter the server ip.',
gbServerPort: 'Please enter the server port.', gbServerPort: 'Please enter the server port.',

6
src/api/wayline.ts

@ -42,10 +42,12 @@ export interface CreatePlan {
dock_sn: string, dock_sn: string,
task_type: TaskType, // 任务类型 task_type: TaskType, // 任务类型
wayline_type: WaylineType, // 航线类型 wayline_type: WaylineType, // 航线类型
task_days?: number[] // 执行任务的日期(秒) task_days: number[] // 执行任务的日期(秒)
task_periods?: number[][] // 执行任务的时间点(秒) task_periods: number[][] // 执行任务的时间点(秒)
rth_altitude: number // 相对机场返航高度 20 - 500 rth_altitude: number // 相对机场返航高度 20 - 500
out_of_control_action: OutOfControlAction // 失控动作 out_of_control_action: OutOfControlAction // 失控动作
min_battery_capacity?: number, // The minimum battery capacity of aircraft.
min_storage_capacity?: number, // The minimum storage capacity of dock and aircraft.
} }
// Create Wayline Job // Create Wayline Job

118
src/components/GMap.vue

@ -21,8 +21,8 @@
</div> </div>
</div> </div>
<!-- 飞机OSD --> <!-- 飞机OSD -->
<div v-if="osdVisible.visible && !osdVisible.is_dock" class="osd-panel fz12"> <div v-if="osdVisible.visible && !osdVisible.is_dock" v-drag-window class="osd-panel fz12">
<div class="pl5 pr5 flex-align-center flex-row flex-justify-between" style="border-bottom: 1px solid #515151; height: 18%;"> <div class="drag-title pl5 pr5 flex-align-center flex-row flex-justify-between" style="border-bottom: 1px solid #515151; height: 18%;">
<span>{{ osdVisible.callsign }}</span> <span>{{ osdVisible.callsign }}</span>
<span><a class="fz16" style="color: white;" @click="() => osdVisible.visible = false"><CloseOutlined /></a></span> <span><a class="fz16" style="color: white;" @click="() => osdVisible.visible = false"><CloseOutlined /></a></span>
</div> </div>
@ -49,7 +49,7 @@
<a-col span="6"> <a-col span="6">
<a-tooltip title="RC Battery Level"> <a-tooltip title="RC Battery Level">
<span><ThunderboltOutlined class="fz14"/></span> <span><ThunderboltOutlined class="fz14"/></span>
<span class="ml10">{{ deviceInfo.gateway && deviceInfo.gateway.capacity_percent !== str ? deviceInfo.gateway.capacity_percent + ' %' : deviceInfo.gateway.capacity_percent }}</span> <span class="ml10">{{ deviceInfo.gateway && deviceInfo.gateway.capacity_percent !== str ? deviceInfo.gateway?.capacity_percent + ' %' : deviceInfo.gateway?.capacity_percent }}</span>
</a-tooltip> </a-tooltip>
</a-col> </a-col>
@ -141,11 +141,11 @@
</div> </div>
</div> </div>
<!-- 机场OSD --> <!-- 机场OSD -->
<div v-if="osdVisible.visible && osdVisible.is_dock" class="osd-panel fz12"> <div v-if="osdVisible.visible && osdVisible.is_dock" v-drag-window class="osd-panel fz12">
<div class="fz16 pl5 pr5 flex-align-center flex-row flex-justify-between" style="border-bottom: 1px solid #515151; height: 10%;"> <div class="drag-title fz16 pl5 pr5 flex-align-center flex-row flex-justify-between" style="border-bottom: 1px solid #515151; height: 10%;">
<span>{{ osdVisible.gateway_callsign }}</span> <span>{{ osdVisible.gateway_callsign }}</span>
<span><a style="color: white;" @click="() => osdVisible.visible = false"><CloseOutlined /></a></span>
</div> </div>
<span><a style="color: white; position: absolute; top: 5px; right: 5px;" @click="() => osdVisible.visible = false"><CloseOutlined /></a></span>
<!-- 机场 --> <!-- 机场 -->
<div class ="flex-display" style="border-bottom: 1px solid #515151;"> <div class ="flex-display" style="border-bottom: 1px solid #515151;">
<div class="flex-column flex-align-stretch flex-justify-center" style="width: 60px; background: #2d2d2d;"> <div class="flex-column flex-align-stretch flex-justify-center" style="width: 60px; background: #2d2d2d;">
@ -267,7 +267,7 @@
<a-col span="6"> <a-col span="6">
<a-tooltip title="Drone in dock"> <a-tooltip title="Drone in dock">
<span><RocketOutlined /></span> <span><RocketOutlined /></span>
<span class="ml10">{{ DroneInDockEnum[deviceInfo.dock.basic_osd?.drone_in_dock] }}</span> <span class="ml10">{{ deviceInfo.dock.basic_osd?.drone_in_dock }}</span>
</a-tooltip> </a-tooltip>
</a-col> </a-col>
</a-row> </a-row>
@ -413,6 +413,17 @@
<!-- 飞行指令 --> <!-- 飞行指令 -->
<DroneControlPanel :sn="osdVisible.gateway_sn" :deviceInfo="deviceInfo" :payloads="osdVisible.payloads"></DroneControlPanel> <DroneControlPanel :sn="osdVisible.gateway_sn" :deviceInfo="deviceInfo" :payloads="osdVisible.payloads"></DroneControlPanel>
</div> </div>
<!-- liveview -->
<div class="liveview" v-if="livestreamOthersVisible" v-drag-window >
<div style="height: 40px; width: 100%" class="drag-title"></div>
<a style="position: absolute; right: 10px; top: 10px; font-size: 16px; color: white;" @click="closeLivestreamOthers"><CloseOutlined /></a>
<LivestreamOthers />
</div>
<div class="liveview" v-if="livestreamAgoraVisible" v-drag-window >
<div style="height: 40px; width: 100%" class="drag-title"></div>
<a style="position: absolute; right: 10px; top: 10px; font-size: 16px; color: white;" @click="closeLivestreamAgora"><CloseOutlined /></a>
<LivestreamAgora />
</div>
</div> </div>
</template> </template>
@ -452,6 +463,8 @@ import DockControlPanel from './g-map/DockControlPanel.vue'
import { useDockControl } from './g-map/use-dock-control' import { useDockControl } from './g-map/use-dock-control'
import DroneControlPanel from './g-map/DroneControlPanel.vue' import DroneControlPanel from './g-map/DroneControlPanel.vue'
import { useConnectMqtt } from './g-map/use-connect-mqtt' import { useConnectMqtt } from './g-map/use-connect-mqtt'
import LivestreamOthers from './livestream-others.vue'
import LivestreamAgora from './livestream-agora.vue'
export default defineComponent({ export default defineComponent({
components: { components: {
@ -475,7 +488,9 @@ export default defineComponent({
DockControlPanel, DockControlPanel,
DroneControlPanel, DroneControlPanel,
CarryOutOutlined, CarryOutOutlined,
RocketOutlined RocketOutlined,
LivestreamOthers,
LivestreamAgora
}, },
name: 'GMap', name: 'GMap',
props: {}, props: {},
@ -534,6 +549,12 @@ export default defineComponent({
const drawVisible = computed(() => { const drawVisible = computed(() => {
return store.state.drawVisible return store.state.drawVisible
}) })
const livestreamOthersVisible = computed(() => {
return store.state.livestreamOthersVisible
})
const livestreamAgoraVisible = computed(() => {
return store.state.livestreamAgoraVisible
})
const osdVisible = computed(() => { const osdVisible = computed(() => {
return store.state.osdVisible return store.state.osdVisible
}) })
@ -572,7 +593,7 @@ export default defineComponent({
} }
} }
if (data.currentType === EDeviceTypeName.Dock && data.dockInfo[data.currentSn]) { if (data.currentType === EDeviceTypeName.Dock && data.dockInfo[data.currentSn]) {
deviceTsaUpdateHook.initMarker(EDeviceTypeName.Dock, [EDeviceTypeName.Dock], data.currentSn, data.dockInfo[data.currentSn].basic_osd?.longitude, data.dockInfo[data.currentSn].basic_osd?.latitude) deviceTsaUpdateHook.initMarker(EDeviceTypeName.Dock, EDeviceTypeName[EDeviceTypeName.Dock], data.currentSn, data.dockInfo[data.currentSn].basic_osd?.longitude, data.dockInfo[data.currentSn].basic_osd?.latitude)
if (osdVisible.value.visible && osdVisible.value.is_dock && osdVisible.value.gateway_sn !== '') { if (osdVisible.value.visible && osdVisible.value.is_dock && osdVisible.value.gateway_sn !== '') {
deviceInfo.dock = data.dockInfo[osdVisible.value.gateway_sn] deviceInfo.dock = data.dockInfo[osdVisible.value.gateway_sn]
deviceInfo.device = data.deviceInfo[deviceInfo.dock.basic_osd.sub_device?.device_sn ?? osdVisible.value.sn] deviceInfo.device = data.deviceInfo[deviceInfo.dock.basic_osd.sub_device?.device_sn ?? osdVisible.value.sn]
@ -607,15 +628,27 @@ export default defineComponent({
}) })
updateCoordinates('wgs84-gcj02', ele) updateCoordinates('wgs84-gcj02', ele)
useGMapCoverHook.init2DPin( const data = { id: ele.id, name: ele.name }
ele.name, if (MapElementEnum.PIN === ele.resource?.type) {
ele.resource.content.geometry.coordinates, useGMapCoverHook.init2DPin(
ele.resource.content.properties.color, ele.name,
{ ele.resource.content.geometry.coordinates,
id: ele.id, ele.resource.content.properties.color,
name: ele.name data
} )
) } else if (MapElementEnum.LINE === ele.resource?.type) {
useGMapCoverHook.initPolyline(
ele.name,
ele.resource.content.geometry.coordinates,
ele.resource.content.properties.color,
data)
} else if (MapElementEnum.POLY === ele.resource?.type) {
useGMapCoverHook.initPolygon(
ele.name,
ele.resource.content.geometry.coordinates,
ele.resource.content.properties.color,
data)
}
} }
store.state.wsEvent.mapElementCreat = {} store.state.wsEvent.mapElementCreat = {}
@ -749,6 +782,12 @@ export default defineComponent({
console.log('layers', layers) console.log('layers', layers)
store.commit('SET_LAYER_INFO', layers) store.commit('SET_LAYER_INFO', layers)
} }
function closeLivestreamOthers () {
store.commit('SET_LIVESTREAM_OTHERS_VISIBLE', false)
}
function closeLivestreamAgora () {
store.commit('SET_LIVESTREAM_AGORA_VISIBLE', false)
}
function updateCoordinates (transformType: string, element: any) { function updateCoordinates (transformType: string, element: any) {
const geoType = element.resource?.content.geometry.type const geoType = element.resource?.content.geometry.type
const type = element.resource?.type as number const type = element.resource?.type as number
@ -769,39 +808,38 @@ export default defineComponent({
) as GeojsonCoordinate ) as GeojsonCoordinate
element.resource.content.geometry.coordinates = transResult element.resource.content.geometry.coordinates = transResult
} }
} else if (MapElementEnum.LINE === type && geoType === 'LineString') { } else if (MapElementEnum.LINE === type) {
const coordinates = element.resource?.content.geometry const coordinates = element.resource?.content.geometry
.coordinates as GeojsonCoordinate[] .coordinates as GeojsonCoordinate[]
if (transformType === 'wgs84-gcj02') { if (transformType === 'wgs84-gcj02') {
coordinates.forEach(coordinate => { coordinates.forEach((coordinate, i, arr) => {
coordinate = wgs84togcj02( arr[i] = wgs84togcj02(
coordinate[0], coordinate[0],
coordinate[1] coordinate[1]
) as GeojsonCoordinate ) as GeojsonCoordinate
}) })
} else if (transformType === 'gcj02-wgs84') { } else if (transformType === 'gcj02-wgs84') {
coordinates.forEach(coordinate => { coordinates.forEach((coordinate, i, arr) => {
coordinate = gcj02towgs84( arr[i] = gcj02towgs84(
coordinate[0], coordinate[0],
coordinate[1] coordinate[1]
) as GeojsonCoordinate ) as GeojsonCoordinate
}) })
} }
element.resource.content.geometry.coordinates = coordinates element.resource.content.geometry.coordinates = coordinates
} else if (MapElementEnum.LINE === type && geoType === 'Polygon') { } else if (MapElementEnum.POLY === type) {
const coordinates = element.resource?.content.geometry const coordinates = element.resource?.content.geometry
.coordinates[0] as GeojsonCoordinate[] .coordinates[0] as GeojsonCoordinate[]
if (transformType === 'wgs84-gcj02') { if (transformType === 'wgs84-gcj02') {
coordinates.forEach(coordinate => { coordinates.forEach((coordinate, i, arr) => {
coordinate = wgs84togcj02( arr[i] = wgs84togcj02(
coordinate[0], coordinate[0],
coordinate[1] coordinate[1]
) as GeojsonCoordinate ) as GeojsonCoordinate
}) })
} else if (transformType === 'gcj02-wgs84') { } else if (transformType === 'gcj02-wgs84') {
coordinates.forEach(coordinate => { coordinates.forEach((coordinate, i, arr) => {
coordinate = gcj02towgs84( arr[i] = gcj02towgs84(
coordinate[0], coordinate[0],
coordinate[1] coordinate[1]
) as GeojsonCoordinate ) as GeojsonCoordinate
@ -815,6 +853,8 @@ export default defineComponent({
draw, draw,
mouseMode, mouseMode,
drawVisible, drawVisible,
livestreamOthersVisible,
livestreamAgoraVisible,
osdVisible, osdVisible,
pin, pin,
state, state,
@ -830,7 +870,9 @@ export default defineComponent({
NetworkStateTypeEnum, NetworkStateTypeEnum,
NetworkStateQualityEnum, NetworkStateQualityEnum,
RainfallEnum, RainfallEnum,
DroneInDockEnum DroneInDockEnum,
closeLivestreamOthers,
closeLivestreamAgora
} }
} }
}) })
@ -875,7 +917,8 @@ export default defineComponent({
} }
.osd-panel { .osd-panel {
position: absolute; position: absolute;
left: 10px; margin-left: 10px;
left: 0;
top: 10px; top: 10px;
width: 480px; width: 480px;
background: #000; background: #000;
@ -926,4 +969,17 @@ export default defineComponent({
min-height: 2px; min-height: 2px;
border-radius: 2px; border-radius: 2px;
} }
.liveview {
position: absolute;
color: #fff;
z-index: 1;
left: 0;
margin-left: 10px;
top: 10px;
text-align: center;
width: 800px;
height: 720px;
background: #232323;
}
</style> </style>

7
src/components/g-map/DockControlPanel.vue

@ -73,15 +73,14 @@ watch(() => props.deviceInfo, (value) => {
deep: true deep: true
}) })
// dock
const debugStatus = ref(props.deviceInfo.dock?.basic_osd?.mode_code === EDockModeCode.Remote_Debugging)
const emit = defineEmits(['close-control-panel']) const emit = defineEmits(['close-control-panel'])
function closeControlPanel () { function closeControlPanel () {
emit('close-control-panel', props.sn) emit('close-control-panel', props.sn, debugStatus.value)
} }
// dock
const debugStatus = ref(props.deviceInfo.dock?.basic_osd.mode_code === EDockModeCode.Remote_Debugging)
async function onDeviceStatusChange (status: boolean) { async function onDeviceStatusChange (status: boolean) {
let result = false let result = false
if (status) { if (status) {

91
src/components/g-map/DroneControlPanel.vue

@ -112,15 +112,53 @@
:options="WaylineLostControlActionInCommandFlightOptions" :options="WaylineLostControlActionInCommandFlightOptions"
></a-select> ></a-select>
</div> </div>
<div>
<span class="form-label">Return-to-Home Mode:</span>
<a-select
v-model:value="takeoffToPointPopoverData.rthMode"
style="width: 120px"
:options="RthModeInCommandFlightOptions"
></a-select>
</div>
<div>
<span class="form-label">Commander Mode Lost Action:</span>
<a-select
v-model:value="takeoffToPointPopoverData.commanderModeLostAction"
style="width: 120px"
:options="CommanderModeLostActionInCommandFlightOptions"
></a-select>
</div>
<div>
<span class="form-label">Commander Flight Mode:</span>
<a-select
v-model:value="takeoffToPointPopoverData.commanderFlightMode"
style="width: 120px"
:options="CommanderFlightModeInCommandFlightOptions"
></a-select>
</div>
<div>
<span class="form-label">Commander Flight Height(m):</span>
<a-input-number v-model:value="takeoffToPointPopoverData.commanderFlightHeight"/>
</div>
</div> </div>
</template> </template>
<Button size="small" ghost @click="onShowTakeoffToPointPopover" > <Button size="small" ghost @click="onShowTakeoffToPointPopover" >
<span>Take off</span> <span>Take off</span>
</Button> </Button>
<div v-for="(cmdItem) in cmdList" :key="cmdItem.cmdKey" class="control-cmd-item">
<Button :loading="cmdItem.loading" size="small" ghost @click="sendControlCmd(cmdItem, 0)">
{{ cmdItem.operateText }}
</Button>
</div>
<div>
<Button size="small" ghost @click="openLivestreamAgora" >
<span>Agora Live</span>
</Button>
<Button size="small" ghost @click="openLivestreamOthers" >
<span>RTMP/GB28181 Live</span>
</Button>
</div>
</DroneControlPopover> </DroneControlPopover>
<Button :loading="cmdItem.loading" size="small" ghost @click="sendControlCmd(cmdItem, 0)">
{{ cmdItem.operateText }}
</Button>
</div> </div>
</div> </div>
<div class="box"> <div class="box">
@ -250,9 +288,15 @@ import { usePayloadControl } from './use-payload-control'
import { CameraMode, CameraType, CameraTypeOptions, ZoomCameraTypeOptions, CameraListItem } from '/@/types/live-stream' import { CameraMode, CameraType, CameraTypeOptions, ZoomCameraTypeOptions, CameraListItem } from '/@/types/live-stream'
import { useDroneControlWsEvent } from './use-drone-control-ws-event' import { useDroneControlWsEvent } from './use-drone-control-ws-event'
import { useDroneControlMqttEvent } from './use-drone-control-mqtt-event' import { useDroneControlMqttEvent } from './use-drone-control-mqtt-event'
import { postFlightAuth, LostControlActionInCommandFLight, WaylineLostControlActionInCommandFlight } from '/@/api/drone-control/drone' import {
postFlightAuth, LostControlActionInCommandFLight, WaylineLostControlActionInCommandFlight, ERthMode,
ECommanderModeLostAction, ECommanderFlightMode
} from '/@/api/drone-control/drone'
import { useDroneControl } from './use-drone-control' import { useDroneControl } from './use-drone-control'
import { GimbalResetMode, GimbalResetModeOptions, LostControlActionInCommandFLightOptions, WaylineLostControlActionInCommandFlightOptions } from '/@/types/drone-control' import {
GimbalResetMode, GimbalResetModeOptions, LostControlActionInCommandFLightOptions, WaylineLostControlActionInCommandFlightOptions,
RthModeInCommandFlightOptions, CommanderModeLostActionInCommandFlightOptions, CommanderFlightModeInCommandFlightOptions
} from '/@/types/drone-control'
import DroneControlPopover from './DroneControlPopover.vue' import DroneControlPopover from './DroneControlPopover.vue'
import DroneControlInfoPanel from './DroneControlInfoPanel.vue' import DroneControlInfoPanel from './DroneControlInfoPanel.vue'
import { noDebugCmdList as baseCmdList, DeviceCmdItem, DeviceCmd } from '/@/types/device-cmd' import { noDebugCmdList as baseCmdList, DeviceCmdItem, DeviceCmd } from '/@/types/device-cmd'
@ -269,8 +313,8 @@ const clientId = computed(() => {
return store.state.clientId return store.state.clientId
}) })
const initCmdList = baseCmdList.find(item => item.cmdKey === DeviceCmd.ReturnHome) as DeviceCmdItem const initCmdList = baseCmdList.map(cmdItem => Object.assign({}, cmdItem))
const cmdItem = ref(initCmdList) const cmdList = ref(initCmdList)
const { const {
sendDockControlCmd sendDockControlCmd
@ -283,9 +327,11 @@ async function sendControlCmd (cmdItem: DeviceCmdItem, index: number) {
cmd: cmdItem.cmdKey, cmd: cmdItem.cmdKey,
action: cmdItem.action action: cmdItem.action
}, false) }, false)
if (result && flightController.value) { if (result) {
message.success('Return home successful') message.success('Return home successful')
exitFlightCOntrol() if (flightController.value) {
exitFlightCOntrol()
}
} else { } else {
message.error('Failed to return home') message.error('Failed to return home')
} }
@ -349,7 +395,11 @@ const takeoffToPointPopoverData = reactive({
maxSpeed: MAX_SPEED, maxSpeed: MAX_SPEED,
rthAltitude: null as null | number, rthAltitude: null as null | number,
rcLostAction: LostControlActionInCommandFLight.RETURN_HOME, rcLostAction: LostControlActionInCommandFLight.RETURN_HOME,
exitWaylineWhenRcLost: WaylineLostControlActionInCommandFlight.EXEC_LOST_ACTION exitWaylineWhenRcLost: WaylineLostControlActionInCommandFlight.EXEC_LOST_ACTION,
rthMode: ERthMode.SETTING,
commanderModeLostAction: ECommanderModeLostAction.CONTINUE,
commanderFlightMode: ECommanderFlightMode.SETTING,
commanderFlightHeight: null as null | number,
}) })
function onShowTakeoffToPointPopover () { function onShowTakeoffToPointPopover () {
@ -361,6 +411,10 @@ function onShowTakeoffToPointPopover () {
takeoffToPointPopoverData.rthAltitude = null takeoffToPointPopoverData.rthAltitude = null
takeoffToPointPopoverData.rcLostAction = LostControlActionInCommandFLight.RETURN_HOME takeoffToPointPopoverData.rcLostAction = LostControlActionInCommandFLight.RETURN_HOME
takeoffToPointPopoverData.exitWaylineWhenRcLost = WaylineLostControlActionInCommandFlight.EXEC_LOST_ACTION takeoffToPointPopoverData.exitWaylineWhenRcLost = WaylineLostControlActionInCommandFlight.EXEC_LOST_ACTION
takeoffToPointPopoverData.rthMode = ERthMode.SETTING
takeoffToPointPopoverData.commanderModeLostAction = ECommanderModeLostAction.CONTINUE
takeoffToPointPopoverData.commanderFlightMode = ECommanderFlightMode.SETTING
takeoffToPointPopoverData.commanderFlightHeight = null
} }
async function onTakeoffToPointConfirm (confirm: boolean) { async function onTakeoffToPointConfirm (confirm: boolean) {
@ -369,7 +423,8 @@ async function onTakeoffToPointConfirm (confirm: boolean) {
!takeoffToPointPopoverData.latitude || !takeoffToPointPopoverData.latitude ||
!takeoffToPointPopoverData.longitude || !takeoffToPointPopoverData.longitude ||
!takeoffToPointPopoverData.securityTakeoffHeight || !takeoffToPointPopoverData.securityTakeoffHeight ||
!takeoffToPointPopoverData.rthAltitude) { !takeoffToPointPopoverData.rthAltitude ||
!takeoffToPointPopoverData.commanderFlightHeight) {
message.error('Input error') message.error('Input error')
return return
} }
@ -382,7 +437,11 @@ async function onTakeoffToPointConfirm (confirm: boolean) {
rth_altitude: takeoffToPointPopoverData.rthAltitude, rth_altitude: takeoffToPointPopoverData.rthAltitude,
max_speed: takeoffToPointPopoverData.maxSpeed, max_speed: takeoffToPointPopoverData.maxSpeed,
rc_lost_action: takeoffToPointPopoverData.rcLostAction, rc_lost_action: takeoffToPointPopoverData.rcLostAction,
exit_wayline_when_rc_lost: takeoffToPointPopoverData.exitWaylineWhenRcLost exit_wayline_when_rc_lost: takeoffToPointPopoverData.exitWaylineWhenRcLost,
rth_mode: takeoffToPointPopoverData.rthMode,
commander_mode_lost_action: takeoffToPointPopoverData.commanderModeLostAction,
commander_flight_mode: takeoffToPointPopoverData.commanderFlightMode,
commander_flight_height: takeoffToPointPopoverData.commanderFlightHeight,
}) })
} catch (error) { } catch (error) {
} }
@ -672,6 +731,14 @@ function onShowCameraAimPopover () {
cameraAimPopoverData.y = null cameraAimPopoverData.y = null
} }
function openLivestreamOthers () {
store.commit('SET_LIVESTREAM_OTHERS_VISIBLE', true)
}
function openLivestreamAgora () {
store.commit('SET_LIVESTREAM_AGORA_VISIBLE', true)
}
async function onCameraAimConfirm (confirm: boolean) { async function onCameraAimConfirm (confirm: boolean) {
if (confirm) { if (confirm) {
if (cameraAimPopoverData.cameraType === null || cameraAimPopoverData.x === null || cameraAimPopoverData.y === null) { if (cameraAimPopoverData.cameraType === null || cameraAimPopoverData.x === null || cameraAimPopoverData.y === null) {

6
src/components/g-map/use-dock-control.ts

@ -45,8 +45,10 @@ export function useDockControl () {
} }
// 控制面板关闭 // 控制面板关闭
async function onCloseControlPanel (sn: string) { async function onCloseControlPanel (sn: string, debugging: boolean) {
await dockDebugOnOff(sn, false) if (debugging) {
await dockDebugOnOff(sn, false)
}
setDockControlPanelVisible(false) setDockControlPanelVisible(false)
} }

2
src/components/livestream-agora.vue

@ -1,5 +1,5 @@
<template> <template>
<div class="mt20 flex-column flex-justify-start flex-align-center"> <div class="flex-column flex-justify-start flex-align-center">
<div id="player" style="width: 720px; height: 420px; border: 1px solid"></div> <div id="player" style="width: 720px; height: 420px; border: 1px solid"></div>
<p class="fz24">Live streaming source selection</p> <p class="fz24">Live streaming source selection</p>
<div class="flex-row flex-justify-center flex-align-center mt10"> <div class="flex-row flex-justify-center flex-align-center mt10">

2
src/components/livestream-others.vue

@ -1,5 +1,5 @@
<template> <template>
<div class="flex-column flex-justify-start flex-align-center mt20"> <div class="flex-column flex-justify-start flex-align-center">
<video <video
:style="{ width: '720px', height: '480px' }" :style="{ width: '720px', height: '480px' }"
id="video-webrtc" id="video-webrtc"

157
src/components/task/CreatePlan.vue

@ -49,7 +49,7 @@
>Select Device</router-link> >Select Device</router-link>
</a-form-item> </a-form-item>
<a-form-item v-if="planBody.dock_sn" style="margin-top: -15px;"> <a-form-item v-if="planBody.dock_sn" style="margin-top: -15px;">
<div class="panel" style="padding-top: 5px;" @click="selectDock(dock)"> <div class="panel" style="padding-top: 5px;">
<div class="title"> <div class="title">
<a-tooltip :title="dock.nickname"> <a-tooltip :title="dock.nickname">
<div class="pr10" style="width: 120px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden;">{{ dock.nickname }}</div> <div class="pr10" style="width: 120px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden;">{{ dock.nickname }}</div>
@ -62,22 +62,64 @@
</div> </div>
</a-form-item> </a-form-item>
<!-- 任务类型 --> <!-- 任务类型 -->
<a-form-item label="Plan Timer" class="plan-timer-form-item" :labelCol="{span: 23}"> <a-form-item label="Plan Timer" class="plan-timer-form-item">
<div style="white-space: nowrap;"> <div style="white-space: nowrap;">
<a-radio-group v-model:value="planBody.task_type" button-style="solid"> <a-radio-group v-model:value="planBody.task_type" button-style="solid">
<a-radio-button v-for="type in TaskTypeOptions" :value="type.value" :key="type.value">{{ type.label }}</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> </a-radio-group>
</div> </div>
</a-form-item> </a-form-item>
<!-- 执行时间 --> <!-- execute date -->
<a-form-item label="Start Time" v-if="planBody.task_type === TaskType.Timed" name="select_execute_time" :labelCol="{span: 23}"> <a-form-item label="Date" v-if="planBody.task_type === TaskType.Timed || planBody.task_type === TaskType.Condition" name="select_execute_date" :labelCol="{span: 23}">
<a-date-picker <a-range-picker
v-model:value="planBody.select_execute_time" v-model:value="planBody.select_execute_date"
format="YYYY-MM-DD HH:mm:ss" :disabledDate="(current: Moment) => current < moment().subtract(1, 'days')"
format="YYYY-MM-DD"
:placeholder="['Start Time', 'End Time']"
style="width: 100%;"
/>
</a-form-item>
<!-- execute time -->
<a-form-item label="Time" v-if="planBody.task_type === TaskType.Timed || planBody.task_type === TaskType.Condition"
name="select_execute_time" ref="select_execute_time" :labelCol="{span: 23}" :autoLink="false">
<div class="mb10 flex-row flex-align-center flex-justify-around" v-for="n in planBody.select_time_number" :key="n">
<a-time-picker
v-model:value="planBody.select_time[n - 1][0]"
format="HH:mm:ss"
show-time show-time
placeholder="Select Time" placeholder="Start Time"
:style="planBody.task_type === TaskType.Condition ? 'width: 40%' : 'width: 82%'"
@change="() => $refs.select_execute_time.onFieldChange()"
/> />
<template v-if="planBody.task_type === TaskType.Condition">
<div><span style="color: white;">-</span></div>
<a-time-picker
v-model:value="planBody.select_time[n - 1][1]"
format="HH:mm:ss"
show-time
placeholder="End Time"
style="width: 40%;"
/>
</template>
<div class="ml5" style="font-size:18px">
<PlusCircleOutlined class="mr5" style="color: #1890ff" @click="addTime"/>
<MinusCircleOutlined :style="planBody.select_time_number === 1 ? 'color: gray' : 'color: red;'" @click="removeTime"/>
</div>
</div>
</a-form-item> </a-form-item>
<template v-if="planBody.task_type === TaskType.Condition">
<!-- battery capacity -->
<a-form-item label="Start task when battery level reaches" :labelCol="{span: 23}" name="min_battery_capacity">
<a-input-number class="width-100" v-model:value="planBody.min_battery_capacity" :min="50" :max="100"
:formatter="(value: number) => `${value}%`" :parser="(value: string) => value.replace('%', '')">
</a-input-number>
</a-form-item>
<!-- storage capacity -->
<a-form-item label="Start task when storage level reaches (MB)" :labelCol="{span: 23}" name="storage_capacity">
<a-input-number v-model:value="planBody.min_storage_capacity" class="width-100">
</a-input-number>
</a-form-item>
</template>
<!-- RTH Altitude Relative to Dock --> <!-- RTH Altitude Relative to Dock -->
<a-form-item label="RTH Altitude Relative to Dock (m)" :labelCol="{span: 23}" name="rth_altitude"> <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 v-model:value="planBody.rth_altitude" :min="20" :max="1500" class="width-100" required>
@ -145,12 +187,16 @@ const disabled = ref(false)
const routeName = ref('') const routeName = ref('')
const planBody = reactive({ const planBody = reactive({
name: '', name: '',
file_id: computed(() => store.state.waylineInfo.id), file_id: computed(() => store.state?.waylineInfo.id),
dock_sn: computed(() => store.state.dockInfo.device_sn), dock_sn: computed(() => store.state?.dockInfo.device_sn),
task_type: TaskType.Immediate, task_type: TaskType.Immediate,
select_execute_time: undefined as Moment| undefined, select_execute_date: [moment(), moment()] as Moment[],
select_time_number: 1,
select_time: [[]] as Moment[][],
rth_altitude: '', rth_altitude: '',
out_of_control_action: OutOfControlAction.ReturnToHome, out_of_control_action: OutOfControlAction.ReturnToHome,
min_battery_capacity: 90 as number,
min_storage_capacity: undefined as number | undefined,
}) })
const drawerVisible = ref(false) const drawerVisible = ref(false)
@ -158,16 +204,35 @@ const valueRef = ref()
const rules = { const rules = {
name: [ name: [
{ required: true, message: 'Please enter plan name.' }, { required: true, message: 'Please enter plan name.' },
{ max: 20, message: 'Length should be 1 to 20', trigger: 'blur' } { max: 20, message: 'Length should be 1 to 20' }
], ],
file_id: [{ required: true, message: 'Select Route' }], file_id: [{ required: true, message: 'Select Route' }],
dock_sn: [{ required: true, message: 'Select Device' }], dock_sn: [{ required: true, message: 'Select Device' }],
select_execute_time: [{ required: true, message: 'Select start time' }], select_execute_time: [{
validator: async (rule: RuleObject, value: Moment[]) => {
validEndTime()
validStartTime()
if (planBody.select_time.length < planBody.select_time_number) {
throw new Error('Select time')
}
validOverlapped()
}
}],
select_execute_date: [{ required: true, message: 'Select date' }],
rth_altitude: [ rth_altitude: [
{ {
validator: async (rule: RuleObject, value: string) => { validator: async (rule: RuleObject, value: string) => {
if (!/^[0-9]{1,}$/.test(value)) { if (!/^[0-9]{1,}$/.test(value)) {
throw new Error('RTH Altitude Relative Require number') throw new Error('RTH Altitude Require number')
}
},
}
],
min_battery_capacity: [
{
validator: async (rule: RuleObject, value: any) => {
if (TaskType.Condition === planBody.task_type && !value) {
throw new Error('Please enter battery capacity')
} }
}, },
} }
@ -175,19 +240,61 @@ const rules = {
out_of_control_action: [{ required: true, message: 'Select Lost Action' }], out_of_control_action: [{ required: true, message: 'Select Lost Action' }],
} }
function validStartTime (): Error | void {
for (let i = 0; i < planBody.select_time.length; i++) {
if (!planBody.select_time[i][0]) {
throw new Error('Select start time')
}
}
}
function validEndTime (): Error | void {
if (TaskType.Condition !== planBody.task_type) return
for (let i = 0; i < planBody.select_time.length; i++) {
if (!planBody.select_time[i][1]) {
throw new Error('Select end time')
}
if (planBody.select_time[i][0] && planBody.select_time[i][1].isSameOrBefore(planBody.select_time[i][0])) {
throw new Error('End time should be later than start time')
}
}
}
function validOverlapped (): Error | void {
if (TaskType.Condition !== planBody.task_type) return
const arr = planBody.select_time.slice()
arr.sort((a, b) => a[0].unix() - b[0].unix())
arr.forEach((v, i, arr) => {
if (i > 0 && v[0] < arr[i - 1][1]) {
throw new Error('Overlapping time periods.')
}
})
}
function onSubmit () { function onSubmit () {
console.info(dock, '12131231')
valueRef.value.validate().then(() => { valueRef.value.validate().then(() => {
disabled.value = true disabled.value = true
const createPlanBody = { ...planBody } as unknown as CreatePlan const createPlanBody = { ...planBody } as unknown as CreatePlan
if (planBody.select_execute_time) { if (planBody.select_execute_date.length === 2) {
createPlanBody.task_days = [moment(planBody.select_execute_time).unix()] createPlanBody.task_days = []
createPlanBody.task_periods = [createPlanBody.task_days] for (let i = planBody.select_execute_date[0]; i.isSameOrBefore(planBody.select_execute_date[1]); i.add(1, 'days')) {
createPlanBody.task_days.push(i.unix())
}
}
createPlanBody.task_periods = []
if (TaskType.Immediate !== planBody.task_type) {
for (let i = 0; i < planBody.select_time.length; i++) {
const result = []
result.push(planBody.select_time[i][0].unix())
if (TaskType.Condition === planBody.task_type) {
result.push(planBody.select_time[i][1].unix())
}
createPlanBody.task_periods.push(result)
}
} }
createPlanBody.rth_altitude = Number(createPlanBody.rth_altitude) createPlanBody.rth_altitude = Number(createPlanBody.rth_altitude)
if (wayline.value && wayline.value.template_types && wayline.value.template_types.length > 0) { if (wayline.value && wayline.value.template_types && wayline.value.template_types.length > 0) {
createPlanBody.wayline_type = wayline.value.template_types[0] createPlanBody.wayline_type = wayline.value.template_types[0]
} }
// console.log('planBody', createPlanBody)
createPlan(workspaceId, createPlanBody) createPlan(workspaceId, createPlanBody)
.then(res => { .then(res => {
disabled.value = false disabled.value = false
@ -217,6 +324,18 @@ function selectDevice () {
drawerVisible.value = true drawerVisible.value = true
routeName.value = 'DockPanel' routeName.value = 'DockPanel'
} }
function addTime () {
valueRef.value.validateFields(['select_execute_time']).then(() => {
planBody.select_time_number++
planBody.select_time.push([])
})
}
function removeTime () {
if (planBody.select_time_number === 1) return
planBody.select_time_number--
planBody.select_time.splice(planBody.select_time_number)
}
</script> </script>
<style lang="scss"> <style lang="scss">
@ -266,7 +385,7 @@ function selectDevice () {
.ant-radio-button-wrapper{ .ant-radio-button-wrapper{
background-color: #232323; background-color: #232323;
color: #fff; color: #fff;
width: 80%; width: 33%;
text-align: center; text-align: center;
&.ant-radio-button-wrapper-checked{ &.ant-radio-button-wrapper-checked{
background-color: #1890ff; background-color: #1890ff;

4
src/constants/map.ts

@ -11,8 +11,8 @@ export const MapElementDefaultColor = MapElementColor.Default
export enum MapDoodleColor { export enum MapDoodleColor {
PinColor = '#2D8CF0', PinColor = '#2D8CF0',
PolylineColor = '#3366FF', PolylineColor = '#2D8CF0',
PolygonColor = '#FF33FF' PolygonColor = '#2D8CF0'
} }
export enum MapElementEnum { export enum MapElementEnum {

38
src/directives/drag-window.ts

@ -0,0 +1,38 @@
import { nextTick, App } from 'vue'
export default function useDragWindowDirective (app: App): void {
app.directive('drag-window', async (el) => {
await nextTick()
const modal = el
const header = el.getElementsByClassName('drag-title')[0]
let left = 0
let top = 0
header.style.cursor = 'move'
top = top || modal.offsetTop
header.onpointerdown = (e: { clientX: number; clientY: number; pointerId: number }) => {
const startX = e.clientX
const startY = e.clientY
header.left = header.offsetLeft
header.top = header.offsetTop
header.setPointerCapture(e.pointerId)
el.onpointermove = (event: { clientX: number; clientY: number }) => {
const endX = event.clientX
const endY = event.clientY
modal.left = header.left + (endX - startX) + left
modal.top = header.top + (endY - startY) + top
modal.style.left = modal.left + 'px'
modal.style.top = modal.top + 'px'
}
el.onpointerup = () => {
left = modal.left || 0
top = modal.top || 0
el.onpointermove = null
el.onpointerup = null
header.releasePointerCapture(e.pointerId)
}
}
})
}

6
src/directives/index.ts

@ -0,0 +1,6 @@
import { App } from 'vue'
import useDragWindowDirective from './drag-window'
export function useDirectives (app: App): void {
useDragWindowDirective(app)
}

37
src/hooks/use-g-map-cover.ts

@ -78,9 +78,9 @@ export function useGMapCover () {
AddOverlayGroup(polyline) AddOverlayGroup(polyline)
} }
function initPolygon (name: string, coordinates:GeojsonCoordinate[], color?:string, data?:{}) { function initPolygon (name: string, coordinates:GeojsonCoordinate[][], color?:string, data?:{}) {
const path = [] as GeojsonCoordinate[] const path = [] as GeojsonCoordinate[]
coordinates.forEach(coordinate => { coordinates[0].forEach(coordinate => {
path.push(new AMap.LngLat(coordinate[0], coordinate[1])) path.push(new AMap.LngLat(coordinate[0], coordinate[1]))
}) })
// console.log('Polygon', path) // console.log('Polygon', path)
@ -141,12 +141,43 @@ export function useGMapCover () {
} }
} }
function updatePolylineElement (id:string, name: string, coordinates:GeojsonCoordinate[], color?:string) {
const element = getElementFromMap(id) as any
if (element) {
const options = element.getOptions()
options.strokeColor = color || normalColor
element.setOptions(options)
} else {
initPolyline(name, coordinates, color, {
id: id,
name: name
})
}
}
function updatePolygonElement (id:string, name: string, coordinates:GeojsonCoordinate[][], color?:string) {
const element = getElementFromMap(id) as any
if (element) {
const options = element.getOptions()
options.fillColor = color || normalColor
options.strokeColor = color || normalColor
element.setOptions(options)
} else {
initPolygon(name, coordinates, color, {
id: id,
name: name
})
}
}
return { return {
init2DPin, init2DPin,
initPolyline, initPolyline,
initPolygon, initPolygon,
removeCoverFromMap, removeCoverFromMap,
getElementFromMap, getElementFromMap,
updatePinElement updatePinElement,
updatePolylineElement,
updatePolygonElement
} }
} }

4
src/main.ts

@ -5,10 +5,14 @@ import { CommonComponents } from './use-common-components'
import 'virtual:svg-icons-register' import 'virtual:svg-icons-register'
import store, { storeKey } from './store' import store, { storeKey } from './store'
import { createInstance } from '/@/root' import { createInstance } from '/@/root'
import { useDirectives } from './directives'
import '/@/styles/index.scss' import '/@/styles/index.scss'
const app = createInstance(App) const app = createInstance(App)
app.use(store, storeKey) app.use(store, storeKey)
app.use(router) app.use(router)
app.use(CommonComponents) app.use(CommonComponents)
app.use(antComponents) app.use(antComponents)
app.use(useDirectives)
app.mount('#demo-app') app.mount('#demo-app')

10
src/pages/page-pilot/pilot-home.vue

@ -13,9 +13,9 @@
<RightOutlined style="float: right; margin-top: 5px; color: #8894a0" /> <RightOutlined style="float: right; margin-top: 5px; color: #8894a0" />
</div> </div>
<div style="height: 50%;"> <div style="height: 50%;">
<CloudSyncOutlined v-if="state === EStatusValue.CONNECTED" style="color: #75c5f6" /> <CloudSyncOutlined v-if="thingState === EStatusValue.CONNECTED" style="color: #75c5f6" />
<SyncOutlined spin v-else/> <SyncOutlined spin v-else/>
<span style="color: #737373; margin-left: 3px;">{{ state }}</span> <span style="color: #737373; margin-left: 3px;">{{ thingState }}</span>
</div> </div>
<a-drawer placement="right" v-model:visible="drawerVisible" width="340px"> <a-drawer placement="right" v-model:visible="drawerVisible" width="340px">
<div class="mb10 flex-row flex-justify-center flex-align-center"> <div class="mb10 flex-row flex-justify-center flex-align-center">
@ -135,7 +135,6 @@ import { useConnectWebSocket } from '/@/hooks/use-connect-websocket'
const root = getRoot() const root = getRoot()
const gatewayState = ref<boolean>(localStorage.getItem(ELocalStorageKey.GatewayOnline) === 'true') const gatewayState = ref<boolean>(localStorage.getItem(ELocalStorageKey.GatewayOnline) === 'true')
const state = ref(EStatusValue.DISCONNECT)
const thingState = ref(EStatusValue.DISCONNECT) const thingState = ref(EStatusValue.DISCONNECT)
const apiState = ref(EStatusValue.DISCONNECT) const apiState = ref(EStatusValue.DISCONNECT)
const liveState = ref(EStatusValue.DISCONNECT) const liveState = ref(EStatusValue.DISCONNECT)
@ -157,7 +156,7 @@ interface DeviceInfoData {
} }
const device = reactive<DeviceInfoData>({ const device = reactive<DeviceInfoData>({
data: { data: {
sn: EStatusValue.DISCONNECT, sn: '',
online_status: false, online_status: false,
device_callsign: '', device_callsign: '',
user_id: '', user_id: '',
@ -218,9 +217,7 @@ const messageHandler = async (payload: any) => {
case EBizCode.DeviceOnline: { case EBizCode.DeviceOnline: {
console.info('online: ', payload) console.info('online: ', payload)
if (payload.data.sn === device.data.gateway_sn) { if (payload.data.sn === device.data.gateway_sn) {
gatewayState.value = true
localStorage.setItem(ELocalStorageKey.GatewayOnline, gatewayState.value.toString()) localStorage.setItem(ELocalStorageKey.GatewayOnline, gatewayState.value.toString())
state.value = gatewayState.value && thingState.value === EStatusValue.CONNECTED ? EStatusValue.CONNECTED : EStatusValue.DISCONNECT
break break
} }
if (payload.data.gateway_sn === device.data.gateway_sn) { if (payload.data.gateway_sn === device.data.gateway_sn) {
@ -441,7 +438,6 @@ function refreshStatus () {
tsaState.value = apiPilot.isComponentLoaded(EComponentName.Tsa) ? EStatusValue.CONNECTED : EStatusValue.DISCONNECT tsaState.value = apiPilot.isComponentLoaded(EComponentName.Tsa) ? EStatusValue.CONNECTED : EStatusValue.DISCONNECT
mediaState.value = apiPilot.isComponentLoaded(EComponentName.Media) ? EStatusValue.CONNECTED : EStatusValue.DISCONNECT mediaState.value = apiPilot.isComponentLoaded(EComponentName.Media) ? EStatusValue.CONNECTED : EStatusValue.DISCONNECT
waylineState.value = apiPilot.isComponentLoaded(EComponentName.Mission) ? EStatusValue.CONNECTED : EStatusValue.DISCONNECT waylineState.value = apiPilot.isComponentLoaded(EComponentName.Mission) ? EStatusValue.CONNECTED : EStatusValue.DISCONNECT
state.value = thingState.value === EStatusValue.CONNECTED && gatewayState.value ? EStatusValue.CONNECTED : EStatusValue.DISCONNECT
} }
function moduleInstall (m: any) { function moduleInstall (m: any) {

28
src/pages/page-pilot/pilot-liveshare.vue

@ -54,7 +54,26 @@
<div class="width-100" style="margin-top: -10px;"> <div class="width-100" style="margin-top: -10px;">
<div class="ml10" style="width: 97%;"> <div class="ml10" style="width: 97%;">
<span class="fz16">Param: </span> <span class="fz16">Param: </span>
<span v-if="liveStreamStatus.type === ELiveTypeValue.Agora" style="word-break: break-all; color: #75c5f6;">{{ agoraParam }}</span> <span v-if="liveStreamStatus.type === ELiveTypeValue.Agora" style="word-break: break-all; color: #75c5f6;">
<div class="flex-col flex-justify-center flex-align-center">
<div>
<span class="ml10">Token:</span>
<a-input
class="ml10"
v-model:value="agoraParam.token"
placeholder="Token"
></a-input>
</div>
<div>
<span class="ml10">Channel:</span>
<a-input
class="ml10"
v-model:value="agoraParam.channelId"
placeholder="Channel"
></a-input>
</div>
</div>
</span>
<span v-else-if="liveStreamStatus.type === ELiveTypeValue.RTMP" style="word-break: break-all; color: #75c5f6;">{{ rtmpParam }}</span> <span v-else-if="liveStreamStatus.type === ELiveTypeValue.RTMP" style="word-break: break-all; color: #75c5f6;">{{ rtmpParam }}</span>
<span v-else-if="liveStreamStatus.type === ELiveTypeValue.RTSP" style="word-break: break-all; color: #75c5f6;">{{ rtspParam }}</span> <span v-else-if="liveStreamStatus.type === ELiveTypeValue.RTSP" style="word-break: break-all; color: #75c5f6;">{{ rtspParam }}</span>
<span v-else-if="liveStreamStatus.type === ELiveTypeValue.GB28181" style="word-break: break-all; color: #75c5f6;">{{ gb28181Param }}</span> <span v-else-if="liveStreamStatus.type === ELiveTypeValue.GB28181" style="word-break: break-all; color: #75c5f6;">{{ gb28181Param }}</span>
@ -154,11 +173,11 @@ const liveTypeList = [
label: ELiveTypeName.GB28181 label: ELiveTypeName.GB28181
} }
] ]
const agoraParam = { const agoraParam = reactive({
uid: '2892130292', uid: '2892130292',
token: config.agoraToken, token: config.agoraToken,
channelId: config.agoraChannel channelId: config.agoraChannel
} })
const rtmpParam = { const rtmpParam = {
url: config.rtmpURL + new Date().getTime() url: config.rtmpURL + new Date().getTime()
} }
@ -174,7 +193,7 @@ const gb28181Param: GB28181Param = {
agentId: CURRENT_CONFIG.gbAgentId, agentId: CURRENT_CONFIG.gbAgentId,
password: CURRENT_CONFIG.gbPassword, password: CURRENT_CONFIG.gbPassword,
agentPort: CURRENT_CONFIG.gbAgentPort, agentPort: CURRENT_CONFIG.gbAgentPort,
agentChannel: CURRENT_CONFIG.gbAgentChannel agentChannel: CURRENT_CONFIG.gbAgentChannel,
} }
const playVisiable = ref(false) const playVisiable = ref(false)
@ -253,6 +272,7 @@ const onPublishModeSelect = (val: string) => {
apiPilot.setVideoPublishType(publishModeSelected.value) apiPilot.setVideoPublishType(publishModeSelected.value)
} }
const onPlay = () => { const onPlay = () => {
console.info(JSON.stringify(agoraParam))
if (!publishModeSelected.value) { if (!publishModeSelected.value) {
message.warn('Please select publish mode!') message.warn('Please select publish mode!')
return return

41
src/pages/page-web/projects/layer.vue

@ -171,7 +171,6 @@ function updateMapElement (
const geoType = element.resource?.content.geometry.type const geoType = element.resource?.content.geometry.type
const id = element.id const id = element.id
const type = element.resource?.type as number const type = element.resource?.type as number
if (MapElementEnum.PIN === type) { if (MapElementEnum.PIN === type) {
const coordinates = element.resource?.content.geometry const coordinates = element.resource?.content.geometry
.coordinates as GeojsonCoordinate .coordinates as GeojsonCoordinate
@ -179,17 +178,11 @@ function updateMapElement (
} else if (MapElementEnum.LINE === type && geoType === 'LineString') { } else if (MapElementEnum.LINE === type && geoType === 'LineString') {
const coordinates = element.resource?.content.geometry const coordinates = element.resource?.content.geometry
.coordinates as GeojsonCoordinate[] .coordinates as GeojsonCoordinate[]
useGMapCoverHook.initPolyline(name, coordinates, color, { useGMapCoverHook.updatePolylineElement(id, name, coordinates, color)
id: id,
name: name
})
} else if (MapElementEnum.POLY === type && geoType === 'Polygon') { } else if (MapElementEnum.POLY === type && geoType === 'Polygon') {
const coordinates = element.resource?.content.geometry const coordinates = element.resource?.content.geometry
.coordinates[0] as GeojsonCoordinate[] .coordinates as GeojsonCoordinate[][]
useGMapCoverHook.initPolygon(name, coordinates, color, { useGMapCoverHook.updatePolygonElement(id, name, coordinates, color)
id: id,
name: name
})
} }
} }
function checkLayer (keys: string[]) { function checkLayer (keys: string[]) {
@ -235,10 +228,12 @@ function setBaseInfo () {
layerState.layerName = layer.name layerState.layerName = layer.name
layerState.layerId = layer.id layerState.layerId = layer.id
layerState.color = layer.resource?.content.properties.color layerState.color = layer.resource?.content.properties.color
let coordinate: GeojsonCoordinate
switch (geoType) { switch (geoType) {
case GeoType.Point: case GeoType.Point:
layerState.longitude = layer.resource?.content.geometry.coordinates[0] coordinate = gcj02towgs84(layer.resource?.content.geometry.coordinates[0], layer.resource?.content.geometry.coordinates[1]) as GeojsonCoordinate
layerState.latitude = layer.resource?.content.geometry.coordinates[1] layerState.longitude = coordinate[0]
layerState.latitude = coordinate[1]
break break
case GeoType.LineString: case GeoType.LineString:
break break
@ -250,7 +245,7 @@ function setBaseInfo () {
onMounted(() => { onMounted(() => {
const element = document.getElementsByClassName('scrollbar').item(0) as HTMLDivElement const element = document.getElementsByClassName('scrollbar').item(0) as HTMLDivElement
const parent = element?.parentNode as HTMLDivElement const parent = element?.parentNode as HTMLDivElement
scorllHeight.value = parent.clientHeight - parent.firstElementChild!.clientHeight scorllHeight.value = parent?.clientHeight - parent.firstElementChild!.clientHeight
getAllElement() getAllElement()
}) })
function closeDrawer () { function closeDrawer () {
@ -356,39 +351,39 @@ function updateCoordinates (transformType: string, element: LayerResource) {
) as GeojsonCoordinate ) as GeojsonCoordinate
element.resource.content.geometry.coordinates = transResult element.resource.content.geometry.coordinates = transResult
} }
} else if (MapElementEnum.LINE === type && geoType === 'LineString') { } else if (MapElementEnum.LINE === type) {
const coordinates = element.resource?.content.geometry const coordinates = element.resource?.content.geometry
.coordinates as GeojsonCoordinate[] .coordinates as GeojsonCoordinate[]
if (transformType === 'wgs84-gcj02') { if (transformType === 'wgs84-gcj02') {
coordinates.forEach(coordinate => { coordinates.forEach((coordinate, i, arr) => {
coordinate = wgs84togcj02( arr[i] = wgs84togcj02(
coordinate[0], coordinate[0],
coordinate[1] coordinate[1]
) as GeojsonCoordinate ) as GeojsonCoordinate
}) })
} else if (transformType === 'gcj02-wgs84') { } else if (transformType === 'gcj02-wgs84') {
coordinates.forEach(coordinate => { coordinates.forEach((coordinate, i, arr) => {
coordinate = gcj02towgs84( arr[i] = gcj02towgs84(
coordinate[0], coordinate[0],
coordinate[1] coordinate[1]
) as GeojsonCoordinate ) as GeojsonCoordinate
}) })
} }
element.resource.content.geometry.coordinates = coordinates element.resource.content.geometry.coordinates = coordinates
} else if (MapElementEnum.LINE === type && geoType === 'Polygon') { } else if (MapElementEnum.POLY === type) {
const coordinates = element.resource?.content.geometry const coordinates = element.resource?.content.geometry
.coordinates[0] as GeojsonCoordinate[] .coordinates[0] as GeojsonCoordinate[]
if (transformType === 'wgs84-gcj02') { if (transformType === 'wgs84-gcj02') {
coordinates.forEach(coordinate => { coordinates.forEach((coordinate, i, arr) => {
coordinate = wgs84togcj02( arr[i] = wgs84togcj02(
coordinate[0], coordinate[0],
coordinate[1] coordinate[1]
) as GeojsonCoordinate ) as GeojsonCoordinate
}) })
} else if (transformType === 'gcj02-wgs84') { } else if (transformType === 'gcj02-wgs84') {
coordinates.forEach(coordinate => { coordinates.forEach((coordinate, i, arr) => {
coordinate = gcj02towgs84( arr[i] = gcj02towgs84(
coordinate[0], coordinate[0],
coordinate[1] coordinate[1]
) as GeojsonCoordinate ) as GeojsonCoordinate

14
src/pages/page-web/projects/livestream.vue

@ -18,7 +18,8 @@
> >
</router-link> </router-link>
</div> </div>
<div class="live" v-if="showLive"> <div class="live" v-if="showLive" v-drag-window>
<div style="height: 40px; width: 100%" class="drag-title"></div>
<a style="position: absolute; right: 10px; top: 10px; font-size: 16px; color: white;" @click="() => root.$router.push('/' + ERouterName.LIVESTREAM)"><CloseOutlined /></a> <a style="position: absolute; right: 10px; top: 10px; font-size: 16px; color: white;" @click="() => root.$router.push('/' + ERouterName.LIVESTREAM)"><CloseOutlined /></a>
<router-view :name="routeName" /> <router-view :name="routeName" />
</div> </div>
@ -74,14 +75,13 @@ onMounted(() => {
.live { .live {
position: absolute; position: absolute;
z-index: 1; z-index: 1;
right: 50%; left: 0;
left: 50%; top: 10px;
top: 50%; margin-left: 345px;
margin: auto;
transform: translate(-50%, -50%);
text-align: center; text-align: center;
width: 800px; width: 800px;
height: 700px; height: 720px;
background: #232323; background: #232323;
} }
</style> </style>

11
src/pages/page-web/projects/tsa.vue

@ -203,7 +203,7 @@ import { EHmsLevel } from '/@/types/enums'
const store = useMyStore() const store = useMyStore()
const username = ref(localStorage.getItem(ELocalStorageKey.Username)) const username = ref(localStorage.getItem(ELocalStorageKey.Username))
const workspaceId = ref(localStorage.getItem(ELocalStorageKey.WorkspaceId)!) const workspaceId = ref(localStorage.getItem(ELocalStorageKey.WorkspaceId)!)
const osdVisible = ref({} as OSDVisible) const osdVisible = computed(() => store.state.osdVisible)
const hmsVisible = new Map<string, boolean>() const hmsVisible = new Map<string, boolean>()
const scorllHeight = ref() const scorllHeight = ref()
@ -344,6 +344,15 @@ function readHms (visiable: boolean, sn: string) {
}) })
} }
} }
function openLivestreamOthers () {
store.commit('SET_LIVESTREAM_OTHERS_VISIBLE', true)
}
function openLivestreamAgora () {
store.commit('SET_LIVESTREAM_AGORA_VISIBLE', true)
}
</script> </script>
<style lang="scss"> <style lang="scss">

8
src/router/index.ts

@ -47,6 +47,10 @@ const routes: Array<RouteRecordRaw> = [
component: () => import('/@/pages/page-web/projects/workspace.vue'), component: () => import('/@/pages/page-web/projects/workspace.vue'),
redirect: '/' + ERouterName.TSA, redirect: '/' + ERouterName.TSA,
children: [ children: [
{
path: '/' + ERouterName.TSA,
component: () => import('/@/pages/page-web/projects/tsa.vue')
},
{ {
path: '/' + ERouterName.LIVESTREAM, path: '/' + ERouterName.LIVESTREAM,
name: ERouterName.LIVESTREAM, name: ERouterName.LIVESTREAM,
@ -62,10 +66,6 @@ const routes: Array<RouteRecordRaw> = [
} }
] ]
}, },
{
path: '/' + ERouterName.TSA,
component: () => import('/@/pages/page-web/projects/tsa.vue')
},
{ {
path: '/' + ERouterName.LAYER, path: '/' + ERouterName.LAYER,
name: ERouterName.LAYER, name: ERouterName.LAYER,

10
src/store/index.ts

@ -32,6 +32,8 @@ const initStateFunc = () => ({
[key:string]:string [key:string]:string
}, },
drawVisible: false, drawVisible: false,
livestreamOthersVisible: false,
livestreamAgoraVisible: false,
coverList: [ coverList: [
] as any, ] as any,
@ -124,7 +126,7 @@ const mutations: MutationTree<RootStateType> = {
dock.basic_osd = info.host dock.basic_osd = info.host
return return
} }
if (info.host.sdr) { if (info.host.wireless_link) {
dock.link_osd = info.host dock.link_osd = info.host
return return
} }
@ -135,6 +137,12 @@ const mutations: MutationTree<RootStateType> = {
SET_DRAW_VISIBLE_INFO (state, bool) { SET_DRAW_VISIBLE_INFO (state, bool) {
state.drawVisible = bool state.drawVisible = bool
}, },
SET_LIVESTREAM_OTHERS_VISIBLE (state, bool) {
state.livestreamOthersVisible = bool
},
SET_LIVESTREAM_AGORA_VISIBLE (state, bool) {
state.livestreamAgoraVisible = bool
},
SET_MAP_ELEMENT_CREATE (state, info) { SET_MAP_ELEMENT_CREATE (state, info) {
state.wsEvent.mapElementCreat = info state.wsEvent.mapElementCreat = info
}, },

9
src/types/device-cmd.ts

@ -7,6 +7,7 @@ export enum DeviceCmd {
SupplementLightOpen = 'supplement_light_open', // 打开补光灯 SupplementLightOpen = 'supplement_light_open', // 打开补光灯
SupplementLightClose = 'supplement_light_close', // 关闭补光灯 SupplementLightClose = 'supplement_light_close', // 关闭补光灯
ReturnHome = 'return_home', // 一键返航 ReturnHome = 'return_home', // 一键返航
ReturnHomeCancel = 'return_home_cancel', // 取消返航
// 复杂指令 // 复杂指令
DeviceReboot = 'device_reboot', // 机场重启 DeviceReboot = 'device_reboot', // 机场重启
DroneOpen = 'drone_open', // 飞行器开机 DroneOpen = 'drone_open', // 飞行器开机
@ -48,6 +49,14 @@ export const noDebugCmdList: DeviceCmdItem[] = [
func: 'returnHome', func: 'returnHome',
loading: false, loading: false,
}, },
{
label: 'Return Home Cancel',
status: '--',
operateText: 'Return Home Cancel',
cmdKey: DeviceCmd.ReturnHomeCancel,
func: 'returnHomeCancel',
loading: false,
}
] ]
// 机场指令 // 机场指令

4
src/types/device.ts

@ -319,7 +319,7 @@ export interface DockBasicOsd {
state: number, state: number,
capacity_percent: number, capacity_percent: number,
}, },
drone_in_dock: DroneInDockEnum, drone_in_dock: boolean,
rainfall: RainfallEnum, rainfall: RainfallEnum,
wind_speed: number, wind_speed: number,
environment_temperature: number, environment_temperature: number,
@ -379,7 +379,7 @@ export interface DockLinkOsd {
media_file_detail?: { media_file_detail?: {
remain_upload: number remain_upload: number
}, },
sdr: { sdr?: {
up_quality: string, up_quality: string,
down_quality: string, down_quality: string,
frequency_band: number, frequency_band: number,

17
src/types/drone-control.ts

@ -1,5 +1,5 @@
import { ControlSource } from './device' import { ControlSource } from './device'
import { LostControlActionInCommandFLight, WaylineLostControlActionInCommandFlight } from '/@/api/drone-control/drone' import { ECommanderModeLostAction, ERthMode, LostControlActionInCommandFLight, WaylineLostControlActionInCommandFlight } from '/@/api/drone-control/drone'
export enum ControlSourceChangeType { export enum ControlSourceChangeType {
Flight = 1, Flight = 1,
@ -52,6 +52,21 @@ export const LostControlActionInCommandFLightOptions = [
{ label: 'Landing', value: LostControlActionInCommandFLight.Land } { label: 'Landing', value: LostControlActionInCommandFLight.Land }
] ]
export const RthModeInCommandFlightOptions = [
{ label: 'Smart Height', value: ERthMode.SMART },
{ label: 'Setting Height', value: ERthMode.SETTING }
]
export const CommanderModeLostActionInCommandFlightOptions = [
{ label: 'Continue', value: ECommanderModeLostAction.CONTINUE },
{ label: 'Execute Lost Action', value: ECommanderModeLostAction.EXEC_LOST_ACTION }
]
export const CommanderFlightModeInCommandFlightOptions = [
{ label: 'Smart Height', value: ERthMode.SMART },
{ label: 'Setting Height', value: ERthMode.SETTING }
]
// 云台重置模式 // 云台重置模式
export enum GimbalResetMode { export enum GimbalResetMode {
Recenter = 0, Recenter = 0,

3
src/types/task.ts

@ -4,16 +4,19 @@ import { commonColor } from '/@/utils/color'
export enum TaskType { export enum TaskType {
Immediate = 0, // 立即执行 Immediate = 0, // 立即执行
Timed = 1, // 单次定时任务 Timed = 1, // 单次定时任务
Condition = 2,
} }
export const TaskTypeMap = { export const TaskTypeMap = {
[TaskType.Immediate]: 'Immediate', [TaskType.Immediate]: 'Immediate',
[TaskType.Timed]: 'Timed', [TaskType.Timed]: 'Timed',
[TaskType.Condition]: 'Continuous',
} }
export const TaskTypeOptions = [ export const TaskTypeOptions = [
{ value: TaskType.Immediate, label: TaskTypeMap[TaskType.Immediate] }, { value: TaskType.Immediate, label: TaskTypeMap[TaskType.Immediate] },
{ value: TaskType.Timed, label: TaskTypeMap[TaskType.Timed] }, { value: TaskType.Timed, label: TaskTypeMap[TaskType.Timed] },
{ value: TaskType.Condition, label: TaskTypeMap[TaskType.Condition] },
] ]
// 失控动作 // 失控动作

5
tsconfig.json

@ -24,6 +24,7 @@
"src/**/*.ts", "src/**/*.ts",
"src/**/*.d.ts", "src/**/*.d.ts",
"src/**/*.tsx", "src/**/*.tsx",
"src/**/*.vue" "src/**/*.vue",
, "src/vendors/coordtransform.js" ] "src/vendors/coordtransform.js"
]
} }
Loading…
Cancel
Save