Skip to content

Commit

Permalink
Add a TrackerStatus.TIMED_OUT for temporal timeouts (#903)
Browse files Browse the repository at this point in the history
  • Loading branch information
ImUrX committed Dec 8, 2023
1 parent 99d0756 commit c3b4727
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 29 deletions.
1 change: 1 addition & 0 deletions gui/public/i18n/en/translation.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion gui/src/components/TopBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
RpcMessage,
ServerInfosRequestT,
ServerInfosResponseT,
TrackerStatus,
} from 'solarxr-protocol';
import { useWebsocketAPI } from '@/hooks/websocket-api';
import { CloseIcon } from './commons/icon/CloseIcon';
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions gui/src/components/tracker/TrackerStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 } = {
Expand All @@ -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 }) {
Expand Down
16 changes: 13 additions & 3 deletions server/core/src/main/java/dev/slimevr/tracking/trackers/Tracker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand All @@ -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).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -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()
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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()
) {
Expand All @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ class UDPProtocolParser {
)
}
connection.lastPacket = System.currentTimeMillis()
connection.trackers.forEach { (_, tracker) ->
tracker.heartbeat()
}
}
if (packetId == PACKET_BUNDLE) {
bundlePackets.clear()
Expand Down

0 comments on commit c3b4727

Please sign in to comment.