diff --git a/gui/public/i18n/en/translation.ftl b/gui/public/i18n/en/translation.ftl index 1c07f91bce..b5fd016814 100644 --- a/gui/public/i18n/en/translation.ftl +++ b/gui/public/i18n/en/translation.ftl @@ -187,6 +187,12 @@ tracker-infos-hardware_identifier = Hardware ID tracker-infos-imu = IMU Sensor tracker-infos-board_type = Main board tracker-infos-network_version = Protocol Version +tracker-infos-magnetometer = Magnetometer +tracker-infos-magnetometer-status = { $status -> + *[0] Not supported + [1] Disabled + [2] Enabled +} ## Tracker settings tracker-settings-back = Go back to trackers list @@ -200,6 +206,13 @@ tracker-settings-mounting_section-edit = Edit mounting tracker-settings-drift_compensation_section = Allow drift compensation tracker-settings-drift_compensation_section-description = Should this tracker compensate for its drift when drift compensation is enabled? tracker-settings-drift_compensation_section-edit = Allow drift compensation +tracker-settings-use_mag = Allow magnetometer on this tracker +# Multiline! +tracker-settings-use_mag-description = + Should this tracker use magnetometer to reduce drift when magnetometer usage is allowed? Please don't shutdown your tracker while toggling this! + + You need to allow magnetometer usage first, click here to go to the setting. +tracker-settings-use_mag-label = Allow magnetometer # The . means it's an attribute and it's related to the top key. # In this case that is the settings for the assignment section. tracker-settings-name_section = Tracker name @@ -354,6 +367,11 @@ settings-general-tracker_mechanics-save_mounting_reset-description = Saves the automatic mounting reset calibrations for the trackers between restarts. Useful when wearing a suit where trackers don't move between sessions. Not recommended for normal users! settings-general-tracker_mechanics-save_mounting_reset-enabled-label = Save mounting reset +settings-general-tracker_mechanics-use_mag_on_all_trackers = Use magnetometer on all IMU trackers that support it +settings-general-tracker_mechanics-use_mag_on_all_trackers-description = + Uses magnetometer on all trackers that have a compatible firmware for it, reducing drift in stable magnetic environments. + Can be disabled per tracker in the tracker's settings. Please don't shutdown any of the trackers while toggling this! +settings-general-tracker_mechanics-use_mag_on_all_trackers-label = Use magnetometer on trackers ## FK/Tracking settings settings-general-fk_settings = Tracking settings diff --git a/gui/src/components/commons/Checkbox.tsx b/gui/src/components/commons/Checkbox.tsx index 2ac8cb8e8f..7287403612 100644 --- a/gui/src/components/commons/Checkbox.tsx +++ b/gui/src/components/commons/Checkbox.tsx @@ -9,6 +9,7 @@ export function CheckBox({ control, outlined, name, + loading, // input props disabled, ...props @@ -19,6 +20,7 @@ export function CheckBox({ variant?: 'checkbox' | 'toggle'; color?: 'primary' | 'secondary' | 'tertiary'; outlined?: boolean; + loading?: boolean; } & React.HTMLProps) { const classes = useMemo(() => { const vriantsMap = { @@ -32,7 +34,9 @@ export function CheckBox({ toggle: { checkbox: classNames('hidden'), toggle: classNames('w-10 h-4 rounded-full relative transition-colors'), - pin: classNames('h-2 w-2 bg-background-10 rounded-full absolute m-1'), + pin: classNames( + 'h-2 w-2 bg-background-10 rounded-full absolute m-1 transition-opacity' + ), }, }; return vriantsMap[variant]; @@ -60,7 +64,8 @@ export function CheckBox({ 'w-full py-3 flex gap-2 items-center text-standard-bold', { 'px-3': outlined, - 'cursor-pointer': !disabled, + 'cursor-pointer': !disabled || !loading, + 'cursor-default': disabled || loading, } )} > @@ -71,23 +76,26 @@ export function CheckBox({ name={name} className={classes.checkbox} type="checkbox" - disabled={disabled} + disabled={disabled || loading} {...props} /> {variant === 'toggle' && (
diff --git a/gui/src/components/settings/pages/GeneralSettings.tsx b/gui/src/components/settings/pages/GeneralSettings.tsx index db8416f0ed..d384670e79 100644 --- a/gui/src/components/settings/pages/GeneralSettings.tsx +++ b/gui/src/components/settings/pages/GeneralSettings.tsx @@ -31,6 +31,7 @@ import { SettingsPagePaneLayout, } from '@/components/settings/SettingsPageLayout'; import { HandsWarningModal } from '@/components/settings/HandsWarningModal'; +import { MagnetometerToggleSetting } from './MagnetometerToggleSetting'; import { DriftCompensationModal } from '@/components/settings/DriftCompensationModal'; interface SettingsForm { @@ -808,6 +809,10 @@ export function GeneralSettings() { 'settings-general-tracker_mechanics-save_mounting_reset-enabled-label' )} /> + (null); + // used to disable the tracker specific toggle if false + const [globalToggle, setGlobalToggle] = useState(false); + const [waitingMag, setWaitingMag] = useState(false); + const { control, watch, handleSubmit, reset } = + useForm({ + defaultValues: { magToggle: settingType === 'tracker' }, + }); + + const onSubmit = useCallback( + (values: MagnetometerToggleForm) => { + if (originalValue.current === values.magToggle) return; + setWaitingMag(true); + const req = new ChangeMagToggleRequestT(); + if (trackerNum !== undefined) { + const id = new TrackerIdT( + deviceId ? new DeviceIdT(deviceId) : undefined, + trackerNum + ); + req.trackerId = id; + } + + req.enable = values.magToggle; + sendRPCPacket(RpcMessage.ChangeMagToggleRequest, req); + }, + [trackerNum, deviceId] + ); + + useEffect(() => { + const subscription = watch(() => handleSubmit(onSubmit)()); + return () => subscription.unsubscribe(); + }, []); + + useEffect(() => { + const req = new MagToggleRequestT(); + if (trackerNum !== undefined) { + const id = new TrackerIdT( + deviceId ? new DeviceIdT(deviceId) : undefined, + trackerNum + ); + req.trackerId = id; + sendRPCPacket(RpcMessage.MagToggleRequest, new MagToggleRequestT()); + } + sendRPCPacket(RpcMessage.MagToggleRequest, req); + }, [trackerNum, deviceId]); + + useRPCPacket(RpcMessage.MagToggleResponse, (mag: MagToggleResponseT) => { + if (trackerNum !== undefined && mag.trackerId?.trackerNum === undefined) { + setGlobalToggle(mag.enable); + } + if ( + mag.trackerId?.trackerNum !== trackerNum || + mag.trackerId?.deviceId?.id !== deviceId + ) { + return; + } + originalValue.current = mag.enable; + setWaitingMag(false); + reset({ magToggle: mag.enable }); + }); + + return settingType === 'general' ? ( + <> +
+ + {l10n.getString( + 'settings-general-tracker_mechanics-use_mag_on_all_trackers' + )} + + }} + > + + +
+ + + ) : ( +
+ + {l10n.getString('tracker-settings-use_mag')} + + , + magSetting: ( + + ), + }} + > + + +
+ +
+
+ ); +} diff --git a/gui/src/components/tracker/TrackerSettings.tsx b/gui/src/components/tracker/TrackerSettings.tsx index 9b18455ab8..85151a715d 100644 --- a/gui/src/components/tracker/TrackerSettings.tsx +++ b/gui/src/components/tracker/TrackerSettings.tsx @@ -33,6 +33,7 @@ import { SingleTrackerBodyAssignmentMenu } from './SingleTrackerBodyAssignmentMe import { TrackerCard } from './TrackerCard'; import { Quaternion } from 'three'; import { useAppContext } from '@/hooks/app'; +import { MagnetometerToggleSetting } from '@/components/settings/pages/MagnetometerToggleSetting'; const rotationsLabels: [Quaternion, string][] = [ [rotationToQuatMap.BACK, 'tracker-rotation-back'], @@ -276,6 +277,18 @@ export function TrackerSettingsPage() { {tracker?.device?.hardwareInfo?.boardType || '--'}
+
+ + {l10n.getString('tracker-infos-magnetometer')} + + + {tracker?.tracker.info?.magnetometer === undefined + ? '--' + : l10n.getString('tracker-infos-magnetometer-status', { + status: tracker.tracker.info.magnetometer, + })} + +
{l10n.getString('tracker-infos-network_version')} @@ -403,6 +416,13 @@ export function TrackerSettingsPage() {
)} + {tracker?.tracker.info?.isImu && ( + + )}
{l10n.getString('tracker-settings-name_section')} diff --git a/server/build.gradle.kts b/server/build.gradle.kts index e20ea0c164..645f9c3831 100644 --- a/server/build.gradle.kts +++ b/server/build.gradle.kts @@ -37,7 +37,7 @@ configure { "java.util.*,kotlin.math.*,dev.slimevr.autobone.errors.*" + ",io.github.axisangles.ktmath.*,kotlinx.atomicfu.*" + ",dev.slimevr.tracking.trackers.*,dev.slimevr.desktop.platform.ProtobufMessages.*" + - ",com.illposed.osc.*,android.app.*", + ",solarxr_protocol.rpc.*,kotlinx.coroutines.*,com.illposed.osc.*,android.app.*", "ij_kotlin_allow_trailing_comma" to true, ) val ktlintVersion = "1.2.1" diff --git a/server/core/build.gradle.kts b/server/core/build.gradle.kts index 0c15a3b551..cc400bc2ef 100644 --- a/server/core/build.gradle.kts +++ b/server/core/build.gradle.kts @@ -76,6 +76,7 @@ dependencies { implementation("org.java-websocket:Java-WebSocket:1.+") implementation("com.melloware:jintellitype:1.+") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1") // Jitpack implementation("com.github.SlimeVR:oscquery-kt:566a0cba58") diff --git a/server/core/src/main/java/dev/slimevr/config/ServerConfig.java b/server/core/src/main/java/dev/slimevr/config/ServerConfig.java deleted file mode 100644 index 2daf2a749c..0000000000 --- a/server/core/src/main/java/dev/slimevr/config/ServerConfig.java +++ /dev/null @@ -1,10 +0,0 @@ -package dev.slimevr.config; - -public class ServerConfig { - - private int trackerPort = 6969; - - public int getTrackerPort() { - return trackerPort; - } -} diff --git a/server/core/src/main/java/dev/slimevr/config/ServerConfig.kt b/server/core/src/main/java/dev/slimevr/config/ServerConfig.kt new file mode 100644 index 0000000000..717d334fe8 --- /dev/null +++ b/server/core/src/main/java/dev/slimevr/config/ServerConfig.kt @@ -0,0 +1,49 @@ +package dev.slimevr.config + +import dev.slimevr.VRServer +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.sync.Mutex + +class ServerConfig { + val trackerPort: Int = 6969 + + var useMagnetometerOnAllTrackers: Boolean = false + private set + + private val magMutex = Mutex() + suspend fun defineMagOnAllTrackers(state: Boolean) = coroutineScope { + magMutex.lock() + try { + if (useMagnetometerOnAllTrackers == state) return@coroutineScope + + VRServer.instance.deviceManager.devices.filter { it.magSupport }.map { + async { + if (!state) { + it.setMag(false) + return@async + } + + val every = it.trackers.all { (_, t) -> t.config.shouldHaveMagEnabled == true } + if (every) { + it.setMag(true) + return@async + } + + it.trackers.filterValues { it.config.shouldHaveMagEnabled == true } + .map { (_, t) -> + async { + // FIXME: Tracker gets restarted after each setMag, what will happen for devices with 3 trackers? + it.setMag(true, t.trackerNum) + } + }.awaitAll() + } + }.awaitAll() + + useMagnetometerOnAllTrackers = state + } finally { + magMutex.unlock() + } + } +} diff --git a/server/core/src/main/java/dev/slimevr/config/TrackerConfig.kt b/server/core/src/main/java/dev/slimevr/config/TrackerConfig.kt index 122abc5c1f..7770bec6be 100644 --- a/server/core/src/main/java/dev/slimevr/config/TrackerConfig.kt +++ b/server/core/src/main/java/dev/slimevr/config/TrackerConfig.kt @@ -2,6 +2,7 @@ package dev.slimevr.config import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.node.JsonNodeFactory +import dev.slimevr.VRServer import dev.slimevr.tracking.trackers.Tracker import io.github.axisangles.ktmath.ObjectQuaternion @@ -16,12 +17,18 @@ class TrackerConfig { var mountingResetOrientation: ObjectQuaternion? = null var allowDriftCompensation: Boolean? = null + /** + * Only checked if [ServerConfig.useMagnetometerOnAllTrackers] enabled + */ + var shouldHaveMagEnabled: Boolean? = null + constructor() constructor(tracker: Tracker) { this.designation = if (tracker.trackerPosition != null) tracker.trackerPosition!!.designation else null this.customName = tracker.customName allowDriftCompensation = tracker.isImu() + shouldHaveMagEnabled = tracker.isImu() } companion object { @@ -37,3 +44,6 @@ class TrackerConfig { } } } + +val Tracker.config: TrackerConfig + get() = VRServer.instance.configManager.vrConfig.getTracker(this) diff --git a/server/core/src/main/java/dev/slimevr/protocol/datafeed/DataFeedBuilder.java b/server/core/src/main/java/dev/slimevr/protocol/datafeed/DataFeedBuilder.java index bd60898624..3eb480af0d 100644 --- a/server/core/src/main/java/dev/slimevr/protocol/datafeed/DataFeedBuilder.java +++ b/server/core/src/main/java/dev/slimevr/protocol/datafeed/DataFeedBuilder.java @@ -139,6 +139,7 @@ public static int createTrackerInfos( TrackerInfo.addMountingResetOrientation(fbb, createQuat(fbb, mountResetFix)); } + TrackerInfo.addMagnetometer(fbb, tracker.getMagStatus().getSolarType()); TrackerInfo.addIsHmd(fbb, tracker.isHmd()); return TrackerInfo.endTrackerInfo(fbb); diff --git a/server/core/src/main/java/dev/slimevr/protocol/rpc/RPCHandler.kt b/server/core/src/main/java/dev/slimevr/protocol/rpc/RPCHandler.kt index 02f3971aa0..d9995f647e 100644 --- a/server/core/src/main/java/dev/slimevr/protocol/rpc/RPCHandler.kt +++ b/server/core/src/main/java/dev/slimevr/protocol/rpc/RPCHandler.kt @@ -2,9 +2,11 @@ package dev.slimevr.protocol.rpc import com.google.flatbuffers.FlatBufferBuilder import dev.slimevr.autobone.errors.BodyProportionError +import dev.slimevr.config.config import dev.slimevr.protocol.GenericConnection import dev.slimevr.protocol.ProtocolAPI import dev.slimevr.protocol.ProtocolHandler +import dev.slimevr.protocol.datafeed.DataFeedBuilder import dev.slimevr.protocol.rpc.autobone.RPCAutoBoneHandler import dev.slimevr.protocol.rpc.reset.RPCResetHandler import dev.slimevr.protocol.rpc.serial.RPCProvisioningHandler @@ -20,33 +22,14 @@ import dev.slimevr.tracking.trackers.TrackerPosition.Companion.getByBodyPart import dev.slimevr.tracking.trackers.TrackerUtils.getTrackerForSkeleton import io.eiren.util.logging.LogManager import io.github.axisangles.ktmath.Quaternion +import kotlinx.coroutines.* import solarxr_protocol.MessageBundle import solarxr_protocol.datatypes.TransactionId -import solarxr_protocol.rpc.AssignTrackerRequest -import solarxr_protocol.rpc.ChangeSkeletonConfigRequest -import solarxr_protocol.rpc.ClearDriftCompensationRequest -import solarxr_protocol.rpc.ClearMountingResetRequest -import solarxr_protocol.rpc.HeightResponse -import solarxr_protocol.rpc.LegTweaksTmpChange -import solarxr_protocol.rpc.LegTweaksTmpClear -import solarxr_protocol.rpc.OverlayDisplayModeChangeRequest -import solarxr_protocol.rpc.OverlayDisplayModeResponse -import solarxr_protocol.rpc.RecordBVHRequest -import solarxr_protocol.rpc.RecordBVHStatus -import solarxr_protocol.rpc.ResetRequest -import solarxr_protocol.rpc.ResetType -import solarxr_protocol.rpc.RpcMessage -import solarxr_protocol.rpc.RpcMessageHeader -import solarxr_protocol.rpc.ServerInfosResponse -import solarxr_protocol.rpc.SetPauseTrackingRequest -import solarxr_protocol.rpc.SkeletonConfigRequest -import solarxr_protocol.rpc.SkeletonResetAllRequest -import solarxr_protocol.rpc.StatusSystemRequest -import solarxr_protocol.rpc.StatusSystemResponse -import solarxr_protocol.rpc.StatusSystemResponseT +import solarxr_protocol.rpc.* class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler() { private var currTransactionId: Long = 0 + private val mainScope = CoroutineScope(SupervisorJob()) init { RPCResetHandler(this, api) @@ -197,6 +180,18 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler + this.onMagToggleRequest(conn, messageHeader) + } + + registerPacketListener( + RpcMessage.ChangeMagToggleRequest, + ) { conn: GenericConnection, messageHeader: RpcMessageHeader -> + this.onChangeMagToggleRequest(conn, messageHeader) + } } private fun onServerInfosRequest( @@ -478,7 +473,92 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler, + var ran: Boolean = false, + ) + + private val queues: MutableMap, Deque> = ConcurrentHashMap() + suspend fun setConfigFlag(device: UDPDevice, configTypeId: ConfigTypeId, state: Boolean, sensorId: Int = 255) { + if (device.timedOut) return + val triple = Triple(device.address, configTypeId, sensorId) + val queue = queues.computeIfAbsent(triple) { _ -> ConcurrentLinkedDeque() } + + suspendCancellableCoroutine { + val waiter = ConfigStateWaiter(state, it) + queue.add(waiter) + it.invokeOnCancellation { + queue.remove(waiter) + } + } + } + + private fun actualSetConfigFlag(device: UDPDevice, configTypeId: ConfigTypeId, state: Boolean, sensorId: Int) { + val packet = UDPPacket25SetConfigFlag(sensorId, configTypeId, state) + bb.limit(bb.capacity()) + bb.rewind() + parser.write(bb, null, packet) + socket.send(DatagramPacket(rcvBuffer, bb.position(), device.address)) } override fun run() { @@ -243,6 +300,19 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker parser.parse(bb, connection) .filterNotNull() .forEach { processPacket(received, it, connection) } + + queues.forEach { (t, p) -> + val q = p.firstOrNull() ?: return@forEach + if (q.ran) return@forEach + + val device = connectionsByAddress[t.first] ?: run { + p.removeFirst() + LogManager.info("[TrackerServer] Device ${t.first} not connected, so can't communicate with it") + return@forEach + } + actualSetConfigFlag(device, t.second, q.expectedState, t.third) + if (!device.timedOut) q.ran = true + } } catch (ignored: SocketTimeoutException) { } catch (e: Exception) { LogManager.warning( @@ -311,7 +381,7 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker private fun processPacket(received: DatagramPacket, packet: UDPPacket, connection: UDPDevice?) { val tracker: Tracker? when (packet) { - is UDPPacket0Heartbeat, is UDPPacket1Heartbeat -> {} + is UDPPacket0Heartbeat, is UDPPacket1Heartbeat, is UDPPacket25SetConfigFlag -> {} is UDPPacket3Handshake -> setUpNewConnection(received, packet) @@ -402,14 +472,14 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker is UDPPacket15SensorInfo -> { if (connection == null) return - setUpSensor(connection, packet.sensorId, packet.sensorType, packet.sensorStatus) + setUpSensor(connection, packet.sensorId, packet.sensorType, packet.sensorStatus, packet.sensorConfig.magStatus) // Send ack bb.limit(bb.capacity()) bb.rewind() parser.writeSensorInfoResponse(bb, connection, packet) socket.send(DatagramPacket(rcvBuffer, bb.position(), connection.address)) LogManager.info( - "[TrackerServer] Sensor info for ${connection.descriptiveName}/${packet.sensorId}: ${packet.sensorStatus}", + "[TrackerServer] Sensor info for ${connection.descriptiveName}/${packet.sensorId}: ${packet.sensorStatus}, mag ${packet.sensorConfig.magStatus}", ) } @@ -418,8 +488,7 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker } is UDPPacket20Temperature -> { - tracker = connection?.getTracker(packet.sensorId) - if (tracker == null) return + tracker = connection?.getTracker(packet.sensorId) ?: return tracker.temperature = packet.temperature } @@ -455,7 +524,7 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker } LogManager.info( - "[TrackerServer] User action from ${connection.descriptiveName } received. $name performed.", + "[TrackerServer] User action from ${connection.descriptiveName} received. $name performed.", ) } @@ -469,6 +538,22 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker connection.firmwareFeatures = packet.firmwareFeatures } + is UDPPacket24AckConfigChange -> { + if (connection == null) return + val queue = queues[Triple(connection.address, packet.configType, packet.sensorId)] ?: run { + LogManager.severe("[TrackerServer] Error, acknowledgment of config change that we don't have in our queue.") + return + } + val changed = queue.removeFirst() + changed.channel.resume(true) + val trackers = if (SensorSpecificPacket.isGlobal(packet.sensorId)) { + connection.trackers.values.toList() + } else { + listOf(connection.getTracker(packet.sensorId) ?: return) + } + LogManager.info("[TrackerServer] Acknowledged config change on ${connection.descriptiveName} (${trackers.map { it.trackerNum }.joinToString()}). Config changed on ${packet.configType}") + } + is UDPPacket200ProtocolChange -> {} } } diff --git a/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPDevice.kt b/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPDevice.kt index afc0a56d79..7407657685 100644 --- a/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPDevice.kt +++ b/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPDevice.kt @@ -1,6 +1,7 @@ package dev.slimevr.tracking.trackers.udp import dev.slimevr.NetworkProtocol +import dev.slimevr.VRServer import dev.slimevr.tracking.trackers.Device import dev.slimevr.tracking.trackers.Tracker import java.net.InetAddress @@ -13,7 +14,7 @@ class UDPDevice( override val hardwareIdentifier: String, override val boardType: BoardType = BoardType.UNKNOWN, override val mcuType: MCUType = MCUType.UNKNOWN, -) : Device() { +) : Device(true) { override val id: Int = nextLocalDeviceId.incrementAndGet() @@ -53,6 +54,17 @@ class UDPDevice( var timedOut = false override val trackers = ConcurrentHashMap() + override suspend fun setMag(state: Boolean, sensorId: Int) { + if (sensorId == 255) { + VRServer.instance.trackersServer.setConfigFlag(this, ConfigTypeId(1u), state) + trackers.forEach { (_, t) -> t.setMagPrivate(state) } + } else { + require(trackers[sensorId] != null) { "There is no tracker $sensorId in device ${toString()}" } + VRServer.instance.trackersServer.setConfigFlag(this, ConfigTypeId(1u), state, sensorId) + trackers[sensorId]!!.setMagPrivate(state) + } + } + var firmwareFeatures = FirmwareFeatures() fun isNextPacket(packetId: Long): Boolean { diff --git a/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPPacket.kt b/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPPacket.kt index 6691602acd..50fe7825b2 100644 --- a/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPPacket.kt +++ b/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPPacket.kt @@ -223,6 +223,7 @@ data class UDPPacket14Error(var errorNumber: Int = 0) : data class UDPPacket15SensorInfo( var sensorStatus: Int = 0, var sensorType: IMUType = IMUType.UNKNOWN, + var sensorConfig: SensorConfig = SensorConfig(0u), ) : UDPPacket(15), SensorSpecificPacket { override var sensorId = 0 @@ -233,6 +234,9 @@ data class UDPPacket15SensorInfo( sensorType = IMUType.getById(buf.get().toUInt() and 0xFFu) ?: IMUType.UNKNOWN } + if (buf.remaining() > 0) { + sensorConfig = SensorConfig(buf.getShort().toUShort()) + } } companion object { @@ -350,6 +354,30 @@ data class UDPPacket23RotationAndAcceleration( } } +data class UDPPacket24AckConfigChange( + override var sensorId: Int = 0, + var configType: ConfigTypeId = ConfigTypeId(0u), +) : UDPPacket(24), + SensorSpecificPacket { + override fun readData(buf: ByteBuffer) { + sensorId = buf.get().toInt() and 0xFF + configType = ConfigTypeId(buf.getShort().toUShort()) + } +} + +data class UDPPacket25SetConfigFlag( + override var sensorId: Int = 255, + var configType: ConfigTypeId, + var state: Boolean, +) : UDPPacket(25), + SensorSpecificPacket { + override fun writeData(buf: ByteBuffer) { + buf.put(sensorId.toByte()) + buf.putShort(configType.v.toShort()) + buf.put(if (state) 1 else 0) + } +} + data class UDPPacket200ProtocolChange( var targetProtocol: Int = 0, var targetProtocolVersion: Int = 0, diff --git a/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPProtocolParser.kt b/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPProtocolParser.kt index e0c7e3e0f7..9f359226c9 100644 --- a/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPProtocolParser.kt +++ b/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/UDPProtocolParser.kt @@ -114,6 +114,7 @@ class UDPProtocolParser { PACKET_TEMPERATURE -> UDPPacket20Temperature() PACKET_USER_ACTION -> UDPPacket21UserAction() PACKET_FEATURE_FLAGS -> UDPPacket22FeatureFlags() + PACKET_ACK_CONFIG_CHANGE -> UDPPacket24AckConfigChange() PACKET_PROTOCOL_CHANGE -> UDPPacket200ProtocolChange() else -> null } @@ -147,6 +148,8 @@ class UDPProtocolParser { const val PACKET_USER_ACTION = 21 const val PACKET_FEATURE_FLAGS = 22 const val PACKET_ROTATION_AND_ACCELERATION = 23 + const val PACKET_ACK_CONFIG_CHANGE = 24 + const val PACKET_SET_CONFIG_FLAG = 25 const val PACKET_BUNDLE = 100 const val PACKET_BUNDLE_COMPACT = 101 const val PACKET_PROTOCOL_CHANGE = 200 diff --git a/solarxr-protocol b/solarxr-protocol index 60f3146f91..73fca6300d 160000 --- a/solarxr-protocol +++ b/solarxr-protocol @@ -1 +1 @@ -Subproject commit 60f3146f914e2a9e35d759a8146be213e715d6d2 +Subproject commit 73fca6300d70ee5e73258ce98a7dbae4d63b9cc8