From c12e1dd93f12ff364c76ee704fb0eb04a34977e4 Mon Sep 17 00:00:00 2001 From: Jabberrock Date: Mon, 11 Nov 2024 12:35:29 -0800 Subject: [PATCH] Enables a tracker to forward log messages to the server, and the server to send the log messages to the UI. 1. `PROTOCOL_LOG_SUPPORT` which determines if the server supports forwarding log messages 2. `PACKET_LOG (26)` which is the UDP packet that the tracker sends to the server 3. `Device.logMessages` which is the new field for the UI to receive log messages --- gui/src/components/tracker/DeviceLogs.tsx | 54 +++++++++++++++++++ .../components/tracker/TrackerSettings.tsx | 7 +++ .../protocol/datafeed/DataFeedBuilder.java | 18 +++++++ .../dev/slimevr/tracking/trackers/Device.kt | 2 + .../tracking/trackers/udp/FeatureFlags.kt | 4 ++ .../trackers/udp/TrackersUDPServer.kt | 6 +++ .../tracking/trackers/udp/UDPPacket.kt | 7 +++ .../trackers/udp/UDPProtocolParser.kt | 2 + 8 files changed, 100 insertions(+) create mode 100644 gui/src/components/tracker/DeviceLogs.tsx diff --git a/gui/src/components/tracker/DeviceLogs.tsx b/gui/src/components/tracker/DeviceLogs.tsx new file mode 100644 index 0000000000..3e8cce0392 --- /dev/null +++ b/gui/src/components/tracker/DeviceLogs.tsx @@ -0,0 +1,54 @@ +import { useEffect, useRef, memo, Fragment } from 'react'; + +type LogProps = { + messages: string[] +}; + +function Log({ messages } : LogProps) { + const preRef = useRef(null); + const lastScrollTopRef = useRef(0); + + useEffect(() => { + const pre = preRef.current; + const lastScrollTop = lastScrollTopRef.current; + // Scroll to the latest message if either: + // - We were looking at the latest message previously; or + // - The scroll height shrunk, e.g. when logs are cleared due to device reconnecting + if (pre && (pre.scrollTop >= lastScrollTop || lastScrollTop >= pre.scrollHeight)) { + pre.scrollTop = pre.scrollHeight; + lastScrollTopRef.current = pre.scrollTop; + } + }, [messages]); + + return ( +
+      {messages.length === 0 && (
+        <>[No log messages]
+      )}
+      {messages.length > 0 &&
+        messages.map((msg, index) => (
+          
+              {msg}
+              
+
+ ))} +
+ ) +} + +// Only render if the log messages have actually changed +const LogMemo = memo(Log, (a, b) => { + return ( + a.messages.length === b.messages.length && + a.messages.every((e, i) => b.messages[i] === e)); +}); + +export { + LogMemo as DeviceLog +}; diff --git a/gui/src/components/tracker/TrackerSettings.tsx b/gui/src/components/tracker/TrackerSettings.tsx index 8a1f1f7a57..8c17e1044a 100644 --- a/gui/src/components/tracker/TrackerSettings.tsx +++ b/gui/src/components/tracker/TrackerSettings.tsx @@ -35,6 +35,7 @@ import { TrackerCard } from './TrackerCard'; import { Quaternion } from 'three'; import { useAppContext } from '@/hooks/app'; import { MagnetometerToggleSetting } from '@/components/settings/pages/MagnetometerToggleSetting'; +import { DeviceLog } from './DeviceLogs'; const rotationsLabels: [Quaternion, string][] = [ [rotationToQuatMap.BACK, 'tracker-rotation-back'], @@ -469,6 +470,12 @@ export function TrackerSettingsPage() { )} +
+ + Device Logs + + +
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 3eb480af0d..299b456398 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 @@ -259,6 +259,21 @@ public static int createTrackersData( return fbb.endVector(); } + public static int createLogMessagesData( + FlatBufferBuilder fbb, + Device device + ) { + List messages = device.getLogMessages(); + + int numMessages = messages.size(); + int[] messageOffsets = new int[numMessages]; + for (int i = 0; i < numMessages; ++i) { + messageOffsets[i] = fbb.createString(messages.get(i)); + } + + return DeviceData.createLogMessagesVector(fbb, messageOffsets); + } + public static int createDeviceData( FlatBufferBuilder fbb, int id, @@ -306,12 +321,15 @@ public static int createDeviceData( ? fbb.createString(device.getName()) : 0; + int logMessagesOffset = DataFeedBuilder.createLogMessagesData(fbb, device); + DeviceData.startDeviceData(fbb); DeviceData.addCustomName(fbb, nameOffset); DeviceData.addId(fbb, DeviceId.createDeviceId(fbb, id)); DeviceData.addHardwareStatus(fbb, hardwareDataOffset); DeviceData.addHardwareInfo(fbb, hardwareInfoOffset); DeviceData.addTrackers(fbb, trackersOffset); + DeviceData.addLogMessages(fbb, logMessagesOffset); return DeviceData.endDeviceData(fbb); } diff --git a/server/core/src/main/java/dev/slimevr/tracking/trackers/Device.kt b/server/core/src/main/java/dev/slimevr/tracking/trackers/Device.kt index 4fca49688a..d360ca6067 100644 --- a/server/core/src/main/java/dev/slimevr/tracking/trackers/Device.kt +++ b/server/core/src/main/java/dev/slimevr/tracking/trackers/Device.kt @@ -25,6 +25,8 @@ open class Device(val magSupport: Boolean = false) { open val hardwareIdentifier: String = "Unknown" + open var logMessages: MutableList = mutableListOf() + val isOpenVrDevice: Boolean get() = manufacturer == "OpenVR" diff --git a/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/FeatureFlags.kt b/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/FeatureFlags.kt index 640af58078..5abbd02422 100644 --- a/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/FeatureFlags.kt +++ b/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/FeatureFlags.kt @@ -52,6 +52,9 @@ enum class ServerFeatureFlags { - `PACKET_ROTATION_AND_ACCELERATION` = 23 (0x17). */ PROTOCOL_BUNDLE_COMPACT_SUPPORT, + /** Server can receive log messages: `PACKET_LOG` = 102 (0x66). */ + PROTOCOL_LOG_SUPPORT, + // Add new flags here BITS_TOTAL, ; @@ -60,6 +63,7 @@ enum class ServerFeatureFlags { val flagsEnabled: Set = setOf( PROTOCOL_BUNDLE_SUPPORT, PROTOCOL_BUNDLE_COMPACT_SUPPORT, + PROTOCOL_LOG_SUPPORT, // Add enabled flags here ) diff --git a/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/TrackersUDPServer.kt b/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/TrackersUDPServer.kt index d97def7b9a..8a16553a59 100644 --- a/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/TrackersUDPServer.kt +++ b/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/TrackersUDPServer.kt @@ -181,6 +181,7 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker connection } connection.firmwareFeatures = FirmwareFeatures() + connection.logMessages.clear() bb.limit(bb.capacity()) bb.rewind() parser.writeHandshakeResponse(bb, connection) @@ -561,6 +562,11 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker LogManager.info("[TrackerServer] Acknowledged config change on ${connection.descriptiveName} (${trackers.map { it.trackerNum }.joinToString()}). Config changed on ${packet.configType}") } + is UDPPacket26Log -> { + if (connection == null) return + connection.logMessages.add(packet.message) + } + is UDPPacket200ProtocolChange -> {} } } 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 1f40d979e7..da1724de28 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 @@ -378,6 +378,13 @@ data class UDPPacket25SetConfigFlag( } } +data class UDPPacket26Log(var message: String = "") : UDPPacket(26) { + override fun readData(buf: ByteBuffer) { + val length = buf.get().toUByte().toInt() + message = readASCIIString(buf, length) + } +} + 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 9f359226c9..6e7331501b 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 @@ -115,6 +115,7 @@ class UDPProtocolParser { PACKET_USER_ACTION -> UDPPacket21UserAction() PACKET_FEATURE_FLAGS -> UDPPacket22FeatureFlags() PACKET_ACK_CONFIG_CHANGE -> UDPPacket24AckConfigChange() + PACKET_LOG -> UDPPacket26Log() PACKET_PROTOCOL_CHANGE -> UDPPacket200ProtocolChange() else -> null } @@ -150,6 +151,7 @@ class UDPProtocolParser { const val PACKET_ROTATION_AND_ACCELERATION = 23 const val PACKET_ACK_CONFIG_CHANGE = 24 const val PACKET_SET_CONFIG_FLAG = 25 + const val PACKET_LOG = 26 const val PACKET_BUNDLE = 100 const val PACKET_BUNDLE_COMPACT = 101 const val PACKET_PROTOCOL_CHANGE = 200