Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 8 additions & 11 deletions core/frontend/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@
<draggable v-model="selected_widgets" class="d-flex align-center justify-center">
<component
:is="getWidget(widget_name).component"
v-for="(widget_name, i) in selected_widgets"
:key="i"
v-for="widget_name in selected_widgets"
:key="widget_name"
v-bind="getWidget(widget_name).props"
class="mr-2"
ripple
Expand Down Expand Up @@ -483,21 +483,18 @@ export default Vue.extend({
props: {},
},
]
// lets filter out docker, veth, and zerotier interfaces
if (!system_information.system?.network) {
return widgets
}
const extra_interfaces = system_information.system?.network?.filter(
(iface) => !['docker', 'lo', 'veth'].some((prefix) => iface.name.startsWith(prefix)),
// Use the stable name list so speed polls do not rebuild this computed every 2s
const extra_interfaces = system_information.network_interface_names.filter(
(name) => !['docker', 'lo', 'veth'].some((prefix) => name.startsWith(prefix)),
)
for (const iface of extra_interfaces) {
for (const name of extra_interfaces) {
widgets.push({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
component: Networking as any,
props: {
interface: iface.name,
interface: name,
},
name: `${iface.name} Networking`,
name: `${name} Networking`,
})
}
return widgets
Expand Down
8 changes: 7 additions & 1 deletion core/frontend/src/components/common/DevicePathHelper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@ import navigator_image from '@/assets/img/devicePathHelper/navigator.svg'
import raspberry_pi3_image from '@/assets/img/devicePathHelper/rpi3b.svg'
import raspberry_pi4_image from '@/assets/img/devicePathHelper/rpi4b.svg'
import raspberry_pi5_image from '@/assets/img/devicePathHelper/rpi5.svg'
import system_information from '@/store/system-information'
import system_information, { FetchType } from '@/store/system-information'
import { Dictionary } from '@/types/common'

const FETCH_TYPES = [FetchType.PlatformType]

enum BoardType {
Rpi4B = 'Rpi4B',
Rpi3B = 'Rpi3B',
Expand Down Expand Up @@ -180,6 +182,7 @@ export default Vue.extend({
},
},
mounted() {
system_information.subscribeSystemInformation(FETCH_TYPES)
// Wait for svg element to be loaded to set object
let id = 0
const name = `.${this.svgName}${this.inline ? '-inline' : ''}`
Expand All @@ -199,6 +202,9 @@ export default Vue.extend({
}
}, 500)
},
beforeDestroy() {
system_information.unsubscribeSystemInformation(FETCH_TYPES)
},
methods: {
updateImgObjectFromElement(element: HTMLEmbedElement) {
this.imgObject = element?.getSVGDocument()
Expand Down
6 changes: 5 additions & 1 deletion core/frontend/src/components/health/HealthTrayMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,11 @@ import { RaspberryEventType } from '@/types/system-information/platform'
import { Disk } from '@/types/system-information/system'
import mavlink_store_get from '@/utils/mavlink'

const FETCH_TYPES = [FetchType.SystemTemperatureType, FetchType.SystemDiskType]
const FETCH_TYPES = [
FetchType.SystemTemperatureType,
FetchType.SystemDiskType,
FetchType.PlatformType,
]

export default Vue.extend({
name: 'HealthTrayMenu',
Expand Down
18 changes: 10 additions & 8 deletions core/frontend/src/components/system-information/Network.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,28 @@
import Vue from 'vue'

import NetworkCard from '@/components/system-information/NetworkCard.vue'
import system_information from '@/store/system-information'
import system_information, { FetchType } from '@/store/system-information'
import { Network } from '@/types/system-information/system'

const FETCH_TYPES = [FetchType.SystemNetworkType]

export default Vue.extend({
name: 'Network',
components: {
NetworkCard,
},
data() {
return {
timer: 0,
}
},
computed: {
networks(): Network[] {
return system_information.system?.network.sort((first, second) => first.name.localeCompare(second.name)) ?? []
// Copy before sort — do not mutate the Vuex network array in place.
const networks = system_information.system?.network ?? []
return [...networks].sort((first, second) => first.name.localeCompare(second.name))
},
},
mounted() {
system_information.subscribeSystemInformation(FETCH_TYPES)
},
beforeDestroy() {
clearInterval(this.timer)
system_information.unsubscribeSystemInformation(FETCH_TYPES)
},
})
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ import { fetchCurrentBoard, fetchFirmwareInfo } from '@/components/autopilot/Aut
import { OneMoreTime } from '@/one-more-time'
import autopilot_data from '@/store/autopilot'
import autopilot from '@/store/autopilot_manager'
import system_information from '@/store/system-information'
import system_information, { FetchType } from '@/store/system-information'

const FETCH_TYPES = [FetchType.PlatformType]

export default Vue.extend({
name: 'VehicleInfo',
Expand Down Expand Up @@ -64,8 +66,12 @@ export default Vue.extend({
},
},
mounted() {
system_information.subscribeSystemInformation(FETCH_TYPES)
this.fetch_firmware_info_task.setAction(fetchCurrentBoard)
this.fetch_current_board_task.setAction(fetchFirmwareInfo)
},
beforeDestroy() {
system_information.unsubscribeSystemInformation(FETCH_TYPES)
},
})
</script>
77 changes: 54 additions & 23 deletions core/frontend/src/store/system-information.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,36 @@ const system_information_subscribers: Partial<Record<FetchType, number>> = {
[FetchType.SystemMemoryType]: 0,
[FetchType.SystemDiskType]: 0,
[FetchType.SystemTemperatureType]: 0,
[FetchType.SystemNetworkType]: 0,
[FetchType.PlatformType]: 0,
}

function subscribedSystemFetchTypes(): FetchType[] {
return (Object.keys(system_information_subscribers) as FetchType[])
.filter((type) => type !== FetchType.PlatformType && (system_information_subscribers[type] ?? 0) > 0)
}

function hasSystemSubscribers(): boolean {
return subscribedSystemFetchTypes().length > 0
}

/** Return a new name list only when the set of interface names changed (keeps App tray stable). */
function nextNetworkInterfaceNames(previous: string[], networks: Network[] | undefined): string[] | undefined {
const names = networks?.map(({ name }) => name) ?? []
const same_set = previous.length === names.length
&& previous.every((name) => names.includes(name))
&& names.every((name) => previous.includes(name))
return same_set ? undefined : names
}

function resumeOrStart(task: OneMoreTime): void {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const state = task as any
if (state.isPaused) {
task.resume()
} else if (!state.isRunning && !state.timeoutId) {
task.start()
}
}

@Module({
Expand All @@ -67,12 +97,11 @@ class SystemInformationStore extends VuexModule {

serial: Serial | null = null

fetchPlatformTask = new OneMoreTime(
{ delay: 5000 },
)
// Stable interface name list for App tray widgets — updated only when the set of names changes.
network_interface_names: string[] = []

fetchSystemNetworkTask = new OneMoreTime(
{ delay: 2000 },
fetchPlatformTask = new OneMoreTime(
{ delay: 5000, autostart: false },
)

fetchSubscribedSystemInformationTask = new OneMoreTime(
Expand Down Expand Up @@ -113,6 +142,10 @@ class SystemInformationStore extends VuexModule {
@Mutation
updateSystem(system: System): void {
this.system = system
const names = nextNetworkInterfaceNames(this.network_interface_names, system.network)
if (names) {
this.network_interface_names = names
}
}

@Mutation
Expand Down Expand Up @@ -158,6 +191,10 @@ class SystemInformationStore extends VuexModule {
}
}
this.system.network = networks
const names = nextNetworkInterfaceNames(this.network_interface_names, networks)
if (names) {
this.network_interface_names = names
}
}
}

Expand Down Expand Up @@ -208,16 +245,9 @@ class SystemInformationStore extends VuexModule {
await this.fetchSystemInformation(FetchType.SystemType)
}

@Action
async fetchNetworkInformation(): Promise<void> {
await this.fetchSystemInformation(FetchType.SystemNetworkType)
}

@Action
async fetchSubscribedSystemInformation(): Promise<void> {
const fetches = (Object.keys(system_information_subscribers) as FetchType[])
.filter((type) => (system_information_subscribers[type] ?? 0) > 0)
.map((type) => this.fetchSystemInformation(type))
const fetches = subscribedSystemFetchTypes().map((type) => this.fetchSystemInformation(type))
if (fetches.length === 0) {
return
}
Expand All @@ -232,12 +262,12 @@ class SystemInformationStore extends VuexModule {
}
system_information_subscribers[type]! += 1
})
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const task = this.fetchSubscribedSystemInformationTask as any
if (task.isPaused) {
this.fetchSubscribedSystemInformationTask.resume()
} else if (!task.isRunning && !task.timeoutId) {
this.fetchSubscribedSystemInformationTask.start()

if (hasSystemSubscribers()) {
resumeOrStart(this.fetchSubscribedSystemInformationTask)
}
if ((system_information_subscribers[FetchType.PlatformType] ?? 0) > 0) {
resumeOrStart(this.fetchPlatformTask)
}
}

Expand All @@ -249,11 +279,13 @@ class SystemInformationStore extends VuexModule {
}
system_information_subscribers[type] = Math.max(0, system_information_subscribers[type]! - 1)
})
const has_subscribers = (Object.keys(system_information_subscribers) as FetchType[])
.some((type) => (system_information_subscribers[type] ?? 0) > 0)
if (!has_subscribers) {

if (!hasSystemSubscribers()) {
this.fetchSubscribedSystemInformationTask.stop()
}
if ((system_information_subscribers[FetchType.PlatformType] ?? 0) === 0) {
this.fetchPlatformTask.stop()
}
}

@Action
Expand Down Expand Up @@ -343,7 +375,6 @@ const system_information: SystemInformationStore = getModule(SystemInformationSt

system_information.fetchSystem()
system_information.fetchPlatformTask.setAction(system_information.fetchPlatform)
system_information.fetchSystemNetworkTask.setAction(system_information.fetchNetworkInformation)
system_information.fetchSubscribedSystemInformationTask.setAction(system_information.fetchSubscribedSystemInformation)

// Vuex store modules are a poor fit for WebSocket callbacks; keep sockets outside the store and
Expand Down
8 changes: 5 additions & 3 deletions core/frontend/src/widgets/Networking.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@
import axios from 'axios'
import Vue from 'vue'

import system_information from '@/store/system-information'
import system_information, { FetchType } from '@/store/system-information'
import { formatBandwidth } from '@/utils/networking'

const FETCH_TYPES = [FetchType.SystemNetworkType]

export default Vue.extend({
name: 'ETh0Widget',
props: {
Expand All @@ -42,7 +44,6 @@ export default Vue.extend({
},
data() {
return {
timer: 0,
image: '',
arrowLeft: '',
arrowRight: '',
Expand All @@ -59,10 +60,11 @@ export default Vue.extend({
},
},
async mounted() {
system_information.subscribeSystemInformation(FETCH_TYPES)
this.loadImages()
},
beforeDestroy() {
clearInterval(this.timer)
system_information.unsubscribeSystemInformation(FETCH_TYPES)
},
methods: {
formatBandwidth(bytesPerSecond: number): string {
Expand Down
Loading