From d49672d95d2ac32910a40d604a39369fd3adb880 Mon Sep 17 00:00:00 2001
From: Rafael Araujo Lehmkuhl
Date: Thu, 9 Nov 2023 17:22:48 -0300
Subject: [PATCH] working without vehicle remapping
---
src/App.vue | 16 +-
src/assets/joystick-profiles.ts | 72 ++--
src/components/joysticks/JoystickPS.vue | 25 +-
src/libs/joystick/protocols.ts | 193 +--------
.../joystick/protocols/cockpit-actions.ts | 86 ++++
.../protocols/mavlink-manual-control.ts | 395 ++++++++++++++++++
src/libs/vehicle/ardupilot/ardupilot.ts | 2 +-
src/stores/controller.ts | 17 +-
src/stores/mainVehicle.ts | 42 +-
src/stores/widgetManager.ts | 16 +-
src/types/joystick.ts | 90 +++-
src/views/ConfigurationJoystickView.vue | 196 ++-------
12 files changed, 723 insertions(+), 427 deletions(-)
create mode 100644 src/libs/joystick/protocols/cockpit-actions.ts
create mode 100644 src/libs/joystick/protocols/mavlink-manual-control.ts
diff --git a/src/App.vue b/src/App.vue
index 0011e48f0..33631e134 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -135,7 +135,11 @@ import {
import { useRoute } from 'vue-router'
import ConfigurationMenu from '@/components/ConfigurationMenu.vue'
-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'
@@ -161,7 +165,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'))
@@ -194,7 +201,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))
diff --git a/src/assets/joystick-profiles.ts b/src/assets/joystick-profiles.ts
index 8747fb1db..70a7af1cd 100644
--- a/src/assets/joystick-profiles.ts
+++ b/src/assets/joystick-profiles.ts
@@ -1,38 +1,46 @@
import { JoystickModel } from '@/libs/joystick/manager'
-import { CockpitAction, MAVLinkAxis } from '@/libs/joystick/protocols'
-import { type GamepadToCockpitStdMapping, type ProtocolControllerMapping, JoystickProtocol } from '@/types/joystick'
+import { otherAvailableActions } from '@/libs/joystick/protocols'
+import { availableCockpitActions } from '@/libs/joystick/protocols/cockpit-actions'
+import {
+ mavlinkManualControlAxes,
+ mavlinkManualControlButtonFunctions,
+} from '@/libs/joystick/protocols/mavlink-manual-control'
+import {
+ type GamepadToCockpitStdMapping,
+ type JoystickProtocolActionsMapping,
+ 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: {
+ [JoystickButton.B0]: { action: mavlinkManualControlButtonFunctions['Arm'] },
+ [JoystickButton.B1]: { action: mavlinkManualControlButtonFunctions['Disarm'] },
+ [JoystickButton.B2]: { action: mavlinkManualControlButtonFunctions['Mount tilt up'] },
+ [JoystickButton.B3]: { action: mavlinkManualControlButtonFunctions['Mount tilt down'] },
+ [JoystickButton.B4]: { action: availableCockpitActions.go_to_previous_view },
+ [JoystickButton.B5]: { action: availableCockpitActions.go_to_next_view },
+ [JoystickButton.B6]: { action: mavlinkManualControlButtonFunctions['Gain inc'] },
+ [JoystickButton.B7]: { action: mavlinkManualControlButtonFunctions['Gain dec'] },
+ [JoystickButton.B8]: { action: mavlinkManualControlButtonFunctions['Lights1 brighter'] },
+ [JoystickButton.B9]: { action: mavlinkManualControlButtonFunctions['Lights1 dimmer'] },
+ [JoystickButton.B10]: { action: availableCockpitActions.toggle_full_screen },
+ [JoystickButton.B11]: { action: otherAvailableActions.no_function },
+ [JoystickButton.B12]: { action: otherAvailableActions.no_function },
+ [JoystickButton.B13]: { action: otherAvailableActions.no_function },
+ [JoystickButton.B14]: { action: otherAvailableActions.no_function },
+ [JoystickButton.B15]: { action: otherAvailableActions.no_function },
+ [JoystickButton.B16]: { action: otherAvailableActions.no_function },
+ [JoystickButton.B17]: { action: otherAvailableActions.no_function },
+ },
}
/**
diff --git a/src/components/joysticks/JoystickPS.vue b/src/components/joysticks/JoystickPS.vue
index 6f7b80a03..2f51bd0d3 100644
--- a/src/components/joysticks/JoystickPS.vue
+++ b/src/components/joysticks/JoystickPS.vue
@@ -6,9 +6,13 @@
import { v4 as uuid4 } from 'uuid'
import { computed, onBeforeUnmount, ref, toRefs, watch } from 'vue'
-import type { InputWithPrettyName } from '@/libs/joystick/protocols'
import { scale } from '@/libs/utils'
-import { type JoystickInput, type ProtocolControllerMapping, JoystickAxis, JoystickButton } from '@/types/joystick'
+import {
+ type JoystickButtonActionCorrespondency,
+ type JoystickInput,
+ JoystickAxis,
+ JoystickButton,
+} from '@/types/joystick'
import { InputType } from '@/types/joystick'
const textColor = '#747474'
@@ -74,8 +78,7 @@ const props = defineProps<{
leftAxisVert?: number // State of the vertical left axis as a floating point number, between -1 and +1
rightAxisHoriz?: number // State of the horizontal right axis as a floating point number, between -1 and +1
rightAxisVert?: number // State of the vertical right axis as a floating point number, between -1 and +1
- protocolMapping: ProtocolControllerMapping // Mapping from the Cockpit standard to the protocol functions
- buttonLabelCorrespondency: InputWithPrettyName[] // Mapping from the protocol functions to human readable names
+ buttonsActionsCorrespondency: JoystickButtonActionCorrespondency // Mapping from the Cockpit standard to the protocol functions
}>()
const emit = defineEmits<{
@@ -86,10 +89,10 @@ const emit = defineEmits<{
const findInputFromPath = (path: string): JoystickInput[] => {
const inputs: JoystickInput[] = []
Object.entries(buttonPath).filter(([, v]) => v === path).forEach((button) => {
- inputs.push({ type: InputType.Button, value: button[0] as unknown as JoystickButton })
+ inputs.push({ type: InputType.Button, id: button[0] as unknown as JoystickButton })
})
Object.entries(axisPath).filter(([, v]) => v === path).forEach((axis) => {
- inputs.push({ type: InputType.Axis, value: axis[0] as unknown as JoystickAxis })
+ inputs.push({ type: InputType.Axis, id: axis[0] as unknown as JoystickAxis })
})
return inputs
}
@@ -171,16 +174,14 @@ watch(
() => updateButtonsState()
)
-const buttonLabelCorrespondency = toRefs(props).buttonLabelCorrespondency
-const protocolMapping = toRefs(props).protocolMapping
-watch([protocolMapping, buttonLabelCorrespondency], () => updateLabelsState())
+const buttonsActionsCorrespondency = toRefs(props).buttonsActionsCorrespondency
+watch(buttonsActionsCorrespondency, () => updateLabelsState())
const updateLabelsState = (): void => {
Object.values(JoystickButton).forEach((button) => {
if (isNaN(Number(button))) return
- const protocolButton = props.protocolMapping.buttonsCorrespondencies[button as JoystickButton] || undefined
- const param = props.buttonLabelCorrespondency.find((btn) => btn.input.protocol === protocolButton.protocol && btn.input.value === protocolButton.value)
- const functionName = param === undefined ? `${protocolButton.value} (${protocolButton.protocol})` : param.prettyName
+ const buttonActionCorrespondency = buttonsActionsCorrespondency.value[button as JoystickButton] || undefined
+ const functionName = buttonActionCorrespondency === undefined ? 'unassigned' : buttonActionCorrespondency.action.name
if (!svg) return
// @ts-ignore: we already check if button is a number and so if button is a valid index
const labelId = buttonPath[button].replace('path', 'text')
diff --git a/src/libs/joystick/protocols.ts b/src/libs/joystick/protocols.ts
index 896f2e2ba..074f31500 100644
--- a/src/libs/joystick/protocols.ts
+++ b/src/libs/joystick/protocols.ts
@@ -1,189 +1,34 @@
-import { v4 as uuid4 } from 'uuid'
+import { type ProtocolAction, JoystickProtocol } from '@/types/joystick'
-import { round, scale } from '@/libs/utils'
-import { sequentialArray } from '@/libs/utils'
-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
-}
+import { availableCockpitActions } from './protocols/cockpit-actions'
+import { mavlinkManualControlAxes, mavlinkManualControlButtonFunctions } from './protocols/mavlink-manual-control'
/**
* 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',
+ 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 const otherAvailableActions: { [key in OtherProtocol]: ProtocolAction } = {
+ [OtherProtocol.no_function]: {
+ protocol: JoystickProtocol.Other,
+ id: OtherProtocol.no_function,
+ name: 'No function',
+ },
}
-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 allAvailableAxes: ProtocolAction[] = [
+ ...Object.values(mavlinkManualControlAxes),
+ ...Object.values(OtherProtocol).map((fn) => ({ protocol: JoystickProtocol.Other, id: fn, name: 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 })
-)
+export const allAvailableButtons: ProtocolAction[] = [
+ ...Object.values(availableCockpitActions),
+ ...Object.values(mavlinkManualControlButtonFunctions),
+ ...Object.values(OtherProtocol).map((fn) => ({ protocol: JoystickProtocol.Other, id: fn, name: fn })),
+]
diff --git a/src/libs/joystick/protocols/cockpit-actions.ts b/src/libs/joystick/protocols/cockpit-actions.ts
new file mode 100644
index 000000000..25c355ed2
--- /dev/null
+++ b/src/libs/joystick/protocols/cockpit-actions.ts
@@ -0,0 +1,86 @@
+/* eslint-disable vue/max-len */
+/* eslint-disable prettier/prettier */
+/* eslint-disable max-len */
+import { v4 as uuid4 } from 'uuid'
+
+import { type JoystickProtocolActionsMapping,type JoystickState, type ProtocolAction, JoystickButton,JoystickProtocol } from '@/types/joystick'
+
+/**
+ * Possible functions in the MAVLink `MANUAL_CONTROL` message protocol
+ */
+export enum CockpitActionsFunction {
+ 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_arm',
+ mavlink_disarm = 'mavlink_disarm',
+ toggle_bottom_bar = 'toggle_bottom_bar',
+}
+
+/**
+ * An action to be performed by Cockpit itself
+ */
+export class CockpitAction implements ProtocolAction {
+ id: CockpitActionsFunction
+ name: string
+ readonly protocol = JoystickProtocol.CockpitAction
+
+ // eslint-disable-next-line jsdoc/require-jsdoc
+ constructor(id: CockpitActionsFunction, name: string) {
+ this.id = id
+ this.name = name
+ }
+}
+
+// Available actions
+export const availableCockpitActions: { [key in CockpitActionsFunction]: CockpitAction } = {
+ [CockpitActionsFunction.go_to_next_view]: new CockpitAction(CockpitActionsFunction.go_to_next_view, 'Go to next view'),
+ [CockpitActionsFunction.go_to_previous_view]: new CockpitAction(CockpitActionsFunction.go_to_previous_view, 'Go to previous view'),
+ [CockpitActionsFunction.toggle_full_screen]: new CockpitAction(CockpitActionsFunction.toggle_full_screen, 'Toggle full screen'),
+ [CockpitActionsFunction.mavlink_arm]: new CockpitAction(CockpitActionsFunction.mavlink_arm, 'Mavlink arm'),
+ [CockpitActionsFunction.mavlink_disarm]: new CockpitAction(CockpitActionsFunction.mavlink_disarm, 'Mavlink disarm'),
+ [CockpitActionsFunction.toggle_bottom_bar]: new CockpitAction(CockpitActionsFunction.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: JoystickProtocolActionsMapping): void => {
+ const actionsToCallback: CockpitAction[] = []
+ joystickState.buttons.forEach((state) => {
+ const mappedButton = mapping.buttonsCorrespondencies[state as JoystickButton]
+ if (state && mappedButton?.action.protocol === JoystickProtocol.CockpitAction) {
+ actionsToCallback.push(mappedButton.action as CockpitAction)
+ }
+ })
+ Object.values(actionsCallbacks).forEach((entry) => {
+ if (actionsToCallback.includes(entry.action)) {
+ entry.callback()
+ }
+ })
+}
\ No newline at end of file
diff --git a/src/libs/joystick/protocols/mavlink-manual-control.ts b/src/libs/joystick/protocols/mavlink-manual-control.ts
new file mode 100644
index 000000000..781318eee
--- /dev/null
+++ b/src/libs/joystick/protocols/mavlink-manual-control.ts
@@ -0,0 +1,395 @@
+/* eslint-disable prettier/prettier */
+/* eslint-disable vue/max-len */
+/* eslint-disable max-len */
+/* eslint-disable jsdoc/require-jsdoc */
+import { round, scale } from '@/libs/utils'
+import { type JoystickProtocolActionsMapping, type JoystickState, type ProtocolAction, InputType, JoystickAxis, JoystickProtocol, ProtocolControllerState, JoystickButton } from '@/types/joystick'
+
+/**
+ * Possible axes in the MAVLink `MANUAL_CONTROL` message protocol
+ */
+export enum MAVLinkAxisFunction {
+ X = 'axis_x',
+ Y = 'axis_y',
+ Z = 'axis_z',
+ R = 'axis_r',
+}
+
+/**
+ * Possible functions in the MAVLink `MANUAL_CONTROL` message protocol
+ */
+export enum MAVLinkButtonFunction {
+ disabled = 'Disabled', // 0
+ shift = 'Shift', // 1
+ arm_toggle = 'Arm toggle', // 2
+ arm = 'Arm', // 3
+ disarm = 'Disarm', // 4
+ mode_manual = 'Mode manual', // 5
+ mode_stabilize = 'Mode stabilize', // 6
+ mode_depth_hold = 'Mode depth hold', // 7
+ mode_poshold = 'Mode poshold', // 8
+ mode_auto = 'Mode auto', // 9
+ mode_circle = 'Mode circle', // 10
+ mode_guided = 'Mode guided', // 11
+ mode_acro = 'Mode acro', // 12
+ mount_center = 'Mount center', // 21
+ mount_tilt_up = 'Mount tilt up', // 22
+ mount_tilt_down = 'Mount tilt down', // 23
+ camera_trigger = 'Camera trigger', // 24
+ camera_source_toggle = 'Camera source toggle', // 25
+ mount_pan_right = 'Mount pan right', // 26
+ mount_pan_left = 'Mount pan left', // 27
+ lights1_cycle = 'Lights1 cycle', // 31
+ lights1_brighter = 'Lights1 brighter', // 32
+ lights1_dimmer = 'Lights1 dimmer', // 33
+ lights2_cycle = 'Lights2 cycle', // 34
+ lights2_brighter = 'Lights2 brighter', // 35
+ lights2_dimmer = 'Lights2 dimmer', // 36
+ gain_toggle = 'Gain toggle', // 41
+ gain_inc = 'Gain inc', // 42
+ gain_dec = 'Gain dec', // 43
+ trim_roll_inc = 'Trim roll inc', // 44
+ trim_roll_dec = 'Trim roll dec', // 45
+ trim_pitch_inc = 'Trim pitch inc', // 46
+ trim_pitch_dec = 'Trim pitch dec', // 47
+ input_hold_set = 'Input hold set', // 48
+ roll_pitch_toggle = 'Roll pitch toggle', // 49
+ relay1_on = 'Relay 1 on', // 51
+ relay1_off = 'Relay 1 off', // 52
+ relay1_toggle = 'Relay 1 toggle', // 53
+ relay2_on = 'Relay 2 on', // 54
+ relay2_off = 'Relay 2 off', // 55
+ relay2_toggle = 'Relay 2 toggle', // 56
+ relay3_on = 'Relay 3 on', // 57
+ relay3_off = 'Relay 3 off', // 58
+ relay3_toggle = 'Relay 3 toggle', // 59
+ servo1_inc = 'Servo 1 inc', // 61
+ servo1_dec = 'Servo 1 dec', // 62
+ servo1_min = 'Servo 1 min', // 63
+ servo1_max = 'Servo 1 max', // 64
+ servo1_center = 'Servo 1 center', // 65
+ servo2_inc = 'Servo 2 inc', // 66
+ servo2_dec = 'Servo 2 dec', // 67
+ servo2_min = 'Servo 2 min', // 68
+ servo2_max = 'Servo 2 max', // 69
+ servo2_center = 'Servo 2 center', // 70
+ servo3_inc = 'Servo 3 inc', // 71
+ servo3_dec = 'Servo 3 dec', // 72
+ servo3_min = 'Servo 3 min', // 73
+ servo3_max = 'Servo 3 max', // 74
+ servo3_center = 'Servo 3 center', // 75
+ servo1_min_momentary = 'Servo 1 min momentary', // 76
+ servo1_max_momentary = 'Servo 1 max momentary', // 77
+ servo1_min_toggle = 'Servo 1 min toggle', // 78
+ servo1_max_toggle = 'Servo 1 max toggle', // 79
+ servo2_min_momentary = 'Servo 2 min momentary', // 80
+ servo2_max_momentary = 'Servo 2 max momentary', // 81
+ servo2_min_toggle = 'Servo 2 min toggle', // 82
+ servo2_max_toggle = 'Servo 2 max toggle', // 83
+ servo3_min_momentary = 'Servo 3 min momentary', // 84
+ servo3_max_momentary = 'Servo 3 max momentary', // 85
+ servo3_min_toggle = 'Servo 3 min toggle', // 86
+ servo3_max_toggle = 'Servo 3 max toggle', // 87
+ custom1 = 'Custom 1', // 91
+ custom2 = 'Custom 2', // 92
+ custom3 = 'Custom 3', // 93
+ custom4 = 'Custom 4', // 94
+ custom5 = 'Custom 5', // 95
+ custom6 = 'Custom 6', // 96
+ relay4_on = 'Relay 4 on', // 101
+ relay4_off = 'Relay 4 off', // 102
+ relay4_toggle = 'Relay 4 toggle', // 103
+ relay1_momentary = 'Relay 1 momentary', // 104
+ relay2_momentary = 'Relay 2 momentary', // 105
+ relay3_momentary = 'Relay 3 momentary', // 106
+ relay4_momentary = 'Relay 4 momentary', // 107
+}
+
+export enum MAVLinkManualControlButton {
+ R0 = 'BTN0_FUNCTION',
+ S0 = 'BTN0_SFUNCTION',
+ R1 = 'BTN1_FUNCTION',
+ S1 = 'BTN1_SFUNCTION',
+ R2 = 'BTN2_FUNCTION',
+ S2 = 'BTN2_SFUNCTION',
+ R3 = 'BTN3_FUNCTION',
+ S3 = 'BTN3_SFUNCTION',
+ R4 = 'BTN4_FUNCTION',
+ S4 = 'BTN4_SFUNCTION',
+ R5 = 'BTN5_FUNCTION',
+ S5 = 'BTN5_SFUNCTION',
+ R6 = 'BTN6_FUNCTION',
+ S6 = 'BTN6_SFUNCTION',
+ R7 = 'BTN7_FUNCTION',
+ S7 = 'BTN7_SFUNCTION',
+ R8 = 'BTN8_FUNCTION',
+ S8 = 'BTN8_SFUNCTION',
+ R9 = 'BTN9_FUNCTION',
+ S9 = 'BTN9_SFUNCTION',
+ R10 = 'BTN10_FUNCTION',
+ S10 = 'BTN10_SFUNCTION',
+ R11 = 'BTN11_FUNCTION',
+ S11 = 'BTN11_SFUNCTION',
+ R12 = 'BTN12_FUNCTION',
+ S12 = 'BTN12_SFUNCTION',
+ R13 = 'BTN13_FUNCTION',
+ S13 = 'BTN13_SFUNCTION',
+ R14 = 'BTN14_FUNCTION',
+ S14 = 'BTN14_SFUNCTION',
+ R15 = 'BTN15_FUNCTION',
+ S15 = 'BTN15_SFUNCTION',
+}
+
+const shiftActivatedButtons = (): MAVLinkManualControlButton[] => {
+ return Object.entries(MAVLinkManualControlButton).filter((btn) => btn[1].includes('SFUNCTION')).map((btn) => btn[0] as MAVLinkManualControlButton)
+}
+
+const manualControlButtonFromParameterName = (name: string): MAVLinkManualControlButton | undefined => {
+ const button = Object.entries(MAVLinkManualControlButton).find((entry) => entry[1] === name)?.[0]
+ return button === undefined ? button : button as MAVLinkManualControlButton
+}
+
+/**
+ * An axis action meant to be used with MAVLink's `MANUAL_CONTROL` message
+ */
+export class MAVLinkManualControlAxisAction implements ProtocolAction {
+ readonly protocol = JoystickProtocol.MAVLinkManualControl
+ /**
+ * Create an axis input
+ * @param {MAVLinkAxisFunction} id Axis identification
+ * @param {string} name Axis human-readable name
+ */
+ constructor(public id: MAVLinkAxisFunction, public name: string) {}
+}
+
+/**
+ * A button action meant to be used with MAVLink's `MANUAL_CONTROL` message
+ */
+export class MAVLinkManualControlButtonAction implements ProtocolAction {
+ readonly protocol = JoystickProtocol.MAVLinkManualControl
+ /**
+ * Create a button input
+ * @param {MAVLinkButtonFunction} id Button identification
+ * @param {string} name Button human-readable name
+ */
+ constructor(public id: MAVLinkButtonFunction, public name: string) {}
+}
+
+// Available axis actions
+export const mavlinkManualControlAxes: { [key in MAVLinkAxisFunction]: MAVLinkManualControlAxisAction } = {
+ [MAVLinkAxisFunction.X]: new MAVLinkManualControlAxisAction(MAVLinkAxisFunction.X, 'Axis X'),
+ [MAVLinkAxisFunction.Y]: new MAVLinkManualControlAxisAction(MAVLinkAxisFunction.Y, 'Axis Y'),
+ [MAVLinkAxisFunction.Z]: new MAVLinkManualControlAxisAction(MAVLinkAxisFunction.Z, 'Axis Z'),
+ [MAVLinkAxisFunction.R]: new MAVLinkManualControlAxisAction(MAVLinkAxisFunction.R, 'Axis R'),
+}
+
+// Available button actions
+export const mavlinkManualControlButtonFunctions: { [key in MAVLinkButtonFunction]: MAVLinkManualControlButtonAction } = {
+ [MAVLinkButtonFunction.disabled]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.disabled, 'Disabled'),
+ [MAVLinkButtonFunction.shift]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.shift, 'Shift'),
+ [MAVLinkButtonFunction.arm_toggle]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.arm_toggle, 'Arm toggle'),
+ [MAVLinkButtonFunction.arm]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.arm, 'Arm'),
+ [MAVLinkButtonFunction.disarm]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.disarm, 'Disarm'),
+ [MAVLinkButtonFunction.mode_manual]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.mode_manual, 'Mode manual'),
+ [MAVLinkButtonFunction.mode_stabilize]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.mode_stabilize, 'Mode stabilize'),
+ [MAVLinkButtonFunction.mode_depth_hold]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.mode_depth_hold, 'Mode depth hold'),
+ [MAVLinkButtonFunction.mode_poshold]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.mode_poshold, 'Mode poshold'),
+ [MAVLinkButtonFunction.mode_auto]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.mode_auto, 'Mode auto'),
+ [MAVLinkButtonFunction.mode_circle]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.mode_circle, 'Mode circle'),
+ [MAVLinkButtonFunction.mode_guided]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.mode_guided, 'Mode guided'),
+ [MAVLinkButtonFunction.mode_acro]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.mode_acro, 'Mode acro'),
+ [MAVLinkButtonFunction.mount_center]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.mount_center, 'Mount center'),
+ [MAVLinkButtonFunction.mount_tilt_up]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.mount_tilt_up, 'Mount tilt up'),
+ [MAVLinkButtonFunction.mount_tilt_down]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.mount_tilt_down, 'Mount tilt down'),
+ [MAVLinkButtonFunction.camera_trigger]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.camera_trigger, 'Camera trigger'),
+ [MAVLinkButtonFunction.camera_source_toggle]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.camera_source_toggle, 'Camera source toggle'),
+ [MAVLinkButtonFunction.mount_pan_right]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.mount_pan_right, 'Mount pan right'),
+ [MAVLinkButtonFunction.mount_pan_left]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.mount_pan_left, 'Mount pan left'),
+ [MAVLinkButtonFunction.lights1_cycle]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.lights1_cycle, 'Lights1 cycle'),
+ [MAVLinkButtonFunction.lights1_brighter]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.lights1_brighter, 'Lights1 brighter'),
+ [MAVLinkButtonFunction.lights1_dimmer]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.lights1_dimmer, 'Lights1 dimmer'),
+ [MAVLinkButtonFunction.lights2_cycle]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.lights2_cycle, 'Lights2 cycle'),
+ [MAVLinkButtonFunction.lights2_brighter]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.lights2_brighter, 'Lights2 brighter'),
+ [MAVLinkButtonFunction.lights2_dimmer]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.lights2_dimmer, 'Lights2 dimmer'),
+ [MAVLinkButtonFunction.gain_toggle]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.gain_toggle, 'Gain toggle'),
+ [MAVLinkButtonFunction.gain_inc]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.gain_inc, 'Gain inc'),
+ [MAVLinkButtonFunction.gain_dec]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.gain_dec, 'Gain dec'),
+ [MAVLinkButtonFunction.trim_roll_inc]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.trim_roll_inc, 'Trim roll inc'),
+ [MAVLinkButtonFunction.trim_roll_dec]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.trim_roll_dec, 'Trim roll dec'),
+ [MAVLinkButtonFunction.trim_pitch_inc]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.trim_pitch_inc, 'Trim pitch inc'),
+ [MAVLinkButtonFunction.trim_pitch_dec]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.trim_pitch_dec, 'Trim pitch dec'),
+ [MAVLinkButtonFunction.input_hold_set]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.input_hold_set, 'Input hold set'),
+ [MAVLinkButtonFunction.roll_pitch_toggle]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.roll_pitch_toggle, 'Roll pitch toggle'),
+ [MAVLinkButtonFunction.relay1_on]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.relay1_on, 'Relay 1 on'),
+ [MAVLinkButtonFunction.relay1_off]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.relay1_off, 'Relay 1 off'),
+ [MAVLinkButtonFunction.relay1_toggle]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.relay1_toggle, 'Relay 1 toggle'),
+ [MAVLinkButtonFunction.relay2_on]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.relay2_on, 'Relay 2 on'),
+ [MAVLinkButtonFunction.relay2_off]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.relay2_off, 'Relay 2 off'),
+ [MAVLinkButtonFunction.relay2_toggle]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.relay2_toggle, 'Relay 2 toggle'),
+ [MAVLinkButtonFunction.relay3_on]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.relay3_on, 'Relay 3 on'),
+ [MAVLinkButtonFunction.relay3_off]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.relay3_off, 'Relay 3 off'),
+ [MAVLinkButtonFunction.relay3_toggle]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.relay3_toggle, 'Relay 3 toggle'),
+ [MAVLinkButtonFunction.servo1_inc]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo1_inc, 'Servo 1 inc'),
+ [MAVLinkButtonFunction.servo1_dec]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo1_dec, 'Servo 1 dec'),
+ [MAVLinkButtonFunction.servo1_min]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo1_min, 'Servo 1 min'),
+ [MAVLinkButtonFunction.servo1_max]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo1_max, 'Servo 1 max'),
+ [MAVLinkButtonFunction.servo1_center]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo1_center, 'Servo 1 center'),
+ [MAVLinkButtonFunction.servo2_inc]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo2_inc, 'Servo 2 inc'),
+ [MAVLinkButtonFunction.servo2_dec]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo2_dec, 'Servo 2 dec'),
+ [MAVLinkButtonFunction.servo2_min]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo2_min, 'Servo 2 min'),
+ [MAVLinkButtonFunction.servo2_max]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo2_max, 'Servo 2 max'),
+ [MAVLinkButtonFunction.servo2_center]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo2_center, 'Servo 2 center'),
+ [MAVLinkButtonFunction.servo3_inc]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo3_inc, 'Servo 3 inc'),
+ [MAVLinkButtonFunction.servo3_dec]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo3_dec, 'Servo 3 dec'),
+ [MAVLinkButtonFunction.servo3_min]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo3_min, 'Servo 3 min'),
+ [MAVLinkButtonFunction.servo3_max]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo3_max, 'Servo 3 max'),
+ [MAVLinkButtonFunction.servo3_center]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo3_center, 'Servo 3 center'),
+ [MAVLinkButtonFunction.servo1_min_momentary]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo1_min_momentary, 'Servo 1 min momentary'),
+ [MAVLinkButtonFunction.servo1_max_momentary]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo1_max_momentary, 'Servo 1 max momentary'),
+ [MAVLinkButtonFunction.servo1_min_toggle]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo1_min_toggle, 'Servo 1 min toggle'),
+ [MAVLinkButtonFunction.servo1_max_toggle]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo1_max_toggle, 'Servo 1 max toggle'),
+ [MAVLinkButtonFunction.servo2_min_momentary]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo2_min_momentary, 'Servo 2 min momentary'),
+ [MAVLinkButtonFunction.servo2_max_momentary]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo2_max_momentary, 'Servo 2 max momentary'),
+ [MAVLinkButtonFunction.servo2_min_toggle]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo2_min_toggle, 'Servo 2 min toggle'),
+ [MAVLinkButtonFunction.servo2_max_toggle]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo2_max_toggle, 'Servo 2 max toggle'),
+ [MAVLinkButtonFunction.servo3_min_momentary]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo3_min_momentary, 'Servo 3 min momentary'),
+ [MAVLinkButtonFunction.servo3_max_momentary]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo3_max_momentary, 'Servo 3 max momentary'),
+ [MAVLinkButtonFunction.servo3_min_toggle]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo3_min_toggle, 'Servo 3 min toggle'),
+ [MAVLinkButtonFunction.servo3_max_toggle]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.servo3_max_toggle, 'Servo 3 max toggle'),
+ [MAVLinkButtonFunction.custom1]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.custom1, 'Custom 1'),
+ [MAVLinkButtonFunction.custom2]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.custom2, 'Custom 2'),
+ [MAVLinkButtonFunction.custom3]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.custom3, 'Custom 3'),
+ [MAVLinkButtonFunction.custom4]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.custom4, 'Custom 4'),
+ [MAVLinkButtonFunction.custom5]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.custom5, 'Custom 5'),
+ [MAVLinkButtonFunction.custom6]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.custom6, 'Custom 6'),
+ [MAVLinkButtonFunction.relay4_on]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.relay4_on, 'Relay 4 on'),
+ [MAVLinkButtonFunction.relay4_off]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.relay4_off, 'Relay 4 off'),
+ [MAVLinkButtonFunction.relay4_toggle]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.relay4_toggle, 'Relay 4 toggle'),
+ [MAVLinkButtonFunction.relay1_momentary]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.relay1_momentary, 'Relay 1 momentary'),
+ [MAVLinkButtonFunction.relay2_momentary]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.relay2_momentary, 'Relay 2 momentary'),
+ [MAVLinkButtonFunction.relay3_momentary]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.relay3_momentary, 'Relay 3 momentary'),
+ [MAVLinkButtonFunction.relay4_momentary]: new MAVLinkManualControlButtonAction(MAVLinkButtonFunction.relay4_momentary, 'Relay 4 momentary'),
+}
+
+// Available actions (axes and buttons)
+export const availableMAVLinkManualControlActions = {
+ ...mavlinkManualControlAxes,
+ ...mavlinkManualControlButtonFunctions,
+}
+
+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 { JoystickProtocolActionsMapping } mapping - Gamepad API to Protocols joystick mapping, where assignments and limits are got from.
+ * @param { { title: string, value: number } } buttonParameterTable - Gamepad API to Protocols joystick mapping, where assignments and limits are got from.
+ * @param { { [key in string]: number } } currentParameters - Gamepad API to Protocols joystick mapping, where assignments and limits are got from.
+ * @param { number } target - Specify targeted vehicle ID.
+ */
+ constructor(
+ joystickState: JoystickState,
+ mapping: JoystickProtocolActionsMapping,
+ buttonParameterTable: { title: string, value: number }[],
+ currentParameters: { [key in string]: number },
+ target = 1
+ ) {
+ super()
+
+ const isMavlinkAction = (action: ProtocolAction): boolean => action.protocol === JoystickProtocol.MAVLinkManualControl
+
+ let buttons_int = 0
+
+ if (buttonParameterTable.length !== 0 && Object.entries(currentParameters).length !== 0) {
+ console.log('lets create a mavlink manual control package')
+
+ // Calculate buttons
+
+ // console.log(joystickState)
+ // console.log(mapping)
+ // console.log(buttonParameterTable)
+ // console.log(currentParameters)
+
+ const buttonParametersNamedObject: { [key in number]: string } = {}
+ buttonParameterTable.forEach((entry) => buttonParametersNamedObject[entry.value] = entry.title)
+
+ // console.log(buttonParametersNamedObject)
+
+ const currentButtonParameters = Object.entries(currentParameters).filter(([k,]) => k.includes('BTN'))
+
+ // console.log(currentButtonParameters)
+
+ const buttonActionIdTable = currentButtonParameters.map((btn) => ({ button: btn[0], actionId: buttonParametersNamedObject[btn[1]]}))
+
+ // console.log(buttonActionIdTable)
+
+ const maybeShiftButton = buttonActionIdTable.find((entry) => entry.actionId as MAVLinkButtonFunction === mavlinkManualControlButtonFunctions['Shift'].id)
+ let shiftButton = undefined
+ if (maybeShiftButton !== undefined) {
+ shiftButton = manualControlButtonFromParameterName(maybeShiftButton.button)
+ }
+
+ if (shiftButton === undefined) {
+ // map shift to some button
+ } else {
+ // we are fine
+ }
+
+ const actionsBeingActivated = joystickState.buttons
+ .map((btnState, idx) => ({ id: idx, value: btnState}))
+ .filter((btn) => btn.value ?? 0 > 0.5)
+ .map((btn) => mapping.buttonsCorrespondencies[btn.id as JoystickButton].action)
+ console.log('shift button: ', shiftButton)
+ console.log('actions activated', actionsBeingActivated.map((action) => action.name))
+
+ const vehicleButtonsToActivate = buttonActionIdTable.filter((entry) => actionsBeingActivated.map((action) => action.id).includes(entry.actionId as MAVLinkButtonFunction)).map((entry) => manualControlButtonFromParameterName(entry.button))
+ console.log('buttons activated', vehicleButtonsToActivate)
+ const useShift = shiftActivatedButtons().filter((btn) => vehicleButtonsToActivate.includes(btn)).length > 0
+ console.log('use shift?', useShift)
+
+ for (let i = 0; i < MavlinkControllerState.BUTTONS_PER_BITFIELD; i++) {
+ let buttonState = 0
+ if (shiftButton !== undefined && Number(shiftButton.replace('R', '').replace('S', '')) === i && useShift) {
+ buttonState = 1
+ }
+ vehicleButtonsToActivate.forEach((btn) => {
+ if (btn !== undefined && Number(btn.replace('R', '').replace('S', '')) === i) {
+ buttonState = 1
+ }
+ })
+ buttons_int += buttonState * 2 ** i
+ }
+
+ // Calculate axes
+
+ const xCorrespondency = Object.entries(mapping.axesCorrespondencies).find((entry) => entry[1].action.protocol === JoystickProtocol.MAVLinkManualControl && entry[1].action.id === mavlinkManualControlAxes.axis_x.id)
+ const yCorrespondency = Object.entries(mapping.axesCorrespondencies).find((entry) => entry[1].action.protocol === JoystickProtocol.MAVLinkManualControl && entry[1].action.id === mavlinkManualControlAxes.axis_y.id)
+ const zCorrespondency = Object.entries(mapping.axesCorrespondencies).find((entry) => entry[1].action.protocol === JoystickProtocol.MAVLinkManualControl && entry[1].action.id === mavlinkManualControlAxes.axis_z.id)
+ const rCorrespondency = Object.entries(mapping.axesCorrespondencies).find((entry) => entry[1].action.protocol === JoystickProtocol.MAVLinkManualControl && entry[1].action.id === mavlinkManualControlAxes.axis_r.id)
+
+ this.x = xCorrespondency === undefined ? 0 : round(scale(joystickState.axes[xCorrespondency[0] as unknown as JoystickAxis] ?? 0, -1, 1, xCorrespondency[1].min, xCorrespondency[1].max), 0)
+ this.y = yCorrespondency === undefined ? 0 : round(scale(joystickState.axes[yCorrespondency[0] as unknown as JoystickAxis] ?? 0, -1, 1, yCorrespondency[1].min, yCorrespondency[1].max), 0)
+ this.z = zCorrespondency === undefined ? 0 : round(scale(joystickState.axes[zCorrespondency[0] as unknown as JoystickAxis] ?? 0, -1, 1, zCorrespondency[1].min, zCorrespondency[1].max), 0)
+ this.r = rCorrespondency === undefined ? 0 : round(scale(joystickState.axes[rCorrespondency[0] as unknown as JoystickAxis] ?? 0, -1, 1, rCorrespondency[1].min, rCorrespondency[1].max), 0)
+ } else {
+ console.log('not enough information to create a mavlink manual control package')
+ }
+
+ console.log('buttons', buttons_int)
+ console.log('x', this.x)
+ console.log('y', this.y)
+ console.log('z', this.z)
+ console.log('r', this.r)
+
+ this.buttons = buttons_int
+ this.target = round(target, 0)
+ }
+}
+
diff --git a/src/libs/vehicle/ardupilot/ardupilot.ts b/src/libs/vehicle/ardupilot/ardupilot.ts
index b7624c5ff..0efdedbad 100644
--- a/src/libs/vehicle/ardupilot/ardupilot.ts
+++ b/src/libs/vehicle/ardupilot/ardupilot.ts
@@ -20,7 +20,7 @@ import {
} from '@/libs/connection/m2r/messages/mavlink2rest-enum'
import { MavFrame } from '@/libs/connection/m2r/messages/mavlink2rest-enum'
import { type Message } from '@/libs/connection/m2r/messages/mavlink2rest-message'
-import { MavlinkControllerState } from '@/libs/joystick/protocols'
+import { MavlinkControllerState } from '@/libs/joystick/protocols/mavlink-manual-control'
import { SignalTyped } from '@/libs/signal'
import { round } from '@/libs/utils'
import {
diff --git a/src/stores/controller.ts b/src/stores/controller.ts
index 9ac92c0bf..c2176798b 100644
--- a/src/stores/controller.ts
+++ b/src/stores/controller.ts
@@ -7,17 +7,20 @@ import { ref } from 'vue'
import { availableGamepadToCockpitMaps, cockpitStandardToProtocols } from '@/assets/joystick-profiles'
import { type JoystickEvent, EventType, joystickManager, JoystickModel } from '@/libs/joystick/manager'
import { allAvailableAxes, allAvailableButtons } from '@/libs/joystick/protocols'
-import { type JoystickState, type ProtocolControllerMapping, Joystick } from '@/types/joystick'
+import { type JoystickProtocolActionsMapping, type JoystickState, Joystick } from '@/types/joystick'
-export type controllerUpdateCallback = (state: JoystickState, protocolMapping: ProtocolControllerMapping) => void
+export type controllerUpdateCallback = (
+ state: JoystickState,
+ protocolActionsMapping: JoystickProtocolActionsMapping
+) => void
export const useControllerStore = defineStore('controller', () => {
const joysticks = ref
Could not stablish communication with the vehicle.
@@ -75,8 +71,7 @@
:b15="joystick.state.buttons[15]"
:b16="joystick.state.buttons[16]"
:b17="joystick.state.buttons[17]"
- :protocol-mapping="controllerStore.protocolMapping"
- :button-label-correspondency="controllerStore.availableProtocolButtonFunctions"
+ :buttons-actions-correspondency="controllerStore.protocolMapping.buttonsCorrespondencies"
@click="(e) => setCurrentInputs(joystick, e)"
/>
@@ -130,21 +125,18 @@
:b15="currentJoystick.state.buttons[15]"
:b16="currentJoystick.state.buttons[16]"
:b17="currentJoystick.state.buttons[17]"
- :protocol-mapping="controllerStore.protocolMapping"
- :button-label-correspondency="controllerStore.availableProtocolButtonFunctions"
+ :buttons-actions-correspondency="controllerStore.protocolMapping.buttonsCorrespondencies"
/>
-
-
-
{{
- [JoystickAxis.A0, JoystickAxis.A2].includes(Number(input.value))
- ? 'mdi-pan-horizontal'
- : 'mdi-pan-vertical'
+
+
+
+ {{
+ [JoystickAxis.A0, JoystickAxis.A2].includes(input.id) ? 'mdi-pan-horizontal' : 'mdi-pan-vertical'
}}
updateMapping(input.value, newValue, input.type)"
/>
-
+
Calibrate
@@ -198,27 +193,24 @@
Assign
-
+
{{ protocol }}
@@ -234,25 +226,26 @@