diff --git a/electron/preload.ts b/electron/preload.ts
index bfdd3fe8c..4312004c1 100644
--- a/electron/preload.ts
+++ b/electron/preload.ts
@@ -1,5 +1,5 @@
import { contextBridge, ipcRenderer } from 'electron'
contextBridge.exposeInMainWorld('electronAPI', {
- getNetworkInfo: () => ipcRenderer.invoke('get-network-info'),
+ getInfoOnSubnets: () => ipcRenderer.invoke('get-info-on-subnets'),
})
diff --git a/electron/services/network.ts b/electron/services/network.ts
index 991b0f60f..4f56dbbf4 100644
--- a/electron/services/network.ts
+++ b/electron/services/network.ts
@@ -1,39 +1,45 @@
import { ipcMain } from 'electron'
import { networkInterfaces } from 'os'
-/**
- * Information about the network
- */
-interface NetworkInfo {
- /**
- * The subnet of the local machine
- */
- subnet: string
-}
-
+import { NetworkInfo } from '../../src/types/network'
/**
* Get the network information
* @returns {NetworkInfo} The network information
*/
-const getNetworkInfo = (): NetworkInfo => {
- const nets = networkInterfaces()
+const getInfoOnSubnets = (): NetworkInfo[] => {
+ const allSubnets = networkInterfaces()
- for (const name of Object.keys(nets)) {
- for (const net of nets[name] ?? []) {
- // Skip over non-IPv4 and internal addresses
- if (net.family === 'IPv4' && !net.internal) {
- // Return the subnet (e.g., if IP is 192.168.1.5, return 192.168.1)
- return { subnet: net.address.split('.').slice(0, 3).join('.') }
- }
- }
+ const ipv4Subnets = Object.entries(allSubnets)
+ .flatMap(([_, nets]) => {
+ return nets.map((net) => ({ ...net, interfaceName: _ }))
+ })
+ .filter((net) => net.family === 'IPv4')
+ .filter((net) => !net.internal)
+
+ if (ipv4Subnets.length === 0) {
+ throw new Error('No network interfaces found.')
}
- throw new Error('No network interface found.')
+ return ipv4Subnets.map((subnet) => {
+ // TODO: Use the mask to calculate the available addresses. The current implementation is not correct for anything else than /24.
+ const subnetPrefix = subnet.address.split('.').slice(0, 3).join('.')
+ const availableAddresses: string[] = []
+ for (let i = 1; i <= 254; i++) {
+ availableAddresses.push(`${subnetPrefix}.${i}`)
+ }
+
+ return {
+ topSideAddress: subnet.address,
+ macAddress: subnet.mac,
+ interfaceName: subnet.interfaceName,
+ availableAddresses,
+ }
+ })
}
/**
* Setup the network service
*/
export const setupNetworkService = (): void => {
- ipcMain.handle('get-network-info', getNetworkInfo)
+ ipcMain.handle('get-info-on-subnets', getInfoOnSubnets)
}
diff --git a/src/components/VehicleDiscoveryDialog.vue b/src/components/VehicleDiscoveryDialog.vue
index cd09bc93e..456f5fb99 100644
--- a/src/components/VehicleDiscoveryDialog.vue
+++ b/src/components/VehicleDiscoveryDialog.vue
@@ -30,7 +30,9 @@
It looks like you're not connected to a vehicle!
-
This tool allows you to locate and connect to vehicles within your network.
+
+ This tool allows you to locate and connect to BlueOS vehicles within your network.
+
@@ -49,6 +51,7 @@ import { ref, watch } from 'vue'
import { useSnackbar } from '@/composables/snackbar'
import vehicleDiscover, { NetworkVehicle } from '@/libs/electron/vehicle-discovery'
+import { reloadCockpit } from '@/libs/utils'
import { useMainVehicleStore } from '@/stores/mainVehicle'
import InteractionDialog, { Action } from './InteractionDialog.vue'
@@ -116,9 +119,10 @@ const searchVehicles = async (): Promise => {
searched.value = true
}
-const selectVehicle = (address: string): void => {
+const selectVehicle = async (address: string): Promise => {
mainVehicleStore.globalAddress = address
isOpen.value = false
+ await reloadCockpit()
showSnackbar({ message: 'Vehicle address updated', variant: 'success', duration: 5000 })
}
diff --git a/src/libs/cosmos.ts b/src/libs/cosmos.ts
index 5463d6d2b..41cc98050 100644
--- a/src/libs/cosmos.ts
+++ b/src/libs/cosmos.ts
@@ -1,5 +1,7 @@
import { isBrowser } from 'browser-or-node'
+import { NetworkInfo } from '@/types/network'
+
import {
cockpitActionVariableData,
createCockpitActionVariable,
@@ -110,7 +112,7 @@ declare global {
* Get network information from the main process
* @returns Promise containing subnet information
*/
- getNetworkInfo: () => Promise<{ subnet: string }>
+ getInfoOnSubnets: () => Promise
}
}
/* eslint-enable jsdoc/require-jsdoc */
diff --git a/src/libs/electron/vehicle-discovery.ts b/src/libs/electron/vehicle-discovery.ts
index 0399eea26..bc7f0f3f4 100644
--- a/src/libs/electron/vehicle-discovery.ts
+++ b/src/libs/electron/vehicle-discovery.ts
@@ -1,4 +1,7 @@
-import { getStatus } from '../blueos'
+import ky from 'ky'
+
+import { NetworkInfo } from '@/types/network'
+
import { isElectron } from '../utils'
/**
@@ -41,47 +44,20 @@ class VehicleDiscover {
private async checkAddress(address: string): Promise {
try {
// First check if the vehicle is online
- const hasRespondingStatusEndpoint = await getStatus(address)
- if (!hasRespondingStatusEndpoint) {
- return null
- }
+ const statusResponse = await ky.get(`http://${address}/status`, { timeout: 3000 })
+ if (!statusResponse.ok) return null
// Try to get the vehicle name
- try {
- const response = await fetch(`http://${address}/beacon/v1.0/vehicle_name`)
- if (!response.ok) {
- return null
- }
- const name = await response.text()
- return { address, name }
- } catch {
- // If we can't get the name, it's because it's not a vehicle (or maybe BlueOS's Beacon service is not running)
- return null
- }
+ const nameResponse = await ky.get(`http://${address}/beacon/v1.0/vehicle_name`, { timeout: 5000 })
+ if (!nameResponse.ok) return null
+ const name = await nameResponse.text()
+ return { address, name }
} catch {
- // If we can't get the status, it's because the vehicle is not online
+ // If we can't get the name, it's because it's not a vehicle (or maybe BlueOS's Beacon service is not running)
return null
}
}
- /**
- * Get the local subnet
- * @returns {string | null} The local subnet, or null if not running in Electron
- */
- private async getLocalSubnet(): Promise {
- if (!isElectron() || !window.electronAPI?.getNetworkInfo) {
- const msg = 'For technical reasons, getting information about the local subnet is only available in Electron.'
- throw new Error(msg)
- }
-
- try {
- const { subnet } = await window.electronAPI.getNetworkInfo()
- return subnet
- } catch (error) {
- throw new Error(`Failed to get information about the local subnet. ${error}`)
- }
- }
-
/**
* Find vehicles on the local network
* @returns {NetworkVehicle[]} The vehicles found
@@ -96,23 +72,37 @@ class VehicleDiscover {
}
const search = async (): Promise => {
- const subnet = await this.getLocalSubnet()
-
- if (!subnet) {
- throw new Error('Failed to get information about the local subnet.')
+ if (!isElectron() || !window.electronAPI?.getInfoOnSubnets) {
+ const msg = 'For technical reasons, getting information about the local subnet is only available in Electron.'
+ throw new Error(msg)
}
- const promises: Promise[] = []
+ let localSubnets: NetworkInfo[] | undefined
+ try {
+ localSubnets = await window.electronAPI.getInfoOnSubnets()
+ } catch (error) {
+ throw new Error(`Failed to get information about the local subnets. ${error}`)
+ }
- // Check all IPs in the subnet
- for (let i = 1; i <= 254; i++) {
- const address = `${subnet}.${i}`
- promises.push(this.checkAddress(address))
+ if (localSubnets.length === 0) {
+ throw new Error('Failed to get information about the local subnets.')
}
- const vehiclesFound = await Promise.all(promises).then((results) => {
- return results.filter((result): result is NetworkVehicle => result !== null)
- })
+ const vehiclesFound: NetworkVehicle[] = []
+ for (const subnet of localSubnets) {
+ const topSideAddress = subnet.topSideAddress
+ const possibleAddresses = subnet.availableAddresses.filter((address) => address !== topSideAddress)
+
+ const promises: Promise[] = possibleAddresses.map((address) => {
+ return this.checkAddress(address)
+ })
+
+ const vehicles = await Promise.all(promises).then((results) => {
+ return results.filter((result): result is NetworkVehicle => result !== null)
+ })
+
+ vehiclesFound.push(...vehicles)
+ }
this.currentSearch = undefined
diff --git a/src/types/network.ts b/src/types/network.ts
new file mode 100644
index 000000000..590716ccb
--- /dev/null
+++ b/src/types/network.ts
@@ -0,0 +1,21 @@
+/**
+ * Information about the network
+ */
+export interface NetworkInfo {
+ /**
+ * The top side address of the local machine
+ */
+ topSideAddress: string
+ /**
+ * The MAC address of the local machine
+ */
+ macAddress: string
+ /**
+ * The name of the network interface
+ */
+ interfaceName: string
+ /**
+ * The CIDR of the local machine
+ */
+ availableAddresses: string[]
+}