Skip to content

Commit

Permalink
frontend: add master endpoint management interface
Browse files Browse the repository at this point in the history
  • Loading branch information
Williangalvani committed Jan 18, 2025
1 parent 688d2e4 commit b40382d
Show file tree
Hide file tree
Showing 2 changed files with 233 additions and 1 deletion.
217 changes: 217 additions & 0 deletions core/frontend/src/components/autopilot/MasterEndpointManager.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
<template>
<div class="master-endpoint-manager d-flex flex-column align-center">
<v-card
width="100%"
class="pa-4"
>
<v-card-title class="text-h6 mb-2">
Master Endpoint Configuration
</v-card-title>

<v-form
ref="form"
v-model="form_valid"
lazy-validation
>
<v-select
v-model="endpoint.connection_type"
:items="endpoint_types"
label="Connection Type"
:rules="[validate_required_field]"
@change="updateDefaultPlace"
/>

<v-text-field
v-model="endpoint.place"
:rules="[validate_required_field, is_ip_address_path, is_useable_ip_address]"
label="IP/Device"
/>

<v-text-field
v-model.number="endpoint.argument"
label="Port/Baudrate"
:rules="[is_socket_port_baudrate]"
/>

<v-card-actions class="mt-4">
<v-spacer />
<v-btn
color="primary"
:loading="saving"
:disabled="!form_valid || !has_changes"
@click="saveEndpoint"
>
Save Changes
</v-btn>
</v-card-actions>
</v-form>
</v-card>

<v-snackbar
v-model="show_success"
color="success"
timeout="3000"
>
Master endpoint updated successfully
<template #action="{ attrs }">
<v-btn
text
v-bind="attrs"
@click="show_success = false"
>
Close
</v-btn>
</template>
</v-snackbar>
</div>
</template>

<script lang="ts">
import Vue from 'vue'
import Notifier from '@/libs/notifier'
import autopilot from '@/store/autopilot_manager'
import beacon from '@/store/beacon'
import { AutopilotEndpoint, EndpointType, userFriendlyEndpointType } from '@/types/autopilot'
import { autopilot_service } from '@/types/frontend_services'
import { VForm } from '@/types/vuetify'
import back_axios from '@/utils/api'
import {
isBaudrate, isFilepath, isIpAddress, isNotEmpty, isSocketPort,
} from '@/utils/pattern_validators'
const notifier = new Notifier(autopilot_service)
const defaultEndpoint: AutopilotEndpoint = {
name: 'master',
owner: 'User',
connection_type: EndpointType.udpin,
place: '0.0.0.0',
argument: 14550,
persistent: true,
protected: false,
enabled: true,
}
export default Vue.extend({
name: 'MasterEndpointManager',
data() {
return {
form_valid: true,
saving: false,
show_success: false,
original_endpoint: { ...defaultEndpoint },
endpoint: { ...defaultEndpoint },
}
},
computed: {
endpoint_types(): {value: EndpointType, text: string}[] {
return Object.entries(EndpointType).map(
(type) => ({ value: type[1], text: userFriendlyEndpointType(type[1]) }),
)
},
form(): VForm {
return this.$refs.form as VForm
},
user_ip_address(): string {
return beacon.client_ip_address
},
available_ips(): string[] {
return [...new Set(beacon.available_domains.map((domain) => domain.ip))]
},
has_changes(): boolean {
return this.endpoint.connection_type !== this.original_endpoint.connection_type
|| this.endpoint.place !== this.original_endpoint.place
|| this.endpoint.argument !== this.original_endpoint.argument
},
},
mounted() {
this.fetchCurrentEndpoint()
},
methods: {
validate_required_field(input: string): (true | string) {
return isNotEmpty(input) ? true : 'Required field.'
},
is_ip_address_path(input: string): (true | string) {
return isIpAddress(input) || isFilepath(input) ? true : 'Invalid IP/Device-path.'
},
is_useable_ip_address(input: string): (true | string) {
if ([EndpointType.udpin, EndpointType.tcpin].includes(this.endpoint.connection_type)) {
if (!['0.0.0.0', ...this.available_ips].includes(input)) {
return 'This IP is not available at any of the network interfaces.'
}
}
if ([EndpointType.udpout, EndpointType.tcpout].includes(this.endpoint.connection_type)) {
if (input === '0.0.0.0') return '0.0.0.0 as a client is undefined behavior.'
}
return true
},
is_socket_port_baudrate(input: number): (true | string) {
if (typeof input === 'string') {
return 'Please use an integer value.'
}
return isSocketPort(input) || isBaudrate(input) ? true : 'Invalid Port/Baudrate.'
},
updateDefaultPlace(): void {
switch (this.endpoint.connection_type) {
case EndpointType.udpin:
case EndpointType.tcpin:
this.endpoint.place = '0.0.0.0'
break
case EndpointType.udpout:
case EndpointType.tcpout:
this.endpoint.place = this.user_ip_address
break
default:
this.endpoint.place = '/dev/ttyAMA1' // Serial3
}
},
async fetchCurrentEndpoint(): Promise<void> {
try {
const response = await back_axios({
method: 'get',
url: `${autopilot.API_URL}/endpoints/unmanaged_board_master_endpoint`,
timeout: 10000,
})
const endpoint_data = {
...defaultEndpoint,
...response.data,
}
this.endpoint = { ...endpoint_data }
this.original_endpoint = { ...endpoint_data }
} catch (error) {
notifier.pushBackError('MASTER_ENDPOINT_FETCH_FAIL', error)
}
},
async saveEndpoint(): Promise<void> {
if (!this.form.validate()) {
return
}
this.saving = true
try {
await back_axios({
method: 'post',
url: `${autopilot.API_URL}/endpoints/unmanaged_board_master_endpoint`,
timeout: 10000,
data: this.endpoint,
})
this.original_endpoint = { ...this.endpoint }
this.show_success = true
} catch (error) {
notifier.pushBackError('MASTER_ENDPOINT_SAVE_FAIL', error)
} finally {
this.saving = false
}
},
},
})
</script>

<style scoped>
.master-endpoint-manager {
width: 100%;
max-width: 600px;
margin: 0 auto;
}
</style>
17 changes: 16 additions & 1 deletion core/frontend/src/views/Autopilot.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,17 @@
<br>
</div>
<not-safe-overlay />
<v-expansion-panels>
<v-expansion-panels v-if="is_external_board">
<v-expansion-panel>
<v-expansion-panel-header>
Master endpoint
</v-expansion-panel-header>
<v-expansion-panel-content>
<master-endpoint-manager />
</v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panels>
<v-expansion-panels v-else>
<v-expansion-panel>
<v-expansion-panel-header>
Firmware update
Expand Down Expand Up @@ -112,6 +122,7 @@ import {
import AutopilotSerialConfiguration from '@/components/autopilot/AutopilotSerialConfiguration.vue'
import BoardChangeDialog from '@/components/autopilot/BoardChangeDialog.vue'
import FirmwareManager from '@/components/autopilot/FirmwareManager.vue'
import MasterEndpointManager from '@/components/autopilot/MasterEndpointManager.vue'
import NotSafeOverlay from '@/components/common/NotSafeOverlay.vue'
import { MavAutopilot } from '@/libs/MAVLink2Rest/mavlink2rest-ts/messages/mavlink2rest-enum'
import Notifier from '@/libs/notifier'
Expand All @@ -132,6 +143,7 @@ export default Vue.extend({
FirmwareManager,
AutopilotSerialConfiguration,
NotSafeOverlay,
MasterEndpointManager,
},
data() {
return {
Expand Down Expand Up @@ -184,6 +196,9 @@ export default Vue.extend({
}
return ['Navigator', 'Navigator64', 'SITL'].includes(boardname)
},
is_external_board(): boolean {
return autopilot.current_board?.name === 'Unmanaged'
},
current_board(): FlightController | null {
return autopilot.current_board
},
Expand Down

0 comments on commit b40382d

Please sign in to comment.