Skip to content

Commit 03ca4b9

Browse files
committed
components: Add joystick wizard
Signed-off-by: Arturo Manzoli <[email protected]>
1 parent 428332b commit 03ca4b9

File tree

7 files changed

+1455
-2
lines changed

7 files changed

+1455
-2
lines changed

src/App.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@
143143
>
144144
<SplashScreen v-if="interfaceStore.showSplashScreen" />
145145
</Transition>
146+
<JoystickWizard v-if="interfaceStore.isJoystickWizardVisible" />
146147
</template>
147148

148149
<script setup lang="ts">
@@ -151,6 +152,7 @@ import { computed, onBeforeMount, onBeforeUnmount, onMounted, ref, watch } from
151152
152153
import ActionDiscoveryModal from '@/components/ActionDiscoveryModal.vue'
153154
import GlassModal from '@/components/GlassModal.vue'
155+
import JoystickWizard from '@/components/joysticks/JoystickWizard.vue'
154156
import SkullAnimation from '@/components/SkullAnimation.vue'
155157
import SnackbarContainer from '@/components/SnackbarContainer.vue'
156158
import Tutorial from '@/components/Tutorial.vue'

src/components/joysticks/JoystickWizard.vue

Lines changed: 1260 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { v4 as uuid4 } from 'uuid'
2+
3+
import {
4+
JoystickAxisActionCorrespondency,
5+
JoystickButtonActionCorrespondency,
6+
JoystickProtocol,
7+
JoystickProtocolActionsMapping,
8+
} from '@/types/joystick'
9+
10+
/**
11+
* Standard axis profile using Blue Robotics recommended default mappings for ROV controllers.
12+
*/
13+
export const axisConfigMapROV: JoystickAxisActionCorrespondency = {
14+
3: {
15+
action: { id: 'axis_z', name: 'Axis Z', protocol: JoystickProtocol.MAVLinkManualControl },
16+
min: 1000,
17+
max: 0,
18+
},
19+
4: {
20+
action: { id: 'axis_y', name: 'Axis Y', protocol: JoystickProtocol.MAVLinkManualControl },
21+
min: -1000,
22+
max: 1000,
23+
},
24+
5: {
25+
action: { id: 'axis_x', name: 'Axis X', protocol: JoystickProtocol.MAVLinkManualControl },
26+
min: 1000,
27+
max: -1000,
28+
},
29+
6: {
30+
action: { id: 'axis_r', name: 'Axis R', protocol: JoystickProtocol.MAVLinkManualControl },
31+
min: -1000,
32+
max: 1000,
33+
},
34+
}
35+
36+
/**
37+
* Standard button profile using Blue Robotics recommended default mappings for ROV controllers.
38+
*/
39+
export const buttonConfigMapROV: JoystickButtonActionCorrespondency = {
40+
7: { action: { id: 'no_function', name: 'Shift (modifier)', protocol: JoystickProtocol.Other } },
41+
9: { action: { id: 'servo_1_max_momentary', name: 'Gripper Open', protocol: JoystickProtocol.MAVLinkManualControl } },
42+
10: {
43+
action: { id: 'servo_1_min_momentary', name: 'Gripper Close', protocol: JoystickProtocol.MAVLinkManualControl },
44+
},
45+
12: { action: { id: 'camera-zoom-increase', name: 'Camera Zoom In', protocol: JoystickProtocol.CockpitAction } },
46+
13: { action: { id: 'camera-zoom-decrease', name: 'Camera Zoom Out', protocol: JoystickProtocol.CockpitAction } },
47+
14: { action: { id: 'camera-focus-increase', name: 'Camera Focus Near', protocol: JoystickProtocol.CockpitAction } },
48+
15: { action: { id: 'camera-focus-decrease', name: 'Camera Focus Far', protocol: JoystickProtocol.CockpitAction } },
49+
16: { action: { id: 'btn_auto_focus', name: 'Auto Focus', protocol: JoystickProtocol.CockpitAction } },
50+
17: { action: { id: 'btn_auto_wb', name: 'Auto White Balance', protocol: JoystickProtocol.CockpitAction } },
51+
18: { action: { id: 'gain_inc', name: 'Pilot Gain +', protocol: JoystickProtocol.MAVLinkManualControl } },
52+
19: { action: { id: 'gain_dec', name: 'Pilot Gain –', protocol: JoystickProtocol.MAVLinkManualControl } },
53+
20: { action: { id: 'Arm', name: 'Arm', protocol: JoystickProtocol.MAVLinkManualControl } },
54+
21: { action: { id: 'Disarm', name: 'Disarm', protocol: JoystickProtocol.MAVLinkManualControl } },
55+
22: { action: { id: 'mount_tilt_up', name: 'Camera Tilt Up', protocol: JoystickProtocol.MAVLinkManualControl } },
56+
23: { action: { id: 'mount_tilt_down', name: 'Camera Tilt Down', protocol: JoystickProtocol.MAVLinkManualControl } },
57+
24: { action: { id: 'mount_center', name: 'Camera Tilt Center', protocol: JoystickProtocol.MAVLinkManualControl } },
58+
25: { action: { id: 'lights1_brighter', name: 'Lights Brighter', protocol: JoystickProtocol.MAVLinkManualControl } },
59+
26: { action: { id: 'lights1_dimmer', name: 'Lights Dimmer', protocol: JoystickProtocol.MAVLinkManualControl } },
60+
27: { action: { id: 'trim_pitch_inc', name: 'Trim Pitch Forward', protocol: JoystickProtocol.MAVLinkManualControl } },
61+
28: {
62+
action: { id: 'trim_pitch_dec', name: 'Trim Pitch Backward', protocol: JoystickProtocol.MAVLinkManualControl },
63+
},
64+
29: { action: { id: 'trim_roll_inc', name: 'Trim Roll Right', protocol: JoystickProtocol.MAVLinkManualControl } },
65+
30: { action: { id: 'trim_roll_dec', name: 'Trim Roll Left', protocol: JoystickProtocol.MAVLinkManualControl } },
66+
31: { action: { id: 'mode_manual', name: 'Manual Mode', protocol: JoystickProtocol.MAVLinkManualControl } },
67+
32: { action: { id: 'mode_depth_hold', name: 'Depth-hold Mode', protocol: JoystickProtocol.MAVLinkManualControl } },
68+
33: { action: { id: 'mode_stabilize', name: 'Stabilize Mode', protocol: JoystickProtocol.MAVLinkManualControl } },
69+
34: { action: { id: 'input_hold_set', name: 'Toggle Input Hold', protocol: JoystickProtocol.MAVLinkManualControl } },
70+
35: {
71+
action: { id: 'roll_pitch_toggle', name: 'Roll & Pitch Toggle', protocol: JoystickProtocol.MAVLinkManualControl },
72+
},
73+
}
74+
75+
/**
76+
* Basic blank mapping to be used as a starting point in the joystick configuration wizard.
77+
*/
78+
export const blankWizardMapping: JoystickProtocolActionsMapping = {
79+
name: 'Wizard Functions mapping',
80+
hash: uuid4(),
81+
axesCorrespondencies: {} as JoystickProtocolActionsMapping['axesCorrespondencies'],
82+
buttonsCorrespondencies: {
83+
regular: {},
84+
shift: {},
85+
},
86+
}

src/stores/appInterface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export const useAppInterfaceStore = defineStore('responsive', {
5959
currentSubMenuComponentName: ref<SubMenuComponentName | null>(null),
6060
isGlassModalAlwaysOnTop: false,
6161
isTutorialVisible: false,
62+
isJoystickWizardVisible: false,
6263
userHasSeenTutorial: useBlueOsStorage('cockpit-has-seen-tutorial', false),
6364
configPanelVisible: false,
6465
showSplashScreen: true,

src/stores/controller.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,18 @@ export const useControllerStore = defineStore('controller', () => {
438438
reader.readAsText(e.target.files[0])
439439
}
440440

441+
const mergeFunctionsMapping = (mapping: JoystickProtocolActionsMapping): void => {
442+
const existingIndex = protocolMappings.value.findIndex((m) => m.hash === mapping.hash || m.name === mapping.name)
443+
444+
if (existingIndex !== -1) {
445+
protocolMappings.value[existingIndex] = mapping
446+
protocolMappingIndex.value = existingIndex
447+
} else {
448+
protocolMappings.value.push(mapping)
449+
protocolMappingIndex.value = protocolMappings.value.length - 1
450+
}
451+
}
452+
441453
// Add hash on mappings that don't have it - TODO: Remove for 1.0.0 release
442454
Object.values(protocolMappings.value).forEach((mapping) => {
443455
if (mapping.hash !== undefined) return
@@ -563,5 +575,6 @@ export const useControllerStore = defineStore('controller', () => {
563575
currentMainJoystick,
564576
disabledJoysticks,
565577
checkForOtherManualControlSources,
578+
mergeFunctionsMapping,
566579
}
567580
})

src/types/joystick.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,3 +530,67 @@ export const convertSDLJoystickStateToGamepadState = (sdlState: SDLJoystickState
530530
axes: sdlState.axes,
531531
}
532532
}
533+
534+
/**
535+
* Interface representing a single step in the joystick wizard
536+
*/
537+
export interface JoystickWizardStep {
538+
/**
539+
* Unique identifier for the step
540+
*/
541+
id: number
542+
/**
543+
* Title of the step
544+
*/
545+
title: string
546+
/**
547+
* Description of the step
548+
*/
549+
content?: string
550+
/**
551+
* Lower description of the step
552+
*/
553+
opposite?: string
554+
/**
555+
* If true, the content will be larger than usual
556+
*/
557+
largeContent?: boolean
558+
/**
559+
* If true, the step detects an axis movement
560+
*/
561+
inputType?: 'axis' | 'button'
562+
/**
563+
* If true, the step has selection options
564+
*/
565+
hasSelection?: boolean
566+
/**
567+
* Options for the selection step
568+
*/
569+
selectionOptions?: {
570+
/**
571+
* Label for the selection option
572+
*/
573+
label: string
574+
/**
575+
* Action to perform when the option is selected
576+
*/
577+
action: () => void
578+
}[]
579+
/**
580+
* If true, the next button will be disabled
581+
*/
582+
disableNextButton?: boolean
583+
/**
584+
* If true, the step is for reviewing mappings
585+
*/
586+
reviewMappings?: boolean
587+
/**
588+
* If the step is disabled
589+
*/
590+
disabled?: boolean
591+
/**
592+
* OnStepReached callback
593+
* @description Callback function to be called when the step is reached
594+
*/
595+
onStepReached?: (step: JoystickWizardStep) => void
596+
}

src/views/ConfigurationJoystickView.vue

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
</div>
4545
</template>
4646
<template #content>
47-
<div class="flex flex-col items-center h-[280px] overflow-auto">
47+
<div class="flex flex-col items-center h-[225px] overflow-hidden">
4848
<div class="flex flex-col items-center">
4949
<div
5050
v-if="
@@ -92,6 +92,7 @@
9292
>
9393
<v-btn
9494
class="text-md bg-[#FFFFFF23]"
95+
size="small"
9596
:class="{
9697
'bg-[#FFFFFF43]': selectedProfile.name === functionMapping.name,
9798
'text-sm': interfaceStore.isOnSmallScreen,
@@ -106,7 +107,7 @@
106107
<!-- Regular profile button -->
107108
<div v-else class="relative mt-2">
108109
<v-btn
109-
class="text-md bg-[#FFFFFF23] px-6"
110+
class="text-[13px] bg-[#FFFFFF23] px-2"
110111
:class="{
111112
'bg-[#FFFFFF43]': selectedProfile.name === functionMapping.name,
112113
'text-sm': interfaceStore.isOnSmallScreen,
@@ -215,6 +216,17 @@
215216
class="-mt-2"
216217
@update:model-value="toggleJoystickEnabling(joystick.model)"
217218
/>
219+
<div v-if="showWizardButton" class="absolute right-0 mb-2">
220+
<v-btn
221+
variant="elevated"
222+
size="small"
223+
class="bg-[#FFFFFF22] text-white pt-[2px] pl-4"
224+
prepend-icon="mdi-gamepad-up"
225+
@click="interfaceStore.isJoystickWizardVisible = true"
226+
>
227+
Joystick config wizard
228+
</v-btn>
229+
</div>
218230
</div>
219231
<div
220232
v-if="showJoystickLayout"
@@ -268,6 +280,17 @@
268280
class="-mt-2 -mb-1"
269281
@update:model-value="toggleJoystickEnabling(joystick.model)"
270282
/>
283+
<div v-if="showWizardButton" class="absolute right-0 mb-2">
284+
<v-btn
285+
variant="elevated"
286+
size="small"
287+
class="bg-[#FFFFFF22] text-white pt-[2px] pl-4"
288+
prepend-icon="mdi-gamepad-up"
289+
@click="interfaceStore.isJoystickWizardVisible = true"
290+
>
291+
Joystick config wizard
292+
</v-btn>
293+
</div>
271294
</div>
272295
<p class="text-start text-sm font-bold w-[93%] mb-1">Axes</p>
273296
<v-data-table
@@ -763,6 +786,10 @@ const throttledButtonStates = ref<Record<number, number | undefined>>({})
763786
const lastButtonUpdateTime = ref(0)
764787
const buttonUpdateThrottleMs = 30
765788
789+
const showWizardButton = computed(() => {
790+
return vehicleType === 'MAV_TYPE_SUBMARINE'
791+
})
792+
766793
// Optimized shallow watcher instead of deep watcher
767794
let buttonUpdateScheduled = false
768795
watch(

0 commit comments

Comments
 (0)