Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaellehmkuhl committed Dec 7, 2023
1 parent a9811bd commit 728446e
Show file tree
Hide file tree
Showing 12 changed files with 1,076 additions and 537 deletions.
16 changes: 13 additions & 3 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,11 @@ import { useRoute } from 'vue-router'
import ConfigurationMenu from '@/components/ConfigurationMenu.vue'
import { coolMissionNames } from '@/libs/funny-name/words'
import { CockpitAction, registerActionCallback, unregisterActionCallback } from '@/libs/joystick/protocols'
import {
availableCockpitActions,
registerActionCallback,
unregisterActionCallback,
} from '@/libs/joystick/protocols/cockpit-actions'
import { useMissionStore } from '@/stores/mission'
import Dialog from './components/Dialog.vue'
Expand All @@ -175,7 +179,10 @@ const routerSection = ref()
const { isFullscreen, toggle: toggleFullscreen } = useFullscreen()
const debouncedToggleFullScreen = useDebounceFn(() => toggleFullscreen(), 10)
const fullScreenCallbackId = registerActionCallback(CockpitAction.TOGGLE_FULL_SCREEN, debouncedToggleFullScreen)
const fullScreenCallbackId = registerActionCallback(
availableCockpitActions.toggle_full_screen,
debouncedToggleFullScreen
)
onBeforeUnmount(() => unregisterActionCallback(fullScreenCallbackId))
const fullScreenToggleIcon = computed(() => (isFullscreen.value ? 'mdi-fullscreen-exit' : 'mdi-overscan'))
Expand Down Expand Up @@ -209,7 +216,10 @@ watch([() => widgetStore.currentView, () => widgetStore.currentView.showBottomBa
showBottomBarNow.value = widgetStore.currentView.showBottomBarOnBoot
})
const debouncedToggleBottomBar = useDebounceFn(() => (showBottomBarNow.value = !showBottomBarNow.value), 25)
const bottomBarToggleCallbackId = registerActionCallback(CockpitAction.TOGGLE_BOTTOM_BAR, debouncedToggleBottomBar)
const bottomBarToggleCallbackId = registerActionCallback(
availableCockpitActions.toggle_bottom_bar,
debouncedToggleBottomBar
)
onBeforeUnmount(() => unregisterActionCallback(bottomBarToggleCallbackId))
// Start datalogging
Expand Down
95 changes: 63 additions & 32 deletions src/assets/joystick-profiles.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,69 @@
import { JoystickModel } from '@/libs/joystick/manager'
import { CockpitAction, MAVLinkAxis } from '@/libs/joystick/protocols'
import { type GamepadToCockpitStdMapping, type ProtocolControllerMapping, JoystickProtocol } from '@/types/joystick'
import { availableCockpitActions } from '@/libs/joystick/protocols/cockpit-actions'
import {
availableMavlinkManualControlButtonFunctions,
mavlinkManualControlAxes,
} from '@/libs/joystick/protocols/mavlink-manual-control'
import { modifierKeyActions, otherAvailableActions } from '@/libs/joystick/protocols/other'
import {
type GamepadToCockpitStdMapping,
type JoystickProtocolActionsMapping,
CockpitModifierKeyOption,
JoystickAxis,
JoystickButton,
} from '@/types/joystick'

// TODO: Adjust mapping for PS5 controller
export const cockpitStandardToProtocols: ProtocolControllerMapping = {
name: 'Cockpit Standard Gamepad to Protocols',
axesCorrespondencies: [
{ protocol: JoystickProtocol.MAVLinkManualControl, value: MAVLinkAxis.Y },
{ protocol: JoystickProtocol.MAVLinkManualControl, value: MAVLinkAxis.X },
{ protocol: JoystickProtocol.MAVLinkManualControl, value: MAVLinkAxis.R },
{ protocol: JoystickProtocol.MAVLinkManualControl, value: MAVLinkAxis.Z },
],
axesMins: [-1000, 1000, -1000, 1000],
axesMaxs: [1000, -1000, 1000, 0],
buttonsCorrespondencies: [
{ protocol: JoystickProtocol.MAVLinkManualControl, value: 0 },
{ protocol: JoystickProtocol.MAVLinkManualControl, value: 1 },
{ protocol: JoystickProtocol.MAVLinkManualControl, value: 2 },
{ protocol: JoystickProtocol.MAVLinkManualControl, value: 3 },
{ protocol: JoystickProtocol.CockpitAction, value: CockpitAction.GO_TO_PREVIOUS_VIEW },
{ protocol: JoystickProtocol.CockpitAction, value: CockpitAction.GO_TO_NEXT_VIEW },
{ protocol: JoystickProtocol.MAVLinkManualControl, value: 9 },
{ protocol: JoystickProtocol.MAVLinkManualControl, value: 10 },
{ protocol: JoystickProtocol.MAVLinkManualControl, value: 4 },
{ protocol: JoystickProtocol.MAVLinkManualControl, value: 6 },
{ protocol: JoystickProtocol.MAVLinkManualControl, value: 7 },
{ protocol: JoystickProtocol.MAVLinkManualControl, value: 8 },
{ protocol: JoystickProtocol.MAVLinkManualControl, value: 11 },
{ protocol: JoystickProtocol.MAVLinkManualControl, value: 12 },
{ protocol: JoystickProtocol.MAVLinkManualControl, value: 13 },
{ protocol: JoystickProtocol.MAVLinkManualControl, value: 14 },
{ protocol: JoystickProtocol.MAVLinkManualControl, value: 5 },
{ protocol: JoystickProtocol.CockpitAction, value: CockpitAction.TOGGLE_FULL_SCREEN },
],
export const cockpitStandardToProtocols: JoystickProtocolActionsMapping = {
name: 'Standard for ArduSub',
axesCorrespondencies: {
[JoystickAxis.A0]: { action: mavlinkManualControlAxes.axis_y, min: -1000, max: +1000 },
[JoystickAxis.A1]: { action: mavlinkManualControlAxes.axis_x, min: +1000, max: -1000 },
[JoystickAxis.A2]: { action: mavlinkManualControlAxes.axis_r, min: -1000, max: +1000 },
[JoystickAxis.A3]: { action: mavlinkManualControlAxes.axis_z, min: +1000, max: 0 },
},
buttonsCorrespondencies: {
[CockpitModifierKeyOption.regular]: {
[JoystickButton.B0]: { action: availableMavlinkManualControlButtonFunctions['Arm'] },
[JoystickButton.B1]: { action: otherAvailableActions.no_function },
[JoystickButton.B2]: { action: availableMavlinkManualControlButtonFunctions['Mount tilt up'] },
[JoystickButton.B3]: { action: availableMavlinkManualControlButtonFunctions['Mount tilt down'] },
[JoystickButton.B4]: { action: availableCockpitActions.go_to_previous_view },
[JoystickButton.B5]: { action: otherAvailableActions.no_function },
[JoystickButton.B6]: { action: availableMavlinkManualControlButtonFunctions['Gain inc'] },
[JoystickButton.B7]: { action: availableMavlinkManualControlButtonFunctions['Gain dec'] },
[JoystickButton.B8]: { action: availableMavlinkManualControlButtonFunctions['Lights1 brighter'] },
[JoystickButton.B9]: { action: availableMavlinkManualControlButtonFunctions['Lights1 dimmer'] },
[JoystickButton.B10]: { action: availableCockpitActions.toggle_full_screen },
[JoystickButton.B11]: { action: otherAvailableActions.no_function },
[JoystickButton.B12]: { action: otherAvailableActions.no_function },
[JoystickButton.B13]: { action: modifierKeyActions.shift },
[JoystickButton.B14]: { action: otherAvailableActions.no_function },
[JoystickButton.B15]: { action: otherAvailableActions.no_function },
[JoystickButton.B16]: { action: otherAvailableActions.no_function },
[JoystickButton.B17]: { action: otherAvailableActions.no_function },
},
[CockpitModifierKeyOption.shift]: {
[JoystickButton.B0]: { action: otherAvailableActions.no_function },
[JoystickButton.B1]: { action: otherAvailableActions.no_function },
[JoystickButton.B2]: { action: otherAvailableActions.no_function },
[JoystickButton.B3]: { action: otherAvailableActions.no_function },
[JoystickButton.B4]: { action: otherAvailableActions.no_function },
[JoystickButton.B5]: { action: otherAvailableActions.no_function },
[JoystickButton.B6]: { action: otherAvailableActions.no_function },
[JoystickButton.B7]: { action: otherAvailableActions.no_function },
[JoystickButton.B8]: { action: otherAvailableActions.no_function },
[JoystickButton.B9]: { action: otherAvailableActions.no_function },
[JoystickButton.B10]: { action: otherAvailableActions.no_function },
[JoystickButton.B11]: { action: otherAvailableActions.no_function },
[JoystickButton.B12]: { action: otherAvailableActions.no_function },
[JoystickButton.B13]: { action: modifierKeyActions.shift },
[JoystickButton.B14]: { action: otherAvailableActions.no_function },
[JoystickButton.B15]: { action: otherAvailableActions.no_function },
[JoystickButton.B16]: { action: otherAvailableActions.no_function },
[JoystickButton.B17]: { action: otherAvailableActions.no_function },
},
},
}

/**
Expand Down
205 changes: 18 additions & 187 deletions src/libs/joystick/protocols.ts
Original file line number Diff line number Diff line change
@@ -1,189 +1,20 @@
import { v4 as uuid4 } from 'uuid'
import { type ProtocolAction } from '@/types/joystick'

import { round, scale } from '@/libs/utils'
import { sequentialArray } from '@/libs/utils'
import { availableCockpitActions } from './protocols/cockpit-actions'
import {
type JoystickState,
type ProtocolControllerMapping,
type ProtocolInput,
JoystickProtocol,
ProtocolControllerState,
} from '@/types/joystick'

/**
* Correspondency between protocol input and it's pretty name (usually the actual function it triggers)
*/
export interface InputWithPrettyName {
/**
* Input which triggers the function
*/
input: ProtocolInput
/**
* Name of the parameter option
*/
prettyName: string
}

/**
* Current state of the controller in the MavLink protocol
*/
export class MavlinkControllerState extends ProtocolControllerState {
x: number
y: number
z: number
r: number
buttons: number
target: number
public static readonly BUTTONS_PER_BITFIELD = 16

/**
*
* @param { JoystickState } joystickState - Cockpit standard mapped values for the joystick
* @param { ProtocolControllerMapping } mapping - Gamepad API to Protocols joystick mapping, where assignments and limits are got from.
* @param { number } target - Specify targeted vehicle ID.
*/
constructor(joystickState: JoystickState, mapping: ProtocolControllerMapping, target = 1) {
super()

const isMavlinkInput = (input: ProtocolInput): boolean => input.protocol === JoystickProtocol.MAVLinkManualControl

let buttons_int = 0
for (let i = 0; i < MavlinkControllerState.BUTTONS_PER_BITFIELD; i++) {
let buttonState = 0
mapping.buttonsCorrespondencies.forEach((b, idx) => {
if (isMavlinkInput(b) && b.value === i && joystickState.buttons[idx]) {
buttonState = 1
}
})
buttons_int += buttonState * 2 ** i
}

const xIndex = mapping.axesCorrespondencies.findIndex((v) => isMavlinkInput(v) && v.value === MAVLinkAxis.X)
const yIndex = mapping.axesCorrespondencies.findIndex((v) => isMavlinkInput(v) && v.value === MAVLinkAxis.Y)
const zIndex = mapping.axesCorrespondencies.findIndex((v) => isMavlinkInput(v) && v.value === MAVLinkAxis.Z)
const rIndex = mapping.axesCorrespondencies.findIndex((v) => isMavlinkInput(v) && v.value === MAVLinkAxis.R)

const absLimits = mavlinkAxesLimits

const xLimits = [mapping.axesMins[xIndex] ?? absLimits[0], mapping.axesMaxs[xIndex] ?? absLimits[1]]
const yLimits = [mapping.axesMins[yIndex] ?? absLimits[0], mapping.axesMaxs[yIndex] ?? absLimits[1]]
const zLimits = [mapping.axesMins[zIndex] ?? absLimits[0], mapping.axesMaxs[zIndex] ?? absLimits[1]]
const rLimits = [mapping.axesMins[rIndex] ?? absLimits[0], mapping.axesMaxs[rIndex] ?? absLimits[1]]

this.x = xIndex === undefined ? 0 : round(scale(joystickState.axes[xIndex] ?? 0, -1, 1, xLimits[0], xLimits[1]), 0)
this.y = yIndex === undefined ? 0 : round(scale(joystickState.axes[yIndex] ?? 0, -1, 1, yLimits[0], yLimits[1]), 0)
this.z = zIndex === undefined ? 0 : round(scale(joystickState.axes[zIndex] ?? 0, -1, 1, zLimits[0], zLimits[1]), 0)
this.r = rIndex === undefined ? 0 : round(scale(joystickState.axes[rIndex] ?? 0, -1, 1, rLimits[0], rLimits[1]), 0)

this.buttons = buttons_int
this.target = round(target, 0)
}
}

/**
* Possible other protocol functions
*/
export enum OtherProtocol {
NO_FUNCTION = 'No function',
}

/**
* Possible Cockpit Actions
*/
export enum CockpitAction {
GO_TO_NEXT_VIEW = 'Go to next view',
GO_TO_PREVIOUS_VIEW = 'Go to previous view',
TOGGLE_FULL_SCREEN = 'Toggle full-screen',
MAVLINK_ARM = 'Mavlink Command - Arm',
MAVLINK_DISARM = 'Mavlink Command - Disarm',
TOGGLE_BOTTOM_BAR = 'Toggle bottom bar',
}

export type CockpitActionCallback = () => void

/**
* Callback entry
*/
interface CallbackEntry {
/**
* Unique ID for that callback register
*/
action: CockpitAction
/**
* Callback to be called
*/
callback: CockpitActionCallback
}

// @ts-ignore: Typescript does not get that we are initializing the object dinamically
const actionsCallbacks: { [id in string]: CallbackEntry } = {}

export const registerActionCallback = (action: CockpitAction, callback: CockpitActionCallback): string => {
const id = uuid4()
actionsCallbacks[id] = { action, callback }
return id
}
export const unregisterActionCallback = (id: string): void => {
delete actionsCallbacks[id]
}

export const sendCockpitActions = (joystickState: JoystickState, mapping: ProtocolControllerMapping): void => {
const actionsToCallback: CockpitAction[] = []
joystickState.buttons.forEach((state, idx) => {
const mappedButton = mapping.buttonsCorrespondencies[idx]
if (state && mappedButton.protocol === JoystickProtocol.CockpitAction) {
actionsToCallback.push(mappedButton.value as CockpitAction)
}
})
Object.values(actionsCallbacks).forEach((entry) => {
if (actionsToCallback.includes(entry.action)) {
entry.callback()
}
})
}

/**
* Possible axes in the MAVLink protocol
*/
export enum MAVLinkAxis {
X = 'x',
Y = 'y',
Z = 'z',
R = 'r',
}
const mavlinkAvailableAxes = Object.values(MAVLinkAxis)
export const mavlinkAvailableButtons = sequentialArray(16)

const mavlinkAxesLimits = [-1000, 1000]
export const protocolAxesLimits = (protocol: JoystickProtocol): number[] => {
switch (protocol) {
case JoystickProtocol.MAVLinkManualControl:
return mavlinkAxesLimits
default:
// Mavlink is the current main protocol and will be used by default
return mavlinkAxesLimits
}
}

export const allAvailableAxes: InputWithPrettyName[] = []
mavlinkAvailableAxes.forEach((axis) =>
allAvailableAxes.push({ input: { protocol: JoystickProtocol.MAVLinkManualControl, value: axis }, prettyName: axis })
)

Object.values(OtherProtocol).forEach((fn) =>
allAvailableAxes.push({ input: { protocol: JoystickProtocol.Other, value: fn }, prettyName: fn })
)

export const allAvailableButtons: InputWithPrettyName[] = []
mavlinkAvailableButtons.forEach((btn) =>
allAvailableButtons.push({
input: { protocol: JoystickProtocol.MAVLinkManualControl, value: btn },
prettyName: btn.toString(),
})
)
Object.values(CockpitAction).forEach((action) =>
allAvailableButtons.push({ input: { protocol: JoystickProtocol.CockpitAction, value: action }, prettyName: action })
)
Object.values(OtherProtocol).forEach((fn) =>
allAvailableButtons.push({ input: { protocol: JoystickProtocol.Other, value: fn }, prettyName: fn })
)
availableMavlinkManualControlButtonFunctions,
mavlinkManualControlAxes,
} from './protocols/mavlink-manual-control'
import { modifierKeyActions, otherAvailableActions } from './protocols/other'

export const allAvailableAxes: ProtocolAction[] = [
...Object.values(mavlinkManualControlAxes),
...Object.values(otherAvailableActions),
]

export const allAvailableButtons: ProtocolAction[] = [
...Object.values(availableCockpitActions),
...Object.values(availableMavlinkManualControlButtonFunctions),
...Object.values(otherAvailableActions),
...Object.values(modifierKeyActions),
]
Loading

0 comments on commit 728446e

Please sign in to comment.