diff --git a/src/assets/joystick-profiles.ts b/src/assets/joystick-profiles.ts index 77303be94..5f1dbcf5c 100644 --- a/src/assets/joystick-profiles.ts +++ b/src/assets/joystick-profiles.ts @@ -14,57 +14,110 @@ import { } from '@/types/joystick' // TODO: Adjust mapping for PS5 controller -export const cockpitStandardToProtocols: JoystickProtocolActionsMapping = { - name: 'Default ROV functions mapping', - 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 }, +export const cockpitStandardToProtocols: JoystickProtocolActionsMapping[] = [ + { + name: 'ROV functions mapping', + 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: modifierKeyActions.shift }, + [JoystickButton.B1]: { action: availableMavlinkManualControlButtonFunctions['Mode manual'] }, + [JoystickButton.B2]: { action: availableMavlinkManualControlButtonFunctions['Mode depth hold'] }, + [JoystickButton.B3]: { action: availableMavlinkManualControlButtonFunctions['Mode stabilize'] }, + [JoystickButton.B4]: { action: availableCockpitActions.go_to_previous_view }, + [JoystickButton.B5]: { action: availableCockpitActions.go_to_next_view }, + [JoystickButton.B6]: { action: availableMavlinkManualControlButtonFunctions['Mount tilt down'] }, + [JoystickButton.B7]: { action: availableMavlinkManualControlButtonFunctions['Mount tilt up'] }, + [JoystickButton.B8]: { action: availableMavlinkManualControlButtonFunctions['Disarm'] }, + [JoystickButton.B9]: { action: availableMavlinkManualControlButtonFunctions['Arm'] }, + [JoystickButton.B10]: { action: availableMavlinkManualControlButtonFunctions['Mount center'] }, + [JoystickButton.B11]: { action: availableMavlinkManualControlButtonFunctions['Input hold set'] }, + [JoystickButton.B12]: { action: availableMavlinkManualControlButtonFunctions['Gain inc'] }, + [JoystickButton.B13]: { action: availableMavlinkManualControlButtonFunctions['Gain dec'] }, + [JoystickButton.B14]: { action: availableMavlinkManualControlButtonFunctions['Lights1 dimmer'] }, + [JoystickButton.B15]: { action: availableMavlinkManualControlButtonFunctions['Lights1 brighter'] }, + [JoystickButton.B16]: { action: availableCockpitActions.toggle_bottom_bar }, + [JoystickButton.B17]: { action: availableMavlinkManualControlButtonFunctions['Roll pitch toggle'] }, + }, + [CockpitModifierKeyOption.shift]: { + [JoystickButton.B0]: { action: otherAvailableActions.no_function }, + [JoystickButton.B1]: { action: otherAvailableActions.no_function }, + [JoystickButton.B2]: { action: availableMavlinkManualControlButtonFunctions['Mode poshold'] }, + [JoystickButton.B3]: { action: availableMavlinkManualControlButtonFunctions['Mode acro'] }, + [JoystickButton.B4]: { action: otherAvailableActions.no_function }, + [JoystickButton.B5]: { action: otherAvailableActions.no_function }, + [JoystickButton.B6]: { action: availableMavlinkManualControlButtonFunctions['Servo 1 min'] }, + [JoystickButton.B7]: { action: availableMavlinkManualControlButtonFunctions['Servo 1 max'] }, + [JoystickButton.B8]: { action: otherAvailableActions.no_function }, + [JoystickButton.B9]: { action: otherAvailableActions.no_function }, + [JoystickButton.B10]: { action: availableMavlinkManualControlButtonFunctions['Relay 1 toggle'] }, + [JoystickButton.B11]: { action: otherAvailableActions.no_function }, + [JoystickButton.B12]: { action: availableMavlinkManualControlButtonFunctions['Trim pitch inc'] }, + [JoystickButton.B13]: { action: availableMavlinkManualControlButtonFunctions['Trim pitch dec'] }, + [JoystickButton.B14]: { action: availableMavlinkManualControlButtonFunctions['Trim roll dec'] }, + [JoystickButton.B15]: { action: availableMavlinkManualControlButtonFunctions['Trim roll inc'] }, + [JoystickButton.B16]: { action: otherAvailableActions.no_function }, + [JoystickButton.B17]: { action: otherAvailableActions.no_function }, + }, + }, }, - buttonsCorrespondencies: { - [CockpitModifierKeyOption.regular]: { - [JoystickButton.B0]: { action: modifierKeyActions.shift }, - [JoystickButton.B1]: { action: availableMavlinkManualControlButtonFunctions['Mode manual'] }, - [JoystickButton.B2]: { action: availableMavlinkManualControlButtonFunctions['Mode depth hold'] }, - [JoystickButton.B3]: { action: availableMavlinkManualControlButtonFunctions['Mode stabilize'] }, - [JoystickButton.B4]: { action: availableCockpitActions.go_to_previous_view }, - [JoystickButton.B5]: { action: availableCockpitActions.go_to_next_view }, - [JoystickButton.B6]: { action: availableMavlinkManualControlButtonFunctions['Mount tilt down'] }, - [JoystickButton.B7]: { action: availableMavlinkManualControlButtonFunctions['Mount tilt up'] }, - [JoystickButton.B8]: { action: availableMavlinkManualControlButtonFunctions['Disarm'] }, - [JoystickButton.B9]: { action: availableMavlinkManualControlButtonFunctions['Arm'] }, - [JoystickButton.B10]: { action: availableMavlinkManualControlButtonFunctions['Mount center'] }, - [JoystickButton.B11]: { action: availableMavlinkManualControlButtonFunctions['Input hold set'] }, - [JoystickButton.B12]: { action: availableMavlinkManualControlButtonFunctions['Gain inc'] }, - [JoystickButton.B13]: { action: availableMavlinkManualControlButtonFunctions['Gain dec'] }, - [JoystickButton.B14]: { action: availableMavlinkManualControlButtonFunctions['Lights1 dimmer'] }, - [JoystickButton.B15]: { action: availableMavlinkManualControlButtonFunctions['Lights1 brighter'] }, - [JoystickButton.B16]: { action: availableCockpitActions.toggle_bottom_bar }, - [JoystickButton.B17]: { action: availableMavlinkManualControlButtonFunctions['Roll pitch toggle'] }, + { + name: 'Boat functions mapping', + 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: -1000 }, }, - [CockpitModifierKeyOption.shift]: { - [JoystickButton.B0]: { action: otherAvailableActions.no_function }, - [JoystickButton.B1]: { action: otherAvailableActions.no_function }, - [JoystickButton.B2]: { action: availableMavlinkManualControlButtonFunctions['Mode poshold'] }, - [JoystickButton.B3]: { action: availableMavlinkManualControlButtonFunctions['Mode acro'] }, - [JoystickButton.B4]: { action: otherAvailableActions.no_function }, - [JoystickButton.B5]: { action: otherAvailableActions.no_function }, - [JoystickButton.B6]: { action: availableMavlinkManualControlButtonFunctions['Servo 1 min'] }, - [JoystickButton.B7]: { action: availableMavlinkManualControlButtonFunctions['Servo 1 max'] }, - [JoystickButton.B8]: { action: otherAvailableActions.no_function }, - [JoystickButton.B9]: { action: otherAvailableActions.no_function }, - [JoystickButton.B10]: { action: availableMavlinkManualControlButtonFunctions['Relay 1 toggle'] }, - [JoystickButton.B11]: { action: otherAvailableActions.no_function }, - [JoystickButton.B12]: { action: availableMavlinkManualControlButtonFunctions['Trim pitch inc'] }, - [JoystickButton.B13]: { action: availableMavlinkManualControlButtonFunctions['Trim pitch dec'] }, - [JoystickButton.B14]: { action: availableMavlinkManualControlButtonFunctions['Trim roll dec'] }, - [JoystickButton.B15]: { action: availableMavlinkManualControlButtonFunctions['Trim roll inc'] }, - [JoystickButton.B16]: { action: otherAvailableActions.no_function }, - [JoystickButton.B17]: { action: otherAvailableActions.no_function }, + buttonsCorrespondencies: { + [CockpitModifierKeyOption.regular]: { + [JoystickButton.B0]: { action: modifierKeyActions.shift }, + [JoystickButton.B1]: { action: otherAvailableActions.no_function }, + [JoystickButton.B2]: { action: otherAvailableActions.no_function }, + [JoystickButton.B3]: { action: otherAvailableActions.no_function }, + [JoystickButton.B4]: { action: availableCockpitActions.go_to_previous_view }, + [JoystickButton.B5]: { action: availableCockpitActions.go_to_next_view }, + [JoystickButton.B6]: { action: otherAvailableActions.no_function }, + [JoystickButton.B7]: { action: otherAvailableActions.no_function }, + [JoystickButton.B8]: { action: availableCockpitActions.mavlink_disarm }, + [JoystickButton.B9]: { action: availableCockpitActions.mavlink_arm }, + [JoystickButton.B10]: { action: otherAvailableActions.no_function }, + [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: availableCockpitActions.toggle_bottom_bar }, + [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: 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 }, + }, }, }, -} +] /** * Follows the standard controller in the Gamepad API: https://www.w3.org/TR/gamepad/#dfn-standard-gamepad diff --git a/src/stores/controller.ts b/src/stores/controller.ts index 548cc2275..79940ed2f 100644 --- a/src/stores/controller.ts +++ b/src/stores/controller.ts @@ -2,7 +2,7 @@ import { useStorage } from '@vueuse/core' import { saveAs } from 'file-saver' import { defineStore } from 'pinia' import Swal from 'sweetalert2' -import { ref } from 'vue' +import { computed, ref } from 'vue' import { availableGamepadToCockpitMaps, cockpitStandardToProtocols } from '@/assets/joystick-profiles' import { getKeyDataFromCockpitVehicleStorage, setKeyDataOnCockpitVehicleStorage } from '@/libs/blueos' @@ -26,18 +26,42 @@ export type controllerUpdateCallback = ( activeButtonActions: ProtocolAction[] ) => void -const protocolMappingKey = 'cockpit-protocol-mapping-v5' +const protocolMappingsKey = 'cockpit-protocol-mappings-v1' +const protocolMappingIndexKey = 'cockpit-protocol-mapping-index-v1' const cockpitStdMappingsKey = 'cockpit-standard-mappings-v2' export const useControllerStore = defineStore('controller', () => { const joysticks = ref>(new Map()) const updateCallbacks = ref([]) - const protocolMapping = useStorage(protocolMappingKey, cockpitStandardToProtocols) + const protocolMappings = useStorage(protocolMappingsKey, cockpitStandardToProtocols) + const protocolMappingIndex = useStorage(protocolMappingIndexKey, 0) const cockpitStdMappings = useStorage(cockpitStdMappingsKey, availableGamepadToCockpitMaps) const availableAxesActions = allAvailableAxes const availableButtonActions = allAvailableButtons const enableForwarding = ref(true) + const protocolMapping = computed({ + get() { + return protocolMappings.value[protocolMappingIndex.value] + }, + set(newValue) { + protocolMappings.value[protocolMappingIndex.value] = newValue + }, + }) + + /** + * Change current protocol mapping for given one + * @param { JoystickProtocolActionsMapping } mapping - The functions mapping to be loaded + */ + const loadProtocolMapping = (mapping: JoystickProtocolActionsMapping): void => { + const mappingIndex = protocolMappings.value.findIndex((p) => p.name === mapping.name) + if (mappingIndex === -1) { + Swal.fire({ icon: 'error', text: 'Could not find mapping.', timer: 3000 }) + return + } + protocolMappingIndex.value = mappingIndex + } + const registerControllerUpdateCallback = (callback: controllerUpdateCallback): void => { updateCallbacks.value.push(callback) } @@ -220,26 +244,25 @@ export const useControllerStore = defineStore('controller', () => { const exportFunctionsMappingToVehicle = async ( vehicleAddress: string, - functionsMapping: JoystickProtocolActionsMapping + functionsMapping: JoystickProtocolActionsMapping[] ): Promise => { - await setKeyDataOnCockpitVehicleStorage(vehicleAddress, protocolMappingKey, functionsMapping) + await setKeyDataOnCockpitVehicleStorage(vehicleAddress, protocolMappingsKey, functionsMapping) Swal.fire({ icon: 'success', text: 'Joystick functions mapping exported to vehicle.', timer: 3000 }) } const importFunctionsMappingFromVehicle = async (vehicleAddress: string): Promise => { - const newMapping = await getKeyDataFromCockpitVehicleStorage(vehicleAddress, protocolMappingKey) - if ( - !newMapping || - !newMapping['name'] || - !newMapping['axesCorrespondencies'] || - !newMapping['buttonsCorrespondencies'] - ) { - Swal.fire({ icon: 'error', text: 'Could not import functions mapping from vehicle. Invalid data.', timer: 3000 }) - return + const newMappings = await getKeyDataFromCockpitVehicleStorage(vehicleAddress, protocolMappingsKey) + if (!newMappings) { + throw new Error('Could not import functions mapping from vehicle. No data available.') } + + newMappings.forEach((mapping: JoystickProtocolActionsMapping) => { + if (!mapping || !mapping['name'] || !mapping['axesCorrespondencies'] || !mapping['buttonsCorrespondencies']) { + throw new Error('Could not import joystick funtions from vehicle. Invalid data.') + } + }) // @ts-ignore: We check for the necessary fields in the if before - protocolMapping.value = newMapping - Swal.fire({ icon: 'success', text: 'Joystick functions mapping imported from vehicle.', timer: 3000 }) + protocolMappings.value = newMappings } return { @@ -247,9 +270,11 @@ export const useControllerStore = defineStore('controller', () => { enableForwarding, joysticks, protocolMapping, + protocolMappings, cockpitStdMappings, availableAxesActions, availableButtonActions, + loadProtocolMapping, exportJoystickMapping, importJoystickMapping, exportJoysticksMappingsToVehicle, diff --git a/src/views/ConfigurationJoystickView.vue b/src/views/ConfigurationJoystickView.vue index 3fab28cec..81f5fb4fc 100644 --- a/src/views/ConfigurationJoystickView.vue +++ b/src/views/ConfigurationJoystickView.vue @@ -38,6 +38,17 @@

Could not stablish communication with the vehicle.

Button functions will appear as numbers. If connection is restablished, function names will appear.

+
+ +
@@ -218,7 +231,11 @@
- {{ [JoystickAxis.A0, JoystickAxis.A2].includes(input.id) ? 'mdi-pan-horizontal' : 'mdi-pan-vertical' }} + {{ + [JoystickAxis.A0, JoystickAxis.A2].includes(Number(input.id)) + ? 'mdi-pan-horizontal' + : 'mdi-pan-vertical' + }}