mirror of
https://github.com/SlimeVR/SlimeVR-Server.git
synced 2026-04-06 02:01:58 +02:00
Add a TrackerStatus.TIMED_OUT for temporal timeouts (#903)
This commit is contained in:
@@ -139,6 +139,7 @@ tracker-status-error = Error
|
||||
tracker-status-disconnected = Disconnected
|
||||
tracker-status-occluded = Occluded
|
||||
tracker-status-ok = OK
|
||||
tracker-status-timed_out = Timed out
|
||||
|
||||
## Tracker status columns
|
||||
tracker-table-column-name = Name
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
RpcMessage,
|
||||
ServerInfosRequestT,
|
||||
ServerInfosResponseT,
|
||||
TrackerStatus,
|
||||
} from 'solarxr-protocol';
|
||||
import { useWebsocketAPI } from '@/hooks/websocket-api';
|
||||
import { CloseIcon } from './commons/icon/CloseIcon';
|
||||
@@ -212,7 +213,9 @@ export function TopBar({
|
||||
onClick={() => {
|
||||
if (
|
||||
config?.connectedTrackersWarning &&
|
||||
connectedIMUTrackers.length > 0
|
||||
connectedIMUTrackers.filter(
|
||||
(t) => t.tracker.status !== TrackerStatus.TIMED_OUT
|
||||
).length > 0
|
||||
) {
|
||||
setConnectedTrackerWarning(true);
|
||||
} else {
|
||||
|
||||
@@ -11,6 +11,7 @@ const statusLabelMap: { [key: number]: string } = {
|
||||
[TrackerStatusEnum.DISCONNECTED]: 'tracker-status-disconnected',
|
||||
[TrackerStatusEnum.OCCLUDED]: 'tracker-status-occluded',
|
||||
[TrackerStatusEnum.OK]: 'tracker-status-ok',
|
||||
[TrackerStatusEnum.TIMED_OUT]: 'tracker-status-timed_out',
|
||||
};
|
||||
|
||||
const statusClassMap: { [key: number]: string } = {
|
||||
@@ -20,6 +21,7 @@ const statusClassMap: { [key: number]: string } = {
|
||||
[TrackerStatusEnum.DISCONNECTED]: 'bg-background-30',
|
||||
[TrackerStatusEnum.OCCLUDED]: 'bg-status-warning',
|
||||
[TrackerStatusEnum.OK]: 'bg-status-success',
|
||||
[TrackerStatusEnum.TIMED_OUT]: 'bg-status-warning',
|
||||
};
|
||||
|
||||
export function TrackerStatus({ status }: { status: number }) {
|
||||
|
||||
@@ -15,7 +15,8 @@ import solarxr_protocol.rpc.StatusTrackerErrorT
|
||||
import solarxr_protocol.rpc.StatusTrackerResetT
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
const val TIMEOUT_MS = 2000L
|
||||
const val TIMEOUT_MS = 2_000L
|
||||
const val DISCONNECT_MS = 3_000L + TIMEOUT_MS
|
||||
|
||||
/**
|
||||
* Generic tracker class for input and output tracker,
|
||||
@@ -65,7 +66,7 @@ class Tracker @JvmOverloads constructor(
|
||||
val needsMounting: Boolean = false,
|
||||
) {
|
||||
private val timer = BufferedTimer(1f)
|
||||
private var timeAtLastUpdate: Long = 0
|
||||
private var timeAtLastUpdate: Long = System.currentTimeMillis()
|
||||
private var rotation = Quaternion.IDENTITY
|
||||
private var acceleration = Vector3.NULL
|
||||
var position = Vector3.NULL
|
||||
@@ -254,8 +255,10 @@ class Tracker @JvmOverloads constructor(
|
||||
*/
|
||||
fun tick() {
|
||||
if (usesTimeout) {
|
||||
if (System.currentTimeMillis() - timeAtLastUpdate > TIMEOUT_MS) {
|
||||
if (System.currentTimeMillis() - timeAtLastUpdate > DISCONNECT_MS) {
|
||||
status = TrackerStatus.DISCONNECTED
|
||||
} else if (System.currentTimeMillis() - timeAtLastUpdate > TIMEOUT_MS) {
|
||||
status = TrackerStatus.TIMED_OUT
|
||||
}
|
||||
}
|
||||
filteringHandler.tick()
|
||||
@@ -270,6 +273,13 @@ class Tracker @JvmOverloads constructor(
|
||||
filteringHandler.dataTick(rotation)
|
||||
}
|
||||
|
||||
/**
|
||||
* A way to delay the timeout of the tracker
|
||||
*/
|
||||
fun heartbeat() {
|
||||
timeAtLastUpdate = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the adjusted tracker rotation after all corrections
|
||||
* (filtering, reset, mounting and drift compensation).
|
||||
|
||||
@@ -7,11 +7,12 @@ enum class TrackerStatus(val id: Int, val sendData: Boolean, val reset: Boolean)
|
||||
BUSY(2, true, false),
|
||||
ERROR(3, false, true),
|
||||
OCCLUDED(4, false, false),
|
||||
TIMED_OUT(5, false, false),
|
||||
;
|
||||
|
||||
companion object {
|
||||
|
||||
private val byId = values().associateBy { it.id }
|
||||
private val byId = entries.associateBy { it.id }
|
||||
|
||||
@JvmStatic
|
||||
fun getById(id: Int): TrackerStatus? = byId[id]
|
||||
|
||||
@@ -5,7 +5,10 @@ object TrackerUtils {
|
||||
/**
|
||||
* Finds a suitable tracker for use in the SlimeVR skeleton
|
||||
* in allTrackers matching the position.
|
||||
* This won't return disconnected, errored or internal trackers.
|
||||
*
|
||||
* This won't return disconnected, errored or internal trackers,
|
||||
* but it will return timed out trackers, and they have a lower
|
||||
* priority than normal trackers.
|
||||
*
|
||||
* @return A tracker for use in the SlimeVR skeleton
|
||||
*/
|
||||
@@ -19,50 +22,64 @@ object TrackerUtils {
|
||||
|
||||
/**
|
||||
* Finds the first non-internal tracker from allTrackers
|
||||
* matching the position, that is not DISCONNECTED
|
||||
* matching the position, that is not `TrackerStatus.reset`.
|
||||
* It will also choose timed out trackers, but they have a lower
|
||||
* priority than the rest of the trackers
|
||||
*
|
||||
* @return A non-internal tracker
|
||||
*/
|
||||
private fun getNonInternalTrackerForBodyPosition(
|
||||
allTrackers: List<Tracker>,
|
||||
position: TrackerPosition,
|
||||
): Tracker? = allTrackers.firstOrNull {
|
||||
it.trackerPosition === position &&
|
||||
!it.isInternal &&
|
||||
!it.status.reset
|
||||
): Tracker? {
|
||||
val resetTrackers = allTrackers.filter {
|
||||
it.trackerPosition === position &&
|
||||
!it.isInternal &&
|
||||
!it.status.reset
|
||||
}
|
||||
return resetTrackers.firstOrNull { it.status != TrackerStatus.TIMED_OUT } ?: resetTrackers.firstOrNull()
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the first non-internal non-computed tracker from allTrackers
|
||||
* matching the position, that is not DISCONNECTED.
|
||||
* matching the position, that is not `TrackerStatus.reset`.
|
||||
* It will also choose timed out trackers, but they have a lower
|
||||
* priority than the rest of the trackers
|
||||
*
|
||||
* @return A non-internal non-computed tracker
|
||||
*/
|
||||
private fun getNonInternalNonComputedTrackerForBodyPosition(
|
||||
allTrackers: List<Tracker>,
|
||||
position: TrackerPosition,
|
||||
): Tracker? = allTrackers.firstOrNull {
|
||||
it.trackerPosition === position &&
|
||||
!it.isComputed &&
|
||||
!it.isInternal &&
|
||||
!it.status.reset
|
||||
): Tracker? {
|
||||
val resetTrackers = allTrackers.filter {
|
||||
it.trackerPosition === position &&
|
||||
!it.isComputed &&
|
||||
!it.isInternal &&
|
||||
!it.status.reset
|
||||
}
|
||||
return resetTrackers.firstOrNull { it.status != TrackerStatus.TIMED_OUT } ?: resetTrackers.firstOrNull()
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the first non-internal and non-imu tracker from allTrackers
|
||||
* matching the position, that is not DISCONNECTED.
|
||||
*
|
||||
* matching the position, that is not `TrackerStatus.reset`.
|
||||
* It will also choose timed out trackers, but they have a lower
|
||||
* priority than the rest of the trackers
|
||||
* @return A non-internal non-imu tracker
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getNonInternalNonImuTrackerForBodyPosition(
|
||||
allTrackers: List<Tracker>,
|
||||
position: TrackerPosition,
|
||||
): Tracker? = allTrackers.firstOrNull {
|
||||
it.trackerPosition === position &&
|
||||
!it.isImu() &&
|
||||
!it.isInternal &&
|
||||
!it.status.reset
|
||||
): Tracker? {
|
||||
val resetTrackers = allTrackers.filter {
|
||||
it.trackerPosition === position &&
|
||||
!it.isImu() &&
|
||||
!it.isInternal &&
|
||||
!it.status.reset
|
||||
}
|
||||
return resetTrackers.firstOrNull { it.status != TrackerStatus.TIMED_OUT } ?: resetTrackers.firstOrNull()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -188,7 +188,8 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
|
||||
imuType = sensorType,
|
||||
allowFiltering = true,
|
||||
needsReset = true,
|
||||
needsMounting = true
|
||||
needsMounting = true,
|
||||
usesTimeout = true
|
||||
)
|
||||
connection.trackers[trackerId] = imuTracker
|
||||
trackersConsumer.accept(imuTracker)
|
||||
@@ -244,16 +245,21 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
|
||||
parser.write(bb, conn, UDPPacket1Heartbeat)
|
||||
socket.send(DatagramPacket(rcvBuffer, bb.position(), conn.address))
|
||||
if (conn.lastPacket + 1000 < System.currentTimeMillis()) {
|
||||
for (value in conn.trackers.values) {
|
||||
value.status = TrackerStatus.DISCONNECTED
|
||||
}
|
||||
if (!conn.timedOut) {
|
||||
conn.timedOut = true
|
||||
LogManager.info("[TrackerServer] Tracker timed out: $conn")
|
||||
}
|
||||
} else {
|
||||
for (value in conn.trackers.values) {
|
||||
if (value.status == TrackerStatus.DISCONNECTED ||
|
||||
value.status == TrackerStatus.TIMED_OUT
|
||||
) {
|
||||
value.status = TrackerStatus.OK
|
||||
}
|
||||
}
|
||||
conn.timedOut = false
|
||||
}
|
||||
|
||||
if (conn.serialBuffer.isNotEmpty() &&
|
||||
conn.lastSerialUpdate + 500L < System.currentTimeMillis()
|
||||
) {
|
||||
@@ -266,6 +272,7 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
|
||||
serialBuffer2.setLength(0)
|
||||
conn.serialBuffer.setLength(0)
|
||||
}
|
||||
|
||||
if (conn.lastPingPacketTime + 500 < System.currentTimeMillis()) {
|
||||
conn.lastPingPacketId = random.nextInt()
|
||||
conn.lastPingPacketTime = System.currentTimeMillis()
|
||||
|
||||
@@ -17,6 +17,9 @@ class UDPProtocolParser {
|
||||
)
|
||||
}
|
||||
connection.lastPacket = System.currentTimeMillis()
|
||||
connection.trackers.forEach { (_, tracker) ->
|
||||
tracker.heartbeat()
|
||||
}
|
||||
}
|
||||
if (packetId == PACKET_BUNDLE) {
|
||||
bundlePackets.clear()
|
||||
|
||||
Submodule solarxr-protocol updated: cec1f04c48...8910f60c6a
Reference in New Issue
Block a user