You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
539 lines
18 KiB
539 lines
18 KiB
<template> |
|
<a-layout class="page"> |
|
<a-layout-sider class="left" width="40%" style="border-radius: 4px;"> |
|
<div style="width:90%; height: 90%; margin: 4vh"> |
|
<a-layout style="height: 20%; margin-top: 3vh; background-color: white; "> |
|
<a-layout-sider width="25%" theme="light" align="center"> |
|
<a-avatar :size="60" :src="cloudapi"> |
|
</a-avatar> |
|
</a-layout-sider> |
|
<a-layout-content style="margin-left: 1vw;" @click="showStatus"> |
|
<div style="height: 50%;"> |
|
<span style="font-size: 16px; font-weight: bolder">{{ workspaceName }}</span> |
|
<RightOutlined style="float: right; margin-top: 5px; color: #8894a0" /> |
|
</div> |
|
<div style="height: 50%;"> |
|
<CloudSyncOutlined v-if="state === EStatusValue.CONNECTED" style="color: #75c5f6" /> |
|
<SyncOutlined spin v-else/> |
|
<span style="color: #737373; margin-left: 3px;">{{ state }}</span> |
|
</div> |
|
<a-drawer placement="right" v-model:visible="drawerVisible" width="340px"> |
|
<div class="mb10 flex-row flex-justify-center flex-align-center"> |
|
<p class="fz14" style="font-weight: 100;">Module State</p> |
|
</div> |
|
<div class= "width-100 mb10 flex-align-start" v-for="m in modules" :key="m.name" style="height: 30px;"> |
|
|
|
<div class="ml5" style="float: left; color: #000000;">{{m.name}}:</div> |
|
<div class="ml10" style="float: right; margin-bottom: 8px;"> |
|
<span :key="m.state" :class="m.state.value === EStatusValue.CONNECTED ? 'green' : 'red'">{{ m.state.value }} </span> |
|
<a-button-group > |
|
<a-button class="ml5" type="primary" size="small" @click.stop="moduleInstall(m)">install</a-button> |
|
<a-button class="ml5 mr5" type="danger" size="small" @click.stop="moduleUninstall(m)">uninstall</a-button> |
|
</a-button-group> |
|
</div> |
|
<a-divider /> |
|
|
|
</div> |
|
</a-drawer> |
|
</a-layout-content> |
|
|
|
</a-layout> |
|
<a-divider style="height: 2px; background-color: #f5f5f5; margin-top: 3vh;" /> |
|
|
|
<a-button id="exitBtn" class="fz18" @click="confirmAgain" |
|
style="width: 10vw; height: 10vh; position: fixed; bottom: 13vh; left: 15vw; background-color: #e6e6e6; color: red; border: 0;" |
|
type="primary">Exit |
|
</a-button> |
|
<a-modal v-model:visible="exitVisible" width="300px" :closable="false"> |
|
<template #footer> |
|
<a-button type="text" style="width: 48%; float: left;" @click="onBack">Cancel</a-button> |
|
<a-button type="text" style="width: 48%;" @click="onExit">Exit</a-button> |
|
</template> |
|
<p>Data will not be synchronized between DJI Pilot and this server after exiting.</p> |
|
</a-modal> |
|
</div> |
|
</a-layout-sider> |
|
<a-layout-content class="right flex-column"> |
|
<div class="mb5"> |
|
<span class="ml5" style="color: #939393;">Serial Number</span> |
|
</div> |
|
<div class="fz16" style="background-color: white; border-radius: 4px;"> |
|
<a-row style="border-bottom: 1px solid #f4f8f9; height: 45px;" align="middle"> |
|
<a-col :span="1"></a-col> |
|
<a-col :span="9"> |
|
Remote Control Sn |
|
</a-col> |
|
<a-col :span="13" class="flex-align-end flex-column"> |
|
<span style="color: #737373">{{ device.data.gateway_sn }}</span> |
|
</a-col> |
|
</a-row> |
|
<a-row style="border-bottom: 1px solid #f4f8f9; height: 45px;" align="middle" v-if="device.data.online_status"> |
|
<a-col :span="1"></a-col> |
|
<a-col :span="9">Aircraft Sn</a-col> |
|
<a-col :span="13" class="flex-align-end flex-column" > |
|
<span style="color: #737373">{{ device.data.sn }}</span> |
|
</a-col> |
|
</a-row> |
|
</div> |
|
<div class="mt5 mb5"> |
|
<span class="ml5" style="color: #939393;">Settings</span> |
|
</div> |
|
<div class="fz16" style="background-color: white; border-radius: 4px;"> |
|
<a-row v-if="device.data.online_status" style="border-bottom: 1px solid #f4f8f9; height: 45px;" align="middle" @click="bindingDevice"> |
|
<a-col :span="1"></a-col> |
|
<a-col :span="11"> |
|
Device Binding |
|
</a-col> |
|
<a-col :span="10" style="text-align: right"> |
|
<span v-if="device.data.bound_status" style="color: #737373">Aircraft bound</span> |
|
<span v-else style="color: #737373">Aircraft not bound</span> |
|
</a-col> |
|
<a-col :span="2" class="flex-align-center flex-column" > |
|
<RightOutlined style="color: #8894a0; font-size: 20px;" /> |
|
</a-col> |
|
</a-row> |
|
<a-row style="border-bottom: 1px solid #f4f8f9; height: 45px;" align="middle" @click="onMediaSetting"> |
|
<a-col :span="1"></a-col> |
|
<a-col :span="21"> |
|
Media File Upload |
|
</a-col> |
|
<a-col :span="2" class="flex-align-center flex-column" > |
|
<RightOutlined style="color: #8894a0; font-size: 20px;" /> |
|
</a-col> |
|
</a-row> |
|
<a-row style="border-bottom: 1px solid #f4f8f9; height: 45px;" align="middle" @click="onLiveshareSetting"> |
|
<a-col :span="1"></a-col> |
|
<a-col :span="21">Livestream Manually</a-col> |
|
<a-col :span="2" class="flex-align-center flex-column"> |
|
<RightOutlined style="color: #8894a0; font-size: 20px;" /> |
|
</a-col> |
|
</a-row> |
|
<a-row style="border-bottom: 1px solid #f4f8f9; height: 45px;" align="middle" @click="onOpen3rdApp"> |
|
<a-col :span="1"></a-col> |
|
<a-col :span="21">Open 3rd Party APP</a-col> |
|
<a-col :span="2" class="flex-align-center flex-column"> |
|
<RightOutlined style="color: #8894a0; font-size: 20px;" /> |
|
</a-col> |
|
</a-row> |
|
</div> |
|
</a-layout-content> |
|
</a-layout> |
|
</template> |
|
<script lang="ts" setup> |
|
import { message, Popconfirm } from 'ant-design-vue' |
|
import { onMounted, onUnmounted, reactive, ref, watch } from 'vue' |
|
import { CURRENT_CONFIG } from '/@/api/http/config' |
|
import { BindBody, bindDevice, getDeviceBySn, getPlatformInfo, getUserInfo } from '/@/api/manage' |
|
import apiPilot, { ApiParam, MapParam, ThingParam, WsParam } from '/@/api/pilot-bridge' |
|
import { getRoot } from '/@/root' |
|
import { EBizCode, EComponentName, EDownloadOwner, ELocalStorageKey, ERouterName, EStatusValue } from '/@/types' |
|
import cloudapi from '/@/assets/icons/cloudapi.png' |
|
import { RightOutlined, CloudOutlined, CloudSyncOutlined, SyncOutlined } from '@ant-design/icons-vue' |
|
import { useMyStore } from '/@/store' |
|
import ReconnectingWebSocket from 'reconnecting-websocket' |
|
import websocket from '/@/api/websocket' |
|
import { DeviceStatus } from '/@/types/device' |
|
|
|
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) |
|
const wsState = ref(EStatusValue.DISCONNECT) |
|
const mapState = ref(EStatusValue.DISCONNECT) |
|
const tsaState = ref(EStatusValue.DISCONNECT) |
|
const mediaState = ref(EStatusValue.DISCONNECT) |
|
const waylineState = ref(EStatusValue.DISCONNECT) |
|
const workspaceName = ref<string>(localStorage.getItem(ELocalStorageKey.WorkspaceName)!) |
|
const username = ref(localStorage.getItem(ELocalStorageKey.Username)!) |
|
const wsId = ref(localStorage.getItem(ELocalStorageKey.WorkspaceId)!) |
|
const components = apiPilot.init() |
|
const exitVisible = ref(false) |
|
const drawerVisible = ref(false) |
|
let minitor = -1 |
|
|
|
interface DeviceInfoData { |
|
data: DeviceStatus |
|
} |
|
const device = reactive<DeviceInfoData>({ |
|
data: { |
|
sn: EStatusValue.DISCONNECT, |
|
online_status: false, |
|
device_callsign: '', |
|
user_id: '', |
|
user_callsign: '', |
|
bound_status: false, |
|
model: '', |
|
gateway_sn: EStatusValue.DISCONNECT, |
|
domain: '' |
|
} |
|
}) |
|
const bindParam: BindBody = { |
|
device_sn: '', |
|
user_id: '', |
|
workspace_id: wsId.value |
|
} |
|
|
|
const modules = [{ |
|
name: 'Cloud', |
|
state: thingState, |
|
module: EComponentName.Thing |
|
}, { |
|
name: 'Api', |
|
state: apiState, |
|
module: EComponentName.Api |
|
}, { |
|
name: 'Live', |
|
state: liveState, |
|
module: EComponentName.Liveshare |
|
}, { |
|
name: 'Ws', |
|
state: wsState, |
|
module: EComponentName.Ws |
|
}, { |
|
name: 'Map', |
|
state: mapState, |
|
module: EComponentName.Map |
|
}, { |
|
name: 'Tsa', |
|
state: tsaState, |
|
module: EComponentName.Tsa |
|
}, { |
|
name: 'Media', |
|
state: mediaState, |
|
module: EComponentName.Media |
|
}, { |
|
name: 'Wayline', |
|
state: waylineState, |
|
module: EComponentName.Mission |
|
}] |
|
|
|
const store = useMyStore() |
|
|
|
const wsGetMsg = async (res: any) => { |
|
const payload = JSON.parse(res.data) |
|
switch (payload.biz_code) { |
|
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) { |
|
device.data = payload.data |
|
localStorage.setItem(ELocalStorageKey.Device, JSON.stringify(device.data)) |
|
} |
|
break |
|
} |
|
case EBizCode.DeviceOffline: { |
|
console.info('offline: ', payload) |
|
if (payload.data.sn === device.data.sn) { |
|
device.data.online_status = payload.data.online_status |
|
localStorage.setItem(ELocalStorageKey.Device, JSON.stringify(device.data)) |
|
} |
|
break |
|
} |
|
|
|
default: |
|
break |
|
} |
|
} |
|
let bindNum: number |
|
let socket: ReconnectingWebSocket |
|
|
|
onMounted(() => { |
|
apiPilot.onBackClickReg() |
|
apiPilot.onStopPlatform() |
|
|
|
device.data.gateway_sn = apiPilot.getRemoteControllerSN() |
|
if (device.data.gateway_sn === EStatusValue.DISCONNECT.toString()) { |
|
message.warn('Data is not available, please restart the remote control.') |
|
return |
|
} |
|
const oldDevice = localStorage.getItem(ELocalStorageKey.Device) |
|
if (oldDevice) { |
|
device.data = JSON.parse(oldDevice) |
|
} |
|
device.data.sn = apiPilot.getAircraftSN() |
|
getDeviceInfo() |
|
|
|
socket = websocket.init(wsGetMsg) |
|
|
|
const isLoaded = apiPilot.isComponentLoaded(EComponentName.Thing) |
|
if (isLoaded) { |
|
username.value = '' + localStorage.getItem(ELocalStorageKey.Username) |
|
workspaceName.value = '' + localStorage.getItem(ELocalStorageKey.WorkspaceName) |
|
refreshStatus() |
|
apiPilot.setPlatformMessage( |
|
'' + localStorage.getItem(ELocalStorageKey.PlatformName), |
|
workspaceName.value, |
|
'' + localStorage.getItem(ELocalStorageKey.WorkspaceDesc) |
|
) |
|
return |
|
} |
|
|
|
setWorkspaceInfo() |
|
|
|
getUserInfo().then(res => { |
|
username.value = res.data.username |
|
localStorage.setItem(ELocalStorageKey.Username, username.value) |
|
// thing |
|
const param: ThingParam = { |
|
host: res.data.mqtt_addr, |
|
username: username.value, |
|
password: res.data.mqtt_password, |
|
connectCallback: 'connectCallback' |
|
} |
|
components.set(EComponentName.Thing, param) |
|
apiPilot.loadComponent(EComponentName.Thing, components.get(EComponentName.Thing)) |
|
|
|
bindParam.device_sn = device.data.gateway_sn |
|
bindParam.user_id = res.data.user_id |
|
bindParam.workspace_id = res.data.workspace_id |
|
}) |
|
window.connectCallback = arg => { |
|
connectCallback(arg) |
|
} |
|
window.wsConnectCallback = arg => { |
|
wsConnectCallback(arg) |
|
} |
|
}) |
|
|
|
onUnmounted(() => { |
|
socket.close() |
|
}) |
|
|
|
const connectCallback = async (arg: any) => { |
|
if (arg) { |
|
thingState.value = EStatusValue.CONNECTED |
|
// liveshare |
|
apiPilot.loadComponent(EComponentName.Liveshare, components.get(EComponentName.Liveshare)) |
|
|
|
// ws |
|
const wsParam: WsParam = components.get(EComponentName.Ws) |
|
wsParam.token = apiPilot.getToken() |
|
apiPilot.loadComponent(EComponentName.Ws, components.get(EComponentName.Ws)) |
|
|
|
// map |
|
const mapParam: MapParam = components.get(EComponentName.Map) |
|
mapParam.userName = username.value |
|
apiPilot.loadComponent(EComponentName.Map, components.get(EComponentName.Map)) |
|
|
|
// tsa |
|
apiPilot.loadComponent(EComponentName.Tsa, components.get(EComponentName.Tsa)) |
|
|
|
// media |
|
apiPilot.loadComponent(EComponentName.Media, components.get(EComponentName.Media)) |
|
apiPilot.setDownloadOwner(EDownloadOwner.Mine.valueOf()) |
|
|
|
// mission |
|
apiPilot.loadComponent(EComponentName.Mission, {}) |
|
|
|
bindNum = setInterval(() => { |
|
bindDevice(bindParam).then(bindRes => { |
|
if (bindRes.code !== 0) { |
|
message.error(bindRes.message) |
|
console.error(bindRes.message) |
|
} else { |
|
clearInterval(bindNum) |
|
} |
|
}) |
|
}, 2000) |
|
setTimeout(getDeviceInfo, 3000) |
|
} else { |
|
thingState.value = EStatusValue.DISCONNECT |
|
} |
|
refreshStatus() |
|
} |
|
const wsConnectCallback = async (arg: any) => { |
|
if (arg) { |
|
wsState.value = EStatusValue.CONNECTED |
|
} else { |
|
wsState.value = EStatusValue.DISCONNECT |
|
} |
|
} |
|
|
|
const confirmAgain = () => { |
|
exitVisible.value = true |
|
} |
|
|
|
const onBack = () => { |
|
exitVisible.value = false |
|
} |
|
|
|
const onExit = () => { |
|
localStorage.clear() |
|
apiPilot.stopwebview() |
|
} |
|
|
|
const bindingDevice = async () => { |
|
root.$router.push(ERouterName.PILOT_BIND) |
|
} |
|
|
|
const onMediaSetting = async (e: any) => { |
|
root.$router.push(ERouterName.PILOT_MEDIA) |
|
} |
|
const onLiveshareSetting = async (e: any) => { |
|
root.$router.push(ERouterName.PILOT_LIVESHARE) |
|
} |
|
const onOpen3rdApp = () => { |
|
const packageName = 'com.dji.sample' |
|
const isInstalled = apiPilot.isAppInstalled(packageName) |
|
if (isInstalled) { |
|
window.open('https://www.dji.com') |
|
} else { |
|
message.error(packageName + ' is not installed.') |
|
} |
|
} |
|
|
|
const showStatus = async () => { |
|
minitor = setInterval(() => { |
|
refreshStatus() |
|
if (!drawerVisible.value) { |
|
clearInterval(minitor) |
|
} |
|
}, 2000) |
|
drawerVisible.value = true |
|
} |
|
|
|
function setWorkspaceInfo () { |
|
if (localStorage.getItem(ELocalStorageKey.WorkspaceName)) { |
|
apiPilot.setPlatformMessage( |
|
'' + localStorage.getItem(ELocalStorageKey.PlatformName), |
|
workspaceName.value, |
|
'' + localStorage.getItem(ELocalStorageKey.WorkspaceDesc) |
|
) |
|
apiPilot.setWorkspaceId(wsId.value) |
|
|
|
return |
|
} |
|
getPlatformInfo().then(res => { |
|
console.log(res) |
|
workspaceName.value = res.data.workspace_name |
|
wsId.value = res.data.workspace_id |
|
localStorage.setItem(ELocalStorageKey.PlatformName, res.data.platform_name) |
|
localStorage.setItem(ELocalStorageKey.WorkspaceName, workspaceName.value) |
|
localStorage.setItem(ELocalStorageKey.WorkspaceDesc, res.data.workspace_desc) |
|
apiPilot.setPlatformMessage( |
|
res.data.platform_name, |
|
workspaceName.value, |
|
res.data.workspace_desc |
|
) |
|
apiPilot.setWorkspaceId(wsId.value) |
|
}) |
|
} |
|
|
|
function refreshStatus () { |
|
thingState.value = apiPilot.thingGetConnectState() ? EStatusValue.CONNECTED : EStatusValue.DISCONNECT |
|
apiState.value = apiPilot.isComponentLoaded(EComponentName.Api) ? EStatusValue.CONNECTED : EStatusValue.DISCONNECT |
|
liveState.value = apiPilot.isComponentLoaded(EComponentName.Liveshare) ? EStatusValue.CONNECTED : EStatusValue.DISCONNECT |
|
wsState.value = apiPilot.isComponentLoaded(EComponentName.Ws) && apiPilot.wsGetConnectState() |
|
? EStatusValue.CONNECTED |
|
: EStatusValue.DISCONNECT |
|
mapState.value = apiPilot.isComponentLoaded(EComponentName.Map) ? EStatusValue.CONNECTED : EStatusValue.DISCONNECT |
|
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) { |
|
let param |
|
switch (m.module) { |
|
case EComponentName.Thing: |
|
param = apiPilot.thingGetConfigs() |
|
break |
|
case EComponentName.Api: { |
|
const apiParam: ApiParam = { |
|
host: apiPilot.getHost(), |
|
token: apiPilot.getToken() |
|
} |
|
param = apiParam |
|
break |
|
} |
|
case EComponentName.Map: { |
|
const mapParam: MapParam = components.get(EComponentName.Map) |
|
mapParam.userName = '' + localStorage.getItem(ELocalStorageKey.Username) |
|
param = mapParam |
|
break |
|
} |
|
case EComponentName.Ws: { |
|
const wsParam: WsParam = components.get(EComponentName.Ws) |
|
wsParam.token = '' + localStorage.getItem(ELocalStorageKey.Token) |
|
param = wsParam |
|
break |
|
} |
|
default: |
|
param = components.get(m.module) |
|
} |
|
|
|
components.set(m.module, param) |
|
console.info(components.get(m.module)) |
|
apiPilot.loadComponent(m.module, components.get(m.module)) |
|
refreshStatus() |
|
} |
|
|
|
function moduleUninstall (m: any) { |
|
message.info('uninstall ' + m.module) |
|
apiPilot.unloadComponent(m.module) |
|
refreshStatus() |
|
} |
|
|
|
function getDeviceInfo () { |
|
if (device.data.sn === EStatusValue.DISCONNECT) { |
|
return |
|
} |
|
getDeviceBySn(bindParam.workspace_id, device.data.sn).then(res => { |
|
if (res.code !== 0) { |
|
return |
|
} |
|
device.data.online_status = res.data.status |
|
device.data.bound_status = res.data.bound_status |
|
device.data.device_callsign = res.data.nickname |
|
device.data.model = res.data.device_name |
|
localStorage.setItem(ELocalStorageKey.Device, JSON.stringify(device.data)) |
|
}) |
|
} |
|
</script> |
|
|
|
<style lang="scss" scoped> |
|
@import '/@/styles/index.scss'; |
|
.page { |
|
display: flex; |
|
position: absolute; |
|
transition: width 0.2s ease; |
|
height: 100%; |
|
width: 100%; |
|
.left { |
|
height: 90%; |
|
background-color: white; |
|
margin-top: 6vh; |
|
margin-left: 2vh; |
|
} |
|
.right { |
|
height: 90%; |
|
margin-top: 6vh; |
|
margin-left: 5vh; |
|
margin-right: 5vh; |
|
} |
|
} |
|
.green { |
|
color: green |
|
} |
|
.red { |
|
color: red; |
|
} |
|
#exitBtn:hover :active { |
|
background-color: rgb(77, 75, 75); |
|
width: 10vw; |
|
height: 10vh; |
|
position: fixed; |
|
bottom: 13vh; |
|
left: 15vw; |
|
line-height: 10vh; |
|
} |
|
|
|
</style>
|
|
|