Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

video: Warn user if they selected an IP that is not available in the ICE candidates #923

Merged
Merged
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
12 changes: 7 additions & 5 deletions src/libs/blueos.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import ky from 'ky'

const defaultTimeout = 10000

/* eslint-disable @typescript-eslint/no-explicit-any */
export const getBagOfHoldingFromVehicle = async (
vehicleAddress: string,
bagName: string
): Promise<Record<string, any>> => {
try {
return await ky.get(`http://${vehicleAddress}/bag/v1.0/get/${bagName}`, { timeout: 5000 }).json()
return await ky.get(`http://${vehicleAddress}/bag/v1.0/get/${bagName}`, { timeout: defaultTimeout }).json()
} catch (error) {
throw new Error(`Could not get bag of holdings for ${bagName}. ${error}`)
}
Expand Down Expand Up @@ -34,7 +36,7 @@ export const setBagOfHoldingOnVehicle = async (
bagData: Record<string, any> | any
): Promise<void> => {
try {
await ky.post(`http://${vehicleAddress}/bag/v1.0/set/${bagName}`, { json: bagData, timeout: 5000 })
await ky.post(`http://${vehicleAddress}/bag/v1.0/set/${bagName}`, { json: bagData, timeout: defaultTimeout })
} catch (error) {
throw new Error(`Could not set bag of holdings for ${bagName}. ${error}`)
}
Expand Down Expand Up @@ -76,7 +78,7 @@ type IpInfo = { ipv4Address: string; interfaceType: string }
export const getIpsInformationFromVehicle = async (vehicleAddress: string): Promise<IpInfo[]> => {
try {
const url = `http://${vehicleAddress}/beacon/v1.0/services`
const rawIpsInfo: RawIpInfo[] = await ky.get(url, { timeout: 5000 }).json()
const rawIpsInfo: RawIpInfo[] = await ky.get(url, { timeout: defaultTimeout }).json()
return rawIpsInfo
.filter((ipInfo) => ipInfo['service_type'] === '_http')
.map((ipInfo) => ({ ipv4Address: ipInfo.ip, interfaceType: ipInfo.interface_type }))
Expand All @@ -93,7 +95,7 @@ type RawM2rInfo = { version: number; service: RawM2rServiceInfo }
export const getMavlink2RestVersion = async (vehicleAddress: string): Promise<string> => {
try {
const url = `http://${vehicleAddress}/mavlink2rest/info`
const m2rRawInfo: RawM2rInfo = await ky.get(url, { timeout: 5000 }).json()
const m2rRawInfo: RawM2rInfo = await ky.get(url, { timeout: defaultTimeout }).json()
return m2rRawInfo.service.version
} catch (error) {
throw new Error(`Could not get Mavlink2Rest version. ${error}`)
Expand All @@ -107,7 +109,7 @@ type RawArdupilotFirmwareInfo = { version: string; type: string }
export const getArdupilotVersion = async (vehicleAddress: string): Promise<string> => {
try {
const url = `http://${vehicleAddress}/ardupilot-manager/v1.0/firmware_info`
const ardupilotFirmwareRawInfo: RawArdupilotFirmwareInfo = await ky.get(url, { timeout: 5000 }).json()
const ardupilotFirmwareRawInfo: RawArdupilotFirmwareInfo = await ky.get(url, { timeout: defaultTimeout }).json()
return ardupilotFirmwareRawInfo.version
} catch (error) {
throw new Error(`Could not get Ardupilot firmware version. ${error}`)
Expand Down
102 changes: 67 additions & 35 deletions src/stores/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -586,62 +586,94 @@ export const useVideoStore = defineStore('video', () => {
}
}

const issueSelectedIpNotAvailableWarning = (): void => {
Swal.fire({
html: `
<p>Cockpit detected that you selected an IP on the video configuration page that is not available
on the video server. This will lead to no video being streamed. This can happen if you changed your
network or the IP of your vehicle.</p>
</br>
<p>To solve this problem, please:</p>
<ol>
<li>1. Open the video configuration page (Main-menu > Configuration > Video).</li>
<li>2. Clear the selected IPs and select an available one from the list.</li>
</ol>
`,
icon: 'warning',
customClass: {
htmlContainer: 'text-left',
},
})
}

const issueNoIpSelectedWarning = (): void => {
Swal.fire({
html: `
<p>Cockpit detected more than one IP address being used to route the video streaming. This often
leads to video stuttering, especially if one of the IPs is from a non-wired connection.</p>
</br>
<p>To prevent issues and achieve an optimal streaming experience, please:</p>
<ol>
<li>1. Open the video configuration page (Main-menu > Configuration > Video).</li>
<li>2. Select the IP address that should be used for the video streaming.</li>
</ol>
`,
icon: 'warning',
customClass: {
htmlContainer: 'text-left',
},
})
}

// Routine to make sure the user has chosen the allowed ICE candidate IPs, so the stream works as expected
let warningTimeout: NodeJS.Timeout | undefined = undefined
let noIpSelectedWarningIssued = false
let selectedIpNotAvailableWarningIssued = false
const iceIpCheckInterval = setInterval(async (): Promise<void> => {
// Pass if there are no available IPs yet
if (availableIceIps.value === undefined) return
if (availableIceIps.value.isEmpty()) return

// Cancel the check if the user has already set the allowed ICE IPs
if (!allowedIceIps.value.isEmpty()) {
// If the user has selected IPs, but none of them are available, warn about it, since no video will be streamed.
// Otherwise, if IPs are selected and available, clear the check routine.
const availableSelectedIps = availableIceIps.value.filter((ip) => allowedIceIps.value.includes(ip))
if (availableSelectedIps.isEmpty() && !selectedIpNotAvailableWarningIssued) {
console.warn('Selected ICE IPs are not available. Warning user.')
issueSelectedIpNotAvailableWarning()
selectedIpNotAvailableWarningIssued = true
}
clearInterval(iceIpCheckInterval)
clearTimeout(warningTimeout)
return
}

// If there's more than one IP candidate available, try getting information about them from BlueOS. If not
// available, send a warning an clear the check routine.
if (availableIceIps.value.length >= 1) {
// If the user has not selected any IPs and there's more than one IP candidate available, try getting information
// about them from BlueOS. If that fails, send a warning an clear the check routine.
if (allowedIceIps.value.isEmpty() && availableIceIps.value.length >= 1) {
// Try to select the IP automatically if it's a wired connection (based on BlueOS data).
try {
const ipsInfo = await getIpsInformationFromVehicle(globalAddress)
const newAllowedIps: string[] = []
ipsInfo.forEach((ipInfo) => {
const isIceIp = availableIceIps.value.includes(ipInfo.ipv4Address)
const alreadyAllowedIp = allowedIceIps.value.includes(ipInfo.ipv4Address)
const alreadyAllowedIp = [...allowedIceIps.value, ...newAllowedIps].includes(ipInfo.ipv4Address)
if (ipInfo.interfaceType !== 'WIRED' || alreadyAllowedIp || !isIceIp) return
console.info(`Adding the wired address '${ipInfo.ipv4Address}' to the list of allowed ICE IPs.`)
allowedIceIps.value.push(ipInfo.ipv4Address)
newAllowedIps.push(ipInfo.ipv4Address)
})
allowedIceIps.value = newAllowedIps
if (!allowedIceIps.value.isEmpty()) {
clearInterval(iceIpCheckInterval)
clearTimeout(warningTimeout)
return
Swal.fire({ text: 'Preferred video stream routes fetched from BlueOS.', icon: 'success', timer: 5000 })
}
} catch (error) {
console.log(error)
console.error('Failed to get IP information from the vehicle:', error)
}

if (warningTimeout) return
warningTimeout = setTimeout(() => {
// If the system was still not able to populate the allowed IPs list yet, warn the user.
// Otherwise, clear the check routine.
if (allowedIceIps.value.isEmpty() && !noIpSelectedWarningIssued) {
console.info('No ICE IPs selected for the allowed list. Warning user.')
Swal.fire({
html: `
<p>Cockpit detected more than one IP address being used to route the video streaming. This often
leads to video stuttering, especially if one of the IPs is from a non-wired connection.</p>
</br>
<p>To prevent issues and achieve an optimal streaming experience, please:</p>
<ol>
<li>1. Open the video configuration page (Main-menu > Configuration > Video).</li>
<li>2. Select the IP address that should be used for the video streaming.</li>
</ol>
`,
icon: 'warning',
customClass: {
htmlContainer: 'text-left',
},
})
clearInterval(iceIpCheckInterval)
return
}, 5000)
issueNoIpSelectedWarning()
noIpSelectedWarningIssued = true
}
clearInterval(iceIpCheckInterval)
}
}, 5000)

Expand Down
Loading