Skip to content

Commit

Permalink
wip widget adding/api
Browse files Browse the repository at this point in the history
  • Loading branch information
Williangalvani committed Dec 10, 2024
1 parent 5ec445d commit a8ab6c7
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 15 deletions.
55 changes: 48 additions & 7 deletions src/components/EditMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -484,8 +484,8 @@
class="flex items-center justify-between w-full h-full gap-3 overflow-x-auto text-white -mb-1 pr-2 cursor-pointer"
>
<div
v-for="widgetType in availableWidgetTypes"
:key="widgetType"
v-for="widgetType in extendedWidgetTypes"
:key="widgetType.name"
class="flex flex-col items-center justify-between rounded-md bg-[#273842] hover:brightness-125 h-[90%] aspect-square cursor-pointer elevation-4"
draggable="true"
@dragstart="onRegularWidgetDragStart"
Expand All @@ -496,15 +496,15 @@
<div />
<img
v-bind="tooltipProps"
:src="widgetImages[widgetType]"
:src="widgetImages[widgetType.component]"
alt="widget-icon"
class="p-4 max-h-[75%] max-w-[95%]"
/>
<div
class="flex items-center justify-center w-full p-1 transition-all bg-[#3B78A8] rounded-b-md text-white"
>
<span class="whitespace-normal text-center">{{
widgetType.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/^./, (str) => str.toUpperCase()) ||
widgetType.name.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/^./, (str) => str.toUpperCase()) ||
'Very Generic Indicator'
}}</span>
</div>
Expand Down Expand Up @@ -612,11 +612,20 @@ import URLVideoPlayerImg from '@/assets/widgets/URLVideoPlayer.png'
import VideoPlayerImg from '@/assets/widgets/VideoPlayer.png'
import VirtualHorizonImg from '@/assets/widgets/VirtualHorizon.png'
import { useInteractionDialog } from '@/composables/interactionDialog'
import { getWidgetsFromBlueOS } from '@/libs/blueos'
import { MavType } from '@/libs/connection/m2r/messages/mavlink2rest-enum'
import { isHorizontalScroll } from '@/libs/utils'
import { useAppInterfaceStore } from '@/stores/appInterface'
import { useWidgetManagerStore } from '@/stores/widgetManager'
import { type Profile, type View, type Widget, MiniWidgetType, WidgetType } from '@/types/widgets'
import {
type Profile,
type View,
type Widget,
BlueOsWidget,
ExtendedWidget,
MiniWidgetType,
WidgetType,
} from '@/types/widgets'
import ExpansiblePanel from './ExpansiblePanel.vue'
import GlassModal from './GlassModal.vue'
Expand All @@ -640,6 +649,8 @@ const toggleDial = (): void => {
const forceUpdate = ref(0)
const blueosWidgets = ref<BlueOsWidget[]>([])
watch(
() => store.currentView.widgets,
() => {
Expand All @@ -659,7 +670,29 @@ const emit = defineEmits<{
(e: 'update:editMode', editMode: boolean): void
}>()
const availableWidgetTypes = computed(() => Object.values(WidgetType))
const availableWidgetTypes = computed(() =>
Object.values(WidgetType).map((widgetType) => {
return {
component: widgetType,
name: widgetType,
options: {},
}
})
)
const extendedWidgetTypes = computed(() => {
return [
...availableWidgetTypes.value,
...blueosWidgets.value.map((widget) => ({
component: WidgetType.IFrame,
name: widget.name,
options: {
source: widget.url,
},
})),
]
})
const availableMiniWidgetTypes = computed(() =>
Object.values(MiniWidgetType).map((widgetType) => ({
component: widgetType,
Expand Down Expand Up @@ -860,7 +893,15 @@ const miniWidgetsContainerOptions = ref<UseDraggableOptions>({
})
useDraggable(availableMiniWidgetsContainer, availableMiniWidgetTypes, miniWidgetsContainerOptions)
/**
*
*/
async function getBlueosWidgets(): Promise<void> {
blueosWidgets.value = await getWidgetsFromBlueOS()
}
onMounted(() => {
getBlueosWidgets()
const widgetContainers = [availableWidgetsContainer.value, availableMiniWidgetsContainer.value]
widgetContainers.forEach((container) => {
container.addEventListener(
Expand Down Expand Up @@ -908,7 +949,7 @@ const onRegularWidgetDragStart = (event: DragEvent): void => {
}
}
const onRegularWidgetDragEnd = (widgetType: WidgetType): void => {
const onRegularWidgetDragEnd = (widgetType: ExtendedWidget): void => {
store.addWidget(widgetType, store.currentView)
const widgetCards = document.querySelectorAll('[draggable="true"]')
Expand Down
25 changes: 23 additions & 2 deletions src/components/widgets/IFrame.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<teleport to=".widgets-view">
<iframe
v-show="iframe_loaded"
ref="iframe"
:src="widget.options.source"
:style="iframeStyle"
frameborder="0"
Expand Down Expand Up @@ -60,12 +61,14 @@ import { defaultBlueOsAddress } from '@/assets/defaults'
import Snackbar from '@/components/Snackbar.vue'
import { isValidURL } from '@/libs/utils'
import { useAppInterfaceStore } from '@/stores/appInterface'
import { useMainVehicleStore } from '@/stores/mainVehicle'
import { useWidgetManagerStore } from '@/stores/widgetManager'
import type { Widget } from '@/types/widgets'
const interfaceStore = useAppInterfaceStore()
const widgetStore = useWidgetManagerStore()
const iframe = ref()
const props = defineProps<{
/**
* Widget reference
Expand Down Expand Up @@ -97,10 +100,28 @@ const updateURL = (): void => {
}
onBeforeMount(() => {
console.log('1')
window.addEventListener(
'message',
(event) => {
console.log(event)
},
false
)
setInterval(() => {
const text = `SENDING ${useMainVehicleStore().attitude.yaw}`
console.log(text)
iframe.value.contentWindow.postMessage(
{
source: 'parent',
text: text,
},
'*'
)
}, 1000)
if (Object.keys(widget.value.options).length !== 0) {
return
}
widget.value.options = {
source: defaultBlueOsAddress,
}
Expand Down
2 changes: 1 addition & 1 deletion src/composables/settingsSyncer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ setTimeout(() => {
resetJustMade.value = false
}, 10000)

const getVehicleAddress = async (): Promise<string> => {
export const getVehicleAddress = async (): Promise<string> => {
const vehicleStore = useMainVehicleStore()

// Wait until we have a global address
Expand Down
43 changes: 42 additions & 1 deletion src/libs/blueos.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import ky, { HTTPError } from 'ky'

import { useMainVehicleStore } from '@/stores/mainVehicle'
import { BlueOsWidget } from '@/types/widgets'

export const NoPathInBlueOsErrorName = 'NoPathInBlueOS'

const defaultTimeout = 10000
Expand All @@ -13,7 +16,7 @@ export const getBagOfHoldingFromVehicle = async (
const options = { timeout: defaultTimeout, retry: 0 }
return await ky.get(`http://${vehicleAddress}/bag/v1.0/get/${bagPath}`, options).json()
} catch (error) {
const errorBody = await (error as HTTPError).response.json()
console.error(error)
if (errorBody.detail === 'Invalid path') {
const noPathError = new Error(`No data available in BlueOS storage for path '${bagPath}'.`)
noPathError.name = NoPathInBlueOsErrorName
Expand All @@ -30,6 +33,44 @@ export const getKeyDataFromCockpitVehicleStorage = async (
return await getBagOfHoldingFromVehicle(vehicleAddress, `cockpit/${storageKey}`)
}

export const getWidgetsFromBlueOS = async (): Promise<BlueOsWidget[]> => {
const vehicleStore = useMainVehicleStore()

// Wait until we have a global address
while (vehicleStore.globalAddress === undefined) {
console.debug('Waiting for vehicle global address on BlueOS sync routine.')
await new Promise((r) => setTimeout(r, 1000))
}
try {
const options = { timeout: defaultTimeout, retry: 0 }
const data = (await ky
.get(`http://${vehicleStore.globalAddress}/helper/v1.0/web_services`, options)
.json()) as Record<string, any>
console.log(data)
// Extract the 'metadata.cockpit_widget' keys from the leaf nodes
let widgets: BlueOsWidget[] = []
for (const key of Object.keys(data)) {
const value = data[key]
if (typeof value === 'object') {
if (value.metadata && value.metadata.cockpit_widget) {
const newWidgets: BlueOsWidget[] = value.metadata.cockpit_widget.map((widget: BlueOsWidget) => {
return {
...widget,
url: `http://${vehicleStore.globalAddress}:${value.port}${widget.url}`,
}
})
widgets = [...widgets, ...newWidgets]
}
}
}
return widgets
} catch (error) {
const errorBody = await (error as HTTPError).response.json()
console.error(errorBody)
}
return []
}

export const setBagOfHoldingOnVehicle = async (
vehicleAddress: string,
bagName: string,
Expand Down
9 changes: 5 additions & 4 deletions src/stores/widgetManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
type Profile,
type View,
type Widget,
ExtendedWidget,
MiniWidgetManagerVars,
validateProfile,
validateView,
Expand Down Expand Up @@ -410,16 +411,16 @@ export const useWidgetManagerStore = defineStore('widget-manager', () => {
* @param { WidgetType } widgetType - Type of the widget
* @param { View } view - View
*/
function addWidget(widgetType: WidgetType, view: View): void {
function addWidget(widgetType: ExtendedWidget, view: View): void {
const widgetHash = uuid4()

const widget = {
hash: widgetHash,
name: widgetType,
component: widgetType,
name: widgetType.name,
component: widgetType.component,
position: { x: 0.4, y: 0.32 },
size: { width: 0.2, height: 0.36 },
options: {},
options: widgetType.options,
}

view.widgets.unshift(widget)
Expand Down
32 changes: 32 additions & 0 deletions src/types/widgets.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,37 @@
import type { Point2D, SizeRect2D } from './general'

/**
*
*/
export interface BlueOsWidget {
/**
*
*/
name: string
/**
*
*/
url: string
}

/**
*
*/
export interface ExtendedWidget {
/**
*
*/
component: WidgetType
/**
*
*/
name: string
/**
*
*/
options: Record<string, unknown>
}

/**
* Available components to be used in the Widget system
* The enum value is equal to the component's filename, without the '.vue' extension
Expand Down

0 comments on commit a8ab6c7

Please sign in to comment.