Browse Source

What's new?

1. Add a movable live box.
2. Add conditional tasks.
v1.7.0
sean.zhou 1 year ago
parent
commit
07ff9ab4cc
  1. 18
      src/api/drone-control/drone.ts
  2. 12
      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 { @@ -18,6 +18,18 @@ export enum LostControlActionInCommandFLight {
Land = 1, // 着陆
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 {
latitude: number;
longitude: number;
@ -48,7 +60,11 @@ export interface PostTakeoffToPointBody{ @@ -48,7 +60,11 @@ export interface PostTakeoffToPointBody{
max_speed: number; // flyto过程中能达到的最大速度, 单位m/s 跟飞机档位有关
rc_lost_action: LostControlActionInCommandFLight; // 失控行为
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;
}
// 一键起飞

12
src/api/http/config.ts

@ -1,17 +1,17 @@ @@ -1,17 +1,17 @@
export const CURRENT_CONFIG = {
// license
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.
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.
appLicense: 'Please enter the app license.', // You need to go to the development website to apply.
// http
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
// 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.
gbServerIp: 'Please enter the server ip.',
gbServerPort: 'Please enter the server port.',
@ -29,7 +29,7 @@ export const CURRENT_CONFIG = { @@ -29,7 +29,7 @@ export const CURRENT_CONFIG = {
agoraToken: 'Please enter the agora temporary token.',
agoraChannel: 'Please enter the agora channel.',
// map
// map
// You can apply on the AMap website.
amapKey: 'Please enter the amap key.',

6
src/api/wayline.ts

@ -42,10 +42,12 @@ export interface CreatePlan { @@ -42,10 +42,12 @@ export interface CreatePlan {
dock_sn: string,
task_type: TaskType, // 任务类型
wayline_type: WaylineType, // 航线类型
task_days?: number[] // 执行任务的日期(秒)
task_periods?: number[][] // 执行任务的时间点(秒)
task_days: number[] // 执行任务的日期(秒)
task_periods: number[][] // 执行任务的时间点(秒)
rth_altitude: number // 相对机场返航高度 20 - 500
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

118
src/components/GMap.vue

@ -21,8 +21,8 @@ @@ -21,8 +21,8 @@
</div>
</div>
<!-- 飞机OSD -->
<div v-if="osdVisible.visible && !osdVisible.is_dock" 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 v-if="osdVisible.visible && !osdVisible.is_dock" v-drag-window class="osd-panel fz12">
<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><a class="fz16" style="color: white;" @click="() => osdVisible.visible = false"><CloseOutlined /></a></span>
</div>
@ -49,7 +49,7 @@ @@ -49,7 +49,7 @@
<a-col span="6">
<a-tooltip title="RC Battery Level">
<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-col>
@ -141,11 +141,11 @@ @@ -141,11 +141,11 @@
</div>
</div>
<!-- 机场OSD -->
<div v-if="osdVisible.visible && osdVisible.is_dock" 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 v-if="osdVisible.visible && osdVisible.is_dock" v-drag-window class="osd-panel fz12">
<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><a style="color: white;" @click="() => osdVisible.visible = false"><CloseOutlined /></a></span>
</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-column flex-align-stretch flex-justify-center" style="width: 60px; background: #2d2d2d;">
@ -267,7 +267,7 @@ @@ -267,7 +267,7 @@
<a-col span="6">
<a-tooltip title="Drone in dock">
<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-col>
</a-row>
@ -413,6 +413,17 @@ @@ -413,6 +413,17 @@
<!-- 飞行指令 -->
<DroneControlPanel :sn="osdVisible.gateway_sn" :deviceInfo="deviceInfo" :payloads="osdVisible.payloads"></DroneControlPanel>
</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>
</template>
@ -452,6 +463,8 @@ import DockControlPanel from './g-map/DockControlPanel.vue' @@ -452,6 +463,8 @@ import DockControlPanel from './g-map/DockControlPanel.vue'
import { useDockControl } from './g-map/use-dock-control'
import DroneControlPanel from './g-map/DroneControlPanel.vue'
import { useConnectMqtt } from './g-map/use-connect-mqtt'
import LivestreamOthers from './livestream-others.vue'
import LivestreamAgora from './livestream-agora.vue'
export default defineComponent({
components: {
@ -475,7 +488,9 @@ export default defineComponent({ @@ -475,7 +488,9 @@ export default defineComponent({
DockControlPanel,
DroneControlPanel,
CarryOutOutlined,
RocketOutlined
RocketOutlined,
LivestreamOthers,
LivestreamAgora
},
name: 'GMap',
props: {},
@ -534,6 +549,12 @@ export default defineComponent({ @@ -534,6 +549,12 @@ export default defineComponent({
const drawVisible = computed(() => {
return store.state.drawVisible
})
const livestreamOthersVisible = computed(() => {
return store.state.livestreamOthersVisible
})
const livestreamAgoraVisible = computed(() => {
return store.state.livestreamAgoraVisible
})
const osdVisible = computed(() => {
return store.state.osdVisible
})
@ -572,7 +593,7 @@ export default defineComponent({ @@ -572,7 +593,7 @@ export default defineComponent({
}
}
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 !== '') {
deviceInfo.dock = data.dockInfo[osdVisible.value.gateway_sn]
deviceInfo.device = data.deviceInfo[deviceInfo.dock.basic_osd.sub_device?.device_sn ?? osdVisible.value.sn]
@ -607,15 +628,27 @@ export default defineComponent({ @@ -607,15 +628,27 @@ export default defineComponent({
})
updateCoordinates('wgs84-gcj02', ele)
useGMapCoverHook.init2DPin(
ele.name,
ele.resource.content.geometry.coordinates,
ele.resource.content.properties.color,
{
id: ele.id,
name: ele.name
}
)
const data = { id: ele.id, name: ele.name }
if (MapElementEnum.PIN === ele.resource?.type) {
useGMapCoverHook.init2DPin(
ele.name,
ele.resource.content.geometry.coordinates,
ele.resource.content.properties.color,
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 = {}
@ -749,6 +782,12 @@ export default defineComponent({ @@ -749,6 +782,12 @@ export default defineComponent({
console.log('layers', 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) {
const geoType = element.resource?.content.geometry.type
const type = element.resource?.type as number
@ -769,39 +808,38 @@ export default defineComponent({ @@ -769,39 +808,38 @@ export default defineComponent({
) as GeojsonCoordinate
element.resource.content.geometry.coordinates = transResult
}
} else if (MapElementEnum.LINE === type && geoType === 'LineString') {
} else if (MapElementEnum.LINE === type) {
const coordinates = element.resource?.content.geometry
.coordinates as GeojsonCoordinate[]
if (transformType === 'wgs84-gcj02') {
coordinates.forEach(coordinate => {
coordinate = wgs84togcj02(
coordinates.forEach((coordinate, i, arr) => {
arr[i] = wgs84togcj02(
coordinate[0],
coordinate[1]
) as GeojsonCoordinate
})
} else if (transformType === 'gcj02-wgs84') {
coordinates.forEach(coordinate => {
coordinate = gcj02towgs84(
coordinates.forEach((coordinate, i, arr) => {
arr[i] = gcj02towgs84(
coordinate[0],
coordinate[1]
) as GeojsonCoordinate
})
}
element.resource.content.geometry.coordinates = coordinates
} else if (MapElementEnum.LINE === type && geoType === 'Polygon') {
} else if (MapElementEnum.POLY === type) {
const coordinates = element.resource?.content.geometry
.coordinates[0] as GeojsonCoordinate[]
if (transformType === 'wgs84-gcj02') {
coordinates.forEach(coordinate => {
coordinate = wgs84togcj02(
coordinates.forEach((coordinate, i, arr) => {
arr[i] = wgs84togcj02(
coordinate[0],
coordinate[1]
) as GeojsonCoordinate
})
} else if (transformType === 'gcj02-wgs84') {
coordinates.forEach(coordinate => {
coordinate = gcj02towgs84(
coordinates.forEach((coordinate, i, arr) => {
arr[i] = gcj02towgs84(
coordinate[0],
coordinate[1]
) as GeojsonCoordinate
@ -815,6 +853,8 @@ export default defineComponent({ @@ -815,6 +853,8 @@ export default defineComponent({
draw,
mouseMode,
drawVisible,
livestreamOthersVisible,
livestreamAgoraVisible,
osdVisible,
pin,
state,
@ -830,7 +870,9 @@ export default defineComponent({ @@ -830,7 +870,9 @@ export default defineComponent({
NetworkStateTypeEnum,
NetworkStateQualityEnum,
RainfallEnum,
DroneInDockEnum
DroneInDockEnum,
closeLivestreamOthers,
closeLivestreamAgora
}
}
})
@ -875,7 +917,8 @@ export default defineComponent({ @@ -875,7 +917,8 @@ export default defineComponent({
}
.osd-panel {
position: absolute;
left: 10px;
margin-left: 10px;
left: 0;
top: 10px;
width: 480px;
background: #000;
@ -926,4 +969,17 @@ export default defineComponent({ @@ -926,4 +969,17 @@ export default defineComponent({
min-height: 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>

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

@ -73,15 +73,14 @@ watch(() => props.deviceInfo, (value) => { @@ -73,15 +73,14 @@ watch(() => props.deviceInfo, (value) => {
deep: true
})
// dock
const debugStatus = ref(props.deviceInfo.dock?.basic_osd?.mode_code === EDockModeCode.Remote_Debugging)
const emit = defineEmits(['close-control-panel'])
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) {
let result = false
if (status) {

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

@ -112,15 +112,53 @@ @@ -112,15 +112,53 @@
:options="WaylineLostControlActionInCommandFlightOptions"
></a-select>
</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>
</template>
<Button size="small" ghost @click="onShowTakeoffToPointPopover" >
<span>Take off</span>
</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>
<Button :loading="cmdItem.loading" size="small" ghost @click="sendControlCmd(cmdItem, 0)">
{{ cmdItem.operateText }}
</Button>
</div>
</div>
<div class="box">
@ -250,9 +288,15 @@ import { usePayloadControl } from './use-payload-control' @@ -250,9 +288,15 @@ import { usePayloadControl } from './use-payload-control'
import { CameraMode, CameraType, CameraTypeOptions, ZoomCameraTypeOptions, CameraListItem } from '/@/types/live-stream'
import { useDroneControlWsEvent } from './use-drone-control-ws-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 { 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 DroneControlInfoPanel from './DroneControlInfoPanel.vue'
import { noDebugCmdList as baseCmdList, DeviceCmdItem, DeviceCmd } from '/@/types/device-cmd'
@ -269,8 +313,8 @@ const clientId = computed(() => { @@ -269,8 +313,8 @@ const clientId = computed(() => {
return store.state.clientId
})
const initCmdList = baseCmdList.find(item => item.cmdKey === DeviceCmd.ReturnHome) as DeviceCmdItem
const cmdItem = ref(initCmdList)
const initCmdList = baseCmdList.map(cmdItem => Object.assign({}, cmdItem))
const cmdList = ref(initCmdList)
const {
sendDockControlCmd
@ -283,9 +327,11 @@ async function sendControlCmd (cmdItem: DeviceCmdItem, index: number) { @@ -283,9 +327,11 @@ async function sendControlCmd (cmdItem: DeviceCmdItem, index: number) {
cmd: cmdItem.cmdKey,
action: cmdItem.action
}, false)
if (result && flightController.value) {
if (result) {
message.success('Return home successful')
exitFlightCOntrol()
if (flightController.value) {
exitFlightCOntrol()
}
} else {
message.error('Failed to return home')
}
@ -349,7 +395,11 @@ const takeoffToPointPopoverData = reactive({ @@ -349,7 +395,11 @@ const takeoffToPointPopoverData = reactive({
maxSpeed: MAX_SPEED,
rthAltitude: null as null | number,
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 () {
@ -361,6 +411,10 @@ function onShowTakeoffToPointPopover () { @@ -361,6 +411,10 @@ function onShowTakeoffToPointPopover () {
takeoffToPointPopoverData.rthAltitude = null
takeoffToPointPopoverData.rcLostAction = LostControlActionInCommandFLight.RETURN_HOME
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) {
@ -369,7 +423,8 @@ async function onTakeoffToPointConfirm (confirm: boolean) { @@ -369,7 +423,8 @@ async function onTakeoffToPointConfirm (confirm: boolean) {
!takeoffToPointPopoverData.latitude ||
!takeoffToPointPopoverData.longitude ||
!takeoffToPointPopoverData.securityTakeoffHeight ||
!takeoffToPointPopoverData.rthAltitude) {
!takeoffToPointPopoverData.rthAltitude ||
!takeoffToPointPopoverData.commanderFlightHeight) {
message.error('Input error')
return
}
@ -382,7 +437,11 @@ async function onTakeoffToPointConfirm (confirm: boolean) { @@ -382,7 +437,11 @@ async function onTakeoffToPointConfirm (confirm: boolean) {
rth_altitude: takeoffToPointPopoverData.rthAltitude,
max_speed: takeoffToPointPopoverData.maxSpeed,
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) {
}
@ -672,6 +731,14 @@ function onShowCameraAimPopover () { @@ -672,6 +731,14 @@ function onShowCameraAimPopover () {
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) {
if (confirm) {
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 () { @@ -45,8 +45,10 @@ export function useDockControl () {
}
// 控制面板关闭
async function onCloseControlPanel (sn: string) {
await dockDebugOnOff(sn, false)
async function onCloseControlPanel (sn: string, debugging: boolean) {
if (debugging) {
await dockDebugOnOff(sn, false)
}
setDockControlPanelVisible(false)
}

2
src/components/livestream-agora.vue

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
<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>
<p class="fz24">Live streaming source selection</p>
<div class="flex-row flex-justify-center flex-align-center mt10">

2
src/components/livestream-others.vue

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

157
src/components/task/CreatePlan.vue

@ -49,7 +49,7 @@ @@ -49,7 +49,7 @@
>Select Device</router-link>
</a-form-item>
<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">
<a-tooltip :title="dock.nickname">
<div class="pr10" style="width: 120px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden;">{{ dock.nickname }}</div>
@ -62,22 +62,64 @@ @@ -62,22 +62,64 @@
</div>
</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;">
<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-group>
</div>
</a-form-item>
<!-- 执行时间 -->
<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"
<!-- execute date -->
<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-range-picker
v-model:value="planBody.select_execute_date"
: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
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>
<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 -->
<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>
@ -145,12 +187,16 @@ const disabled = ref(false) @@ -145,12 +187,16 @@ const disabled = ref(false)
const routeName = ref('')
const planBody = reactive({
name: '',
file_id: computed(() => store.state.waylineInfo.id),
dock_sn: computed(() => store.state.dockInfo.device_sn),
file_id: computed(() => store.state?.waylineInfo.id),
dock_sn: computed(() => store.state?.dockInfo.device_sn),
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: '',
out_of_control_action: OutOfControlAction.ReturnToHome,
min_battery_capacity: 90 as number,
min_storage_capacity: undefined as number | undefined,
})
const drawerVisible = ref(false)
@ -158,16 +204,35 @@ const valueRef = ref() @@ -158,16 +204,35 @@ const valueRef = ref()
const rules = {
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' }],
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: [
{
validator: async (rule: RuleObject, value: string) => {
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 = { @@ -175,19 +240,61 @@ const rules = {
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 () {
console.info(dock, '12131231')
valueRef.value.validate().then(() => {
disabled.value = true
const createPlanBody = { ...planBody } as unknown as CreatePlan
if (planBody.select_execute_time) {
createPlanBody.task_days = [moment(planBody.select_execute_time).unix()]
createPlanBody.task_periods = [createPlanBody.task_days]
if (planBody.select_execute_date.length === 2) {
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)
if (wayline.value && wayline.value.template_types && wayline.value.template_types.length > 0) {
createPlanBody.wayline_type = wayline.value.template_types[0]
}
// console.log('planBody', createPlanBody)
createPlan(workspaceId, createPlanBody)
.then(res => {
disabled.value = false
@ -217,6 +324,18 @@ function selectDevice () { @@ -217,6 +324,18 @@ function selectDevice () {
drawerVisible.value = true
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>
<style lang="scss">
@ -266,7 +385,7 @@ function selectDevice () { @@ -266,7 +385,7 @@ function selectDevice () {
.ant-radio-button-wrapper{
background-color: #232323;
color: #fff;
width: 80%;
width: 33%;
text-align: center;
&.ant-radio-button-wrapper-checked{
background-color: #1890ff;

4
src/constants/map.ts

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

38
src/directives/drag-window.ts

@ -0,0 +1,38 @@ @@ -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 @@ @@ -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 () { @@ -78,9 +78,9 @@ export function useGMapCover () {
AddOverlayGroup(polyline)
}
function initPolygon (name: string, coordinates:GeojsonCoordinate[], color?:string, data?:{}) {
function initPolygon (name: string, coordinates:GeojsonCoordinate[][], color?:string, data?:{}) {
const path = [] as GeojsonCoordinate[]
coordinates.forEach(coordinate => {
coordinates[0].forEach(coordinate => {
path.push(new AMap.LngLat(coordinate[0], coordinate[1]))
})
// console.log('Polygon', path)
@ -141,12 +141,43 @@ export function useGMapCover () { @@ -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 {
init2DPin,
initPolyline,
initPolygon,
removeCoverFromMap,
getElementFromMap,
updatePinElement
updatePinElement,
updatePolylineElement,
updatePolygonElement
}
}

4
src/main.ts

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

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

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

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

@ -54,7 +54,26 @@ @@ -54,7 +54,26 @@
<div class="width-100" style="margin-top: -10px;">
<div class="ml10" style="width: 97%;">
<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.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>
@ -154,11 +173,11 @@ const liveTypeList = [ @@ -154,11 +173,11 @@ const liveTypeList = [
label: ELiveTypeName.GB28181
}
]
const agoraParam = {
const agoraParam = reactive({
uid: '2892130292',
token: config.agoraToken,
channelId: config.agoraChannel
}
})
const rtmpParam = {
url: config.rtmpURL + new Date().getTime()
}
@ -174,7 +193,7 @@ const gb28181Param: GB28181Param = { @@ -174,7 +193,7 @@ const gb28181Param: GB28181Param = {
agentId: CURRENT_CONFIG.gbAgentId,
password: CURRENT_CONFIG.gbPassword,
agentPort: CURRENT_CONFIG.gbAgentPort,
agentChannel: CURRENT_CONFIG.gbAgentChannel
agentChannel: CURRENT_CONFIG.gbAgentChannel,
}
const playVisiable = ref(false)
@ -253,6 +272,7 @@ const onPublishModeSelect = (val: string) => { @@ -253,6 +272,7 @@ const onPublishModeSelect = (val: string) => {
apiPilot.setVideoPublishType(publishModeSelected.value)
}
const onPlay = () => {
console.info(JSON.stringify(agoraParam))
if (!publishModeSelected.value) {
message.warn('Please select publish mode!')
return

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

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

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

@ -18,7 +18,8 @@ @@ -18,7 +18,8 @@
>
</router-link>
</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>
<router-view :name="routeName" />
</div>
@ -74,14 +75,13 @@ onMounted(() => { @@ -74,14 +75,13 @@ onMounted(() => {
.live {
position: absolute;
z-index: 1;
right: 50%;
left: 50%;
top: 50%;
margin: auto;
transform: translate(-50%, -50%);
left: 0;
top: 10px;
margin-left: 345px;
text-align: center;
width: 800px;
height: 700px;
height: 720px;
background: #232323;
}
</style>

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

@ -203,7 +203,7 @@ import { EHmsLevel } from '/@/types/enums' @@ -203,7 +203,7 @@ import { EHmsLevel } from '/@/types/enums'
const store = useMyStore()
const username = ref(localStorage.getItem(ELocalStorageKey.Username))
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 scorllHeight = ref()
@ -344,6 +344,15 @@ function readHms (visiable: boolean, sn: string) { @@ -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>
<style lang="scss">

8
src/router/index.ts

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

10
src/store/index.ts

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

9
src/types/device-cmd.ts

@ -7,6 +7,7 @@ export enum DeviceCmd { @@ -7,6 +7,7 @@ export enum DeviceCmd {
SupplementLightOpen = 'supplement_light_open', // 打开补光灯
SupplementLightClose = 'supplement_light_close', // 关闭补光灯
ReturnHome = 'return_home', // 一键返航
ReturnHomeCancel = 'return_home_cancel', // 取消返航
// 复杂指令
DeviceReboot = 'device_reboot', // 机场重启
DroneOpen = 'drone_open', // 飞行器开机
@ -48,6 +49,14 @@ export const noDebugCmdList: DeviceCmdItem[] = [ @@ -48,6 +49,14 @@ export const noDebugCmdList: DeviceCmdItem[] = [
func: 'returnHome',
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 { @@ -319,7 +319,7 @@ export interface DockBasicOsd {
state: number,
capacity_percent: number,
},
drone_in_dock: DroneInDockEnum,
drone_in_dock: boolean,
rainfall: RainfallEnum,
wind_speed: number,
environment_temperature: number,
@ -379,7 +379,7 @@ export interface DockLinkOsd { @@ -379,7 +379,7 @@ export interface DockLinkOsd {
media_file_detail?: {
remain_upload: number
},
sdr: {
sdr?: {
up_quality: string,
down_quality: string,
frequency_band: number,

17
src/types/drone-control.ts

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
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 {
Flight = 1,
@ -52,6 +52,21 @@ export const LostControlActionInCommandFLightOptions = [ @@ -52,6 +52,21 @@ export const LostControlActionInCommandFLightOptions = [
{ 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 {
Recenter = 0,

3
src/types/task.ts

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

5
tsconfig.json

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