mirror of
https://github.com/SlimeVR/SlimeVR-Server.git
synced 2026-04-06 02:01:58 +02:00
Compare commits
16 Commits
bscotch/re
...
feat_flash
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e89e64e27 | ||
|
|
bd6d9b693a | ||
|
|
57d74747c2 | ||
|
|
0a493ac345 | ||
|
|
17bb2703d1 | ||
|
|
f0981bf709 | ||
|
|
99de554c18 | ||
|
|
f95a4d56d7 | ||
|
|
1df3c9d322 | ||
|
|
e0838cce6c | ||
|
|
e25d3201c2 | ||
|
|
5d14f14139 | ||
|
|
09e81f5ace | ||
|
|
8f57ef2de4 | ||
|
|
ea242960b3 | ||
|
|
35ac14a7de |
@@ -336,6 +336,7 @@ tracker-table-column-name = Name
|
||||
tracker-table-column-type = Type
|
||||
tracker-table-column-battery = Battery
|
||||
tracker-table-column-ping = Ping
|
||||
tracker-table-column-packet_loss = Packet Loss
|
||||
tracker-table-column-tps = TPS
|
||||
tracker-table-column-temperature = Temp. °C
|
||||
tracker-table-column-linear-acceleration = Accel. X/Y/Z
|
||||
@@ -375,6 +376,10 @@ tracker-infos-magnetometer-status-v1 = { $status ->
|
||||
[ENABLED] Enabled
|
||||
}
|
||||
|
||||
tracker-infos-packet_loss = Packet Loss
|
||||
tracker-infos-packets_lost = Packets Lost
|
||||
tracker-infos-packets_received = Packets Received
|
||||
|
||||
## Tracker settings
|
||||
tracker-settings-back = Go back to trackers list
|
||||
tracker-settings-title = Tracker settings
|
||||
|
||||
@@ -59,14 +59,14 @@ export function PersonFrontIcon({ mirror = true }: { mirror?: boolean }) {
|
||||
/>
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="81.5"
|
||||
cx="82"
|
||||
cy="80"
|
||||
r={CIRCLE_RADIUS}
|
||||
id={BodyPart[BodyPart.NECK]}
|
||||
/>
|
||||
<circle
|
||||
className="body-part-circle"
|
||||
cx="81.5"
|
||||
cx="82"
|
||||
cy="35"
|
||||
r={CIRCLE_RADIUS}
|
||||
id={BodyPart[BodyPart.HEAD]}
|
||||
|
||||
@@ -11,6 +11,9 @@ export function BatteryIcon({
|
||||
charging: boolean;
|
||||
}) {
|
||||
const col = useMemo(() => {
|
||||
if (disabled) return 'fill-background-40';
|
||||
else if (charging) return 'fill-status-success';
|
||||
|
||||
const colorsMap: { [key: number]: string } = {
|
||||
0.4: 'fill-status-success',
|
||||
0.2: 'fill-status-warning',
|
||||
@@ -20,10 +23,8 @@ export function BatteryIcon({
|
||||
const val = Object.keys(colorsMap)
|
||||
.filter((key) => +key < value)
|
||||
.sort((a, b) => +b - +a)[0];
|
||||
return disabled
|
||||
? 'fill-background-40'
|
||||
: colorsMap[+val] || 'fill-background-10';
|
||||
}, [value, disabled]);
|
||||
return colorsMap[+val] || 'fill-background-10';
|
||||
}, [value, disabled, charging]);
|
||||
|
||||
return (
|
||||
<svg
|
||||
@@ -59,13 +60,21 @@ export function BatteryIcon({
|
||||
/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_4_39)" className={classNames(col, 'opacity-100')}>
|
||||
<rect width={value * 18} height="9" />
|
||||
<rect width={charging ? 18 : value * 18} height="9" />
|
||||
</g>
|
||||
{charging && (
|
||||
{charging && value <= 1 && (
|
||||
<path
|
||||
d="M 0.93561138,11.744353 2.4349252,6.1488377 H 0.0312815 L 3.5761014,0.00903018 2.2061799,5.1216451 h 2.4534885 z"
|
||||
d="M 7.7638355,8.4189633 8.0112251,4.9834646 5.7712838,4.9834645 8.5644084,0.07977871 8.3170195,3.5152773 H 10.55696 Z"
|
||||
fill="#081e30"
|
||||
transform="translate(5,-1)"
|
||||
/>
|
||||
)}
|
||||
{charging && value > 1 && (
|
||||
<path
|
||||
d="M 5.5342464,4.6225095 C 6.1777799,5.0106205 6.6131537,5.2516456 7.5253371,6.545223 8.4340868,4.4016445 8.7809738,3.661475 10.605195,1.5520288"
|
||||
fill="none"
|
||||
stroke="#081e30"
|
||||
strokeWidth={1.5}
|
||||
strokeLinecap="square"
|
||||
/>
|
||||
)}
|
||||
</svg>
|
||||
|
||||
@@ -231,7 +231,10 @@ function OTADevicesList({
|
||||
const allDevices = useAtomValue(devicesAtom);
|
||||
|
||||
const devices =
|
||||
allDevices.filter(({ trackers }) => {
|
||||
allDevices.filter(({ hardwareInfo, trackers }) => {
|
||||
// filter out devices we can't update
|
||||
if (!hardwareInfo?.officialBoardType) return false;
|
||||
|
||||
// if the device has no trackers it is prob misconfigured so we skip for safety
|
||||
if (trackers.length <= 0) return false;
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ export function VerifyResultsStep({
|
||||
hasRecording === ProcessStatus.FULFILLED && (
|
||||
<Typography>
|
||||
{l10n.getString(
|
||||
'onboarding-automatic-proportions-verify-results-processing'
|
||||
'onboarding-automatic_proportions-verify_results-processing'
|
||||
)}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
@@ -376,6 +376,37 @@ export function TrackerSettingsPage() {
|
||||
{tracker?.device?.hardwareInfo?.networkProtocolVersion || '--'}
|
||||
</Typography>
|
||||
</div>
|
||||
{tracker?.device?.hardwareStatus?.packetsReceived !== null && (
|
||||
<>
|
||||
<div className="flex justify-between">
|
||||
<Typography>
|
||||
{l10n.getString('tracker-infos-packet_loss')}
|
||||
</Typography>
|
||||
<Typography>
|
||||
{(
|
||||
(tracker?.device?.hardwareStatus?.packetLoss ?? 0) * 100
|
||||
).toFixed(0)}
|
||||
%
|
||||
</Typography>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<Typography>
|
||||
{l10n.getString('tracker-infos-packets_lost')}
|
||||
</Typography>
|
||||
<Typography>
|
||||
{tracker?.device?.hardwareStatus?.packetsLost ?? '0'}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<Typography>
|
||||
{l10n.getString('tracker-infos-packets_received')}
|
||||
</Typography>
|
||||
<Typography>
|
||||
{tracker?.device?.hardwareStatus?.packetsReceived ?? '0'}
|
||||
</Typography>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{tracker?.tracker && (
|
||||
<IMUVisualizerWidget tracker={tracker?.tracker} />
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
import { WifiIcon } from '@/components/commons/icon/WifiIcon';
|
||||
import { Typography } from '@/components/commons/Typography';
|
||||
import { Tooltip } from '@/components/commons/Tooltip';
|
||||
|
||||
export function TrackerWifi({
|
||||
rssi,
|
||||
ping,
|
||||
rssiShowNumeric,
|
||||
disabled,
|
||||
packetLoss,
|
||||
packetsLost,
|
||||
packetsReceived,
|
||||
showPacketLoss = false,
|
||||
textColor = 'primary',
|
||||
}: {
|
||||
rssi: number | null;
|
||||
ping: number | null;
|
||||
packetLoss?: number | null;
|
||||
packetsLost?: number | null;
|
||||
packetsReceived?: number | null;
|
||||
showPacketLoss?: boolean;
|
||||
rssiShowNumeric?: boolean;
|
||||
disabled?: boolean;
|
||||
textColor?: string;
|
||||
@@ -31,6 +40,17 @@ export function TrackerWifi({
|
||||
{rssi} dBm
|
||||
</Typography>
|
||||
)}
|
||||
{showPacketLoss && packetsReceived != null && (
|
||||
<Tooltip
|
||||
preferedDirection="top"
|
||||
content={<Typography id="tracker-infos-packet_loss" />}
|
||||
>
|
||||
<Typography
|
||||
color={textColor}
|
||||
whitespace="whitespace-nowrap"
|
||||
>{`${((packetLoss ?? 0) * 100).toFixed(0)}% (${packetsLost ?? 0} / ${packetsReceived})`}</Typography>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
)) || (
|
||||
<div className="flex flex-col justify-center w-12">
|
||||
|
||||
@@ -245,6 +245,10 @@ function Row({
|
||||
ping={device?.hardwareStatus?.ping}
|
||||
disabled={tracker.status === TrackerStatusEnum.DISCONNECTED}
|
||||
textColor={fontColor}
|
||||
showPacketLoss
|
||||
packetLoss={device.hardwareStatus.packetLoss}
|
||||
packetsLost={device.hardwareStatus.packetsLost}
|
||||
packetsReceived={device.hardwareStatus.packetsReceived}
|
||||
/>
|
||||
)}
|
||||
</Cell>
|
||||
|
||||
@@ -3,6 +3,7 @@ package dev.slimevr
|
||||
import com.melloware.jintellitype.HotkeyListener
|
||||
import com.melloware.jintellitype.JIntellitype
|
||||
import dev.slimevr.config.KeybindingsConfig
|
||||
import dev.slimevr.tracking.trackers.TrackerUtils
|
||||
import io.eiren.util.OperatingSystem
|
||||
import io.eiren.util.OperatingSystem.Companion.currentPlatform
|
||||
import io.eiren.util.ann.AWTThread
|
||||
@@ -37,6 +38,11 @@ class Keybinding @AWTThread constructor(val server: VRServer) : HotkeyListener {
|
||||
.registerHotKey(MOUNTING_RESET, mountingResetBinding)
|
||||
LogManager.info("[Keybinding] Bound reset mounting to $mountingResetBinding")
|
||||
|
||||
val feetMountingResetBinding = config.feetMountingResetBinding
|
||||
JIntellitype.getInstance()
|
||||
.registerHotKey(FEET_MOUNTING_RESET, feetMountingResetBinding)
|
||||
LogManager.info("[Keybinding] Bound feet reset mounting to $feetMountingResetBinding")
|
||||
|
||||
val pauseTrackingBinding = config.pauseTrackingBinding
|
||||
JIntellitype.getInstance()
|
||||
.registerHotKey(PAUSE_TRACKING, pauseTrackingBinding)
|
||||
@@ -63,6 +69,12 @@ class Keybinding @AWTThread constructor(val server: VRServer) : HotkeyListener {
|
||||
config.mountingResetDelay,
|
||||
)
|
||||
|
||||
FEET_MOUNTING_RESET -> server.scheduleResetTrackersMounting(
|
||||
RESET_SOURCE_NAME,
|
||||
config.feetMountingResetDelay,
|
||||
TrackerUtils.feetsBodyParts,
|
||||
)
|
||||
|
||||
PAUSE_TRACKING ->
|
||||
server
|
||||
.scheduleTogglePauseTracking(
|
||||
@@ -78,6 +90,7 @@ class Keybinding @AWTThread constructor(val server: VRServer) : HotkeyListener {
|
||||
private const val FULL_RESET = 1
|
||||
private const val YAW_RESET = 2
|
||||
private const val MOUNTING_RESET = 3
|
||||
private const val PAUSE_TRACKING = 4
|
||||
private const val FEET_MOUNTING_RESET = 4
|
||||
private const val PAUSE_TRACKING = 5
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ public class KeybindingsConfig {
|
||||
|
||||
private String mountingResetBinding = "CTRL+ALT+SHIFT+I";
|
||||
|
||||
private String feetMountingResetBinding = "CTRL+ALT+SHIFT+P";
|
||||
|
||||
private String pauseTrackingBinding = "CTRL+ALT+SHIFT+O";
|
||||
|
||||
private long fullResetDelay = 0L;
|
||||
@@ -16,6 +18,8 @@ public class KeybindingsConfig {
|
||||
|
||||
private long mountingResetDelay = 0L;
|
||||
|
||||
private long feetMountingResetDelay = 0L;
|
||||
|
||||
private long pauseTrackingDelay = 0L;
|
||||
|
||||
|
||||
@@ -34,6 +38,10 @@ public class KeybindingsConfig {
|
||||
return mountingResetBinding;
|
||||
}
|
||||
|
||||
public String getFeetMountingResetBinding() {
|
||||
return feetMountingResetBinding;
|
||||
}
|
||||
|
||||
public String getPauseTrackingBinding() {
|
||||
return pauseTrackingBinding;
|
||||
}
|
||||
@@ -62,6 +70,14 @@ public class KeybindingsConfig {
|
||||
mountingResetDelay = delay;
|
||||
}
|
||||
|
||||
public long getFeetMountingResetDelay() {
|
||||
return feetMountingResetDelay;
|
||||
}
|
||||
|
||||
public void setFeetMountingResetDelay(long delay) {
|
||||
feetMountingResetDelay = delay;
|
||||
}
|
||||
|
||||
public long getPauseTrackingDelay() {
|
||||
return pauseTrackingDelay;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import dev.slimevr.serial.SerialPort
|
||||
import dev.slimevr.tracking.trackers.Tracker
|
||||
import dev.slimevr.tracking.trackers.TrackerStatus
|
||||
import dev.slimevr.tracking.trackers.TrackerStatusListener
|
||||
import dev.slimevr.tracking.trackers.udp.MCUType
|
||||
import dev.slimevr.tracking.trackers.udp.UDPDevice
|
||||
import io.eiren.util.logging.LogManager
|
||||
import kotlinx.coroutines.*
|
||||
@@ -101,11 +102,27 @@ class FirmwareUpdateHandler(private val server: VRServer) :
|
||||
)
|
||||
return@suspendCancellableCoroutine
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// - Use the Firmware Builder to get the expected MCU
|
||||
// It would be wrong to assume that the Target MCU is the correct one,
|
||||
// just because the device is listening on the correct port.
|
||||
// The Upload protocol does not verify the compatibility of the firmware with the MCU.
|
||||
|
||||
val port = when (udpDevice.mcuType) {
|
||||
MCUType.ESP8266 -> 8266
|
||||
MCUType.ESP32, MCUType.ESP32_C3 -> 3232
|
||||
else -> error("MCU-Typ: ${udpDevice.mcuType} not supported for OTA updates")
|
||||
}
|
||||
|
||||
LogManager.info("[FirmwareUpdateHandler] Starting OTA update for device ${deviceId.id} at ${udpDevice.ipAddress.hostAddress}:$port and MCU ${udpDevice.mcuType}")
|
||||
|
||||
val task = OTAUpdateTask(
|
||||
part.firmware,
|
||||
deviceId,
|
||||
udpDevice.ipAddress,
|
||||
::onStatusChange,
|
||||
port,
|
||||
)
|
||||
c.invokeOnCancellation {
|
||||
task.cancel()
|
||||
@@ -156,6 +173,15 @@ class FirmwareUpdateHandler(private val server: VRServer) :
|
||||
flasher.addBin(part.firmware, part.offset.toInt())
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// - Check if FW is able to use flashmode
|
||||
// - Add check if the flashmode was successfully set to surpress the request
|
||||
// for manual flashmode setting prompt in gui
|
||||
|
||||
server.serialHandler.openSerial(deviceId.id, false)
|
||||
server.serialHandler.write("SET FLASHMODE\r\n".toByteArray())
|
||||
server.serialHandler.closeSerial()
|
||||
|
||||
flasher.addProgressListener(object : FlashingProgressListener {
|
||||
override fun progress(progress: Float) {
|
||||
onStatusChange(
|
||||
|
||||
@@ -14,6 +14,8 @@ import java.security.MessageDigest
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import java.util.*
|
||||
import java.util.function.Consumer
|
||||
import javax.crypto.SecretKeyFactory
|
||||
import javax.crypto.spec.PBEKeySpec
|
||||
import kotlin.math.min
|
||||
|
||||
class OTAUpdateTask(
|
||||
@@ -21,8 +23,9 @@ class OTAUpdateTask(
|
||||
private val deviceId: UpdateDeviceId<Int>,
|
||||
private val deviceIp: InetAddress,
|
||||
private val statusCallback: Consumer<UpdateStatusEvent<Int>>,
|
||||
private val port: Int = 8266,
|
||||
) {
|
||||
private val receiveBuffer: ByteArray = ByteArray(38)
|
||||
private val receiveBuffer: ByteArray = ByteArray(69)
|
||||
var socketServer: ServerSocket? = null
|
||||
var uploadSocket: Socket? = null
|
||||
var authSocket: DatagramSocket? = null
|
||||
@@ -40,17 +43,35 @@ class OTAUpdateTask(
|
||||
return md5str.toString()
|
||||
}
|
||||
|
||||
@Throws(NoSuchAlgorithmException::class)
|
||||
private fun bytesToSha256(bytes: ByteArray): String {
|
||||
val sha256 = MessageDigest.getInstance("SHA-256")
|
||||
sha256.update(bytes)
|
||||
val digest = sha256.digest()
|
||||
val sha256str = StringBuilder()
|
||||
for (b in digest) {
|
||||
sha256str.append(String.format("%02x", b))
|
||||
}
|
||||
return sha256str.toString()
|
||||
}
|
||||
|
||||
fun pbkdf2Hmac(password: String, salt: ByteArray, iterations: Int, keyLength: Int): ByteArray {
|
||||
val spec = PBEKeySpec(password.toCharArray(), salt, iterations, keyLength * 8)
|
||||
val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
|
||||
return factory.generateSecret(spec).encoded
|
||||
}
|
||||
|
||||
private fun authenticate(localPort: Int): Boolean {
|
||||
try {
|
||||
DatagramSocket().use { socket ->
|
||||
authSocket = socket
|
||||
statusCallback.accept(UpdateStatusEvent(deviceId, FirmwareUpdateStatus.AUTHENTICATING))
|
||||
LogManager.info("[OTAUpdate] Sending OTA invitation to: $deviceIp")
|
||||
LogManager.info("[OTAUpdate] Sending OTA invitation to: $deviceIp:$port")
|
||||
|
||||
val fileMd5 = bytesToMd5(firmware)
|
||||
val message = "$FLASH $localPort ${firmware.size} $fileMd5\n"
|
||||
|
||||
socket.send(DatagramPacket(message.toByteArray(), message.length, deviceIp, PORT))
|
||||
socket.send(DatagramPacket(message.toByteArray(), message.length, deviceIp, port))
|
||||
socket.soTimeout = 10000
|
||||
|
||||
val authPacket = DatagramPacket(receiveBuffer, receiveBuffer.size)
|
||||
@@ -68,13 +89,26 @@ class OTAUpdateTask(
|
||||
if (args.size != 2 || args[0] != "AUTH") return false
|
||||
|
||||
LogManager.info("[OTAUpdate] Authenticating...")
|
||||
var payload = ""
|
||||
var signature = ""
|
||||
|
||||
val authToken = args[1]
|
||||
val signature = bytesToMd5(UUID.randomUUID().toString().toByteArray())
|
||||
val hashedPassword = bytesToMd5(PASSWORD.toByteArray())
|
||||
val resultText = "$hashedPassword:$authToken:$signature"
|
||||
val payload = bytesToMd5(resultText.toByteArray())
|
||||
|
||||
if (authToken.length == 32) {
|
||||
signature =
|
||||
bytesToMd5(UUID.randomUUID().toString().toByteArray())
|
||||
val hashedPassword = bytesToMd5(PASSWORD.toByteArray())
|
||||
val resultText = "$hashedPassword:$authToken:$signature"
|
||||
payload = bytesToMd5(resultText.toByteArray())
|
||||
} else if (authToken.length == 64) {
|
||||
signature =
|
||||
bytesToSha256(UUID.randomUUID().toString().toByteArray())
|
||||
val salt = "$authToken:$signature"
|
||||
val hashedPassword = bytesToSha256(PASSWORD.toByteArray())
|
||||
val derivedkey = pbkdf2Hmac(hashedPassword, salt.toByteArray(), 10000, 32)
|
||||
val derivedkeyHex = derivedkey.joinToString("") { "%02x".format(it) }
|
||||
val challenge = "$derivedkeyHex:$authToken:$signature"
|
||||
payload = bytesToSha256(challenge.toByteArray())
|
||||
}
|
||||
val authMessage = "$AUTH $signature $payload\n"
|
||||
|
||||
socket.soTimeout = 10000
|
||||
@@ -83,7 +117,7 @@ class OTAUpdateTask(
|
||||
authMessage.toByteArray(),
|
||||
authMessage.length,
|
||||
deviceIp,
|
||||
PORT,
|
||||
port,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -198,7 +232,6 @@ class OTAUpdateTask(
|
||||
|
||||
companion object {
|
||||
private const val FLASH = 0
|
||||
private const val PORT = 8266
|
||||
private const val PASSWORD = "SlimeVR-OTA"
|
||||
private const val AUTH = 200
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
package dev.slimevr.protocol;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class ConnectionContext {
|
||||
|
||||
private final List<DataFeed> dataFeedList = new ArrayList<>();
|
||||
|
||||
private final List<Integer> subscribedTopics = new ArrayList<>();
|
||||
|
||||
private boolean useSerial = false;
|
||||
|
||||
private boolean useProvisioning = false;
|
||||
private boolean useAutoBone = false;
|
||||
|
||||
public List<DataFeed> getDataFeedList() {
|
||||
return dataFeedList;
|
||||
}
|
||||
|
||||
public List<Integer> getSubscribedTopics() {
|
||||
return subscribedTopics;
|
||||
}
|
||||
|
||||
public boolean useSerial() {
|
||||
return useSerial;
|
||||
}
|
||||
|
||||
public void setUseSerial(boolean useSerial) {
|
||||
this.useSerial = useSerial;
|
||||
}
|
||||
|
||||
public boolean useAutoBone() {
|
||||
return useAutoBone;
|
||||
}
|
||||
|
||||
public void setUseAutoBone(boolean useAutoBone) {
|
||||
this.useAutoBone = useAutoBone;
|
||||
}
|
||||
|
||||
public boolean useProvisioning() {
|
||||
return useProvisioning;
|
||||
}
|
||||
|
||||
public void setUseProvisioning(boolean useProvisioning) {
|
||||
this.useProvisioning = useProvisioning;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package dev.slimevr.protocol
|
||||
|
||||
class ConnectionContext {
|
||||
val dataFeedList: MutableList<DataFeed> = mutableListOf()
|
||||
val subscribedTopics: MutableList<Int> = mutableListOf()
|
||||
|
||||
var useSerial: Boolean = false
|
||||
var useProvisioning: Boolean = false
|
||||
var useAutoBone: Boolean = false
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package dev.slimevr.protocol;
|
||||
|
||||
import solarxr_protocol.data_feed.DataFeedConfigT;
|
||||
|
||||
|
||||
public class DataFeed {
|
||||
private DataFeedConfigT config;
|
||||
private Long timeLastSent;
|
||||
|
||||
public DataFeed(DataFeedConfigT config) {
|
||||
this.config = config;
|
||||
this.timeLastSent = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public DataFeedConfigT getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public Long getTimeLastSent() {
|
||||
return timeLastSent;
|
||||
}
|
||||
|
||||
public void setTimeLastSent(Long timeLastSent) {
|
||||
this.timeLastSent = timeLastSent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package dev.slimevr.protocol
|
||||
|
||||
import solarxr_protocol.data_feed.DataFeedConfigT
|
||||
|
||||
class DataFeed(val config: DataFeedConfigT) {
|
||||
var timeLastSent: Long = System.currentTimeMillis()
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package dev.slimevr.protocol;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
public interface GenericConnection {
|
||||
|
||||
UUID getConnectionId();
|
||||
|
||||
ConnectionContext getContext();
|
||||
|
||||
void send(ByteBuffer bytes);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package dev.slimevr.protocol
|
||||
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.UUID
|
||||
|
||||
interface GenericConnection {
|
||||
val connectionId: UUID
|
||||
|
||||
val context: ConnectionContext
|
||||
|
||||
fun send(bytes: ByteBuffer)
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package dev.slimevr.protocol;
|
||||
|
||||
import dev.slimevr.VRServer;
|
||||
import dev.slimevr.protocol.datafeed.DataFeedHandler;
|
||||
import dev.slimevr.protocol.pubsub.PubSubHandler;
|
||||
import dev.slimevr.protocol.rpc.RPCHandler;
|
||||
import solarxr_protocol.MessageBundle;
|
||||
import solarxr_protocol.data_feed.DataFeedMessageHeader;
|
||||
import solarxr_protocol.pub_sub.PubSubHeader;
|
||||
import solarxr_protocol.rpc.RpcMessageHeader;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class ProtocolAPI {
|
||||
|
||||
public final VRServer server;
|
||||
public final RPCHandler rpcHandler;
|
||||
public final DataFeedHandler dataFeedHandler;
|
||||
public final PubSubHandler pubSubHandler;
|
||||
|
||||
private final List<ProtocolAPIServer> servers = new ArrayList<>();
|
||||
|
||||
public ProtocolAPI(VRServer server) {
|
||||
this.server = server;
|
||||
this.rpcHandler = new RPCHandler(this);
|
||||
this.dataFeedHandler = new DataFeedHandler(this);
|
||||
this.pubSubHandler = new PubSubHandler(this);
|
||||
}
|
||||
|
||||
public void onMessage(GenericConnection conn, ByteBuffer message) {
|
||||
MessageBundle messageBundle = MessageBundle.getRootAsMessageBundle(message);
|
||||
|
||||
try {
|
||||
for (int index = 0; index < messageBundle.dataFeedMsgsLength(); index++) {
|
||||
DataFeedMessageHeader header = messageBundle.dataFeedMsgsVector().get(index);
|
||||
this.dataFeedHandler.onMessage(conn, header);
|
||||
}
|
||||
|
||||
for (int index = 0; index < messageBundle.rpcMsgsLength(); index++) {
|
||||
RpcMessageHeader header = messageBundle.rpcMsgsVector().get(index);
|
||||
this.rpcHandler.onMessage(conn, header);
|
||||
}
|
||||
|
||||
for (int index = 0; index < messageBundle.pubSubMsgsLength(); index++) {
|
||||
PubSubHeader header = messageBundle.pubSubMsgsVector().get(index);
|
||||
this.pubSubHandler.onMessage(conn, header);
|
||||
}
|
||||
} catch (AssertionError e) {
|
||||
// Catch flatbuffer errors
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public List<ProtocolAPIServer> getAPIServers() {
|
||||
return servers;
|
||||
}
|
||||
|
||||
public void registerAPIServer(ProtocolAPIServer server) {
|
||||
this.servers.add(server);
|
||||
}
|
||||
|
||||
public void removeAPIServer(ProtocolAPIServer server) {
|
||||
this.servers.remove(server);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package dev.slimevr.protocol
|
||||
|
||||
import dev.slimevr.VRServer
|
||||
import dev.slimevr.protocol.datafeed.DataFeedHandler
|
||||
import dev.slimevr.protocol.pubsub.PubSubHandler
|
||||
import dev.slimevr.protocol.rpc.RPCHandler
|
||||
import solarxr_protocol.MessageBundle
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
class ProtocolAPI(val server: VRServer) {
|
||||
val apiServers: MutableList<ProtocolAPIServer> = ArrayList()
|
||||
val dataFeedHandler: DataFeedHandler = DataFeedHandler(this)
|
||||
val pubSubHandler: PubSubHandler = PubSubHandler(this)
|
||||
val rpcHandler: RPCHandler = RPCHandler(this)
|
||||
|
||||
fun onMessage(conn: GenericConnection, message: ByteBuffer) {
|
||||
val messageBundle = MessageBundle.getRootAsMessageBundle(message)
|
||||
|
||||
try {
|
||||
for (index in 0..<messageBundle.dataFeedMsgsLength()) {
|
||||
val header = messageBundle.dataFeedMsgsVector().get(index)
|
||||
this.dataFeedHandler.onMessage(conn, header)
|
||||
}
|
||||
|
||||
for (index in 0..<messageBundle.rpcMsgsLength()) {
|
||||
val header = messageBundle.rpcMsgsVector().get(index)
|
||||
this.rpcHandler.onMessage(conn, header)
|
||||
}
|
||||
|
||||
for (index in 0..<messageBundle.pubSubMsgsLength()) {
|
||||
val header = messageBundle.pubSubMsgsVector().get(index)
|
||||
this.pubSubHandler.onMessage(conn, header)
|
||||
}
|
||||
} catch (e: AssertionError) {
|
||||
// Catch flatbuffer errors
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun registerAPIServer(server: ProtocolAPIServer) {
|
||||
this.apiServers.add(server)
|
||||
}
|
||||
|
||||
fun removeAPIServer(server: ProtocolAPIServer) {
|
||||
this.apiServers.remove(server)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package dev.slimevr.protocol;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
||||
public interface ProtocolAPIServer {
|
||||
|
||||
Stream<GenericConnection> getAPIConnections();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package dev.slimevr.protocol
|
||||
|
||||
import java.util.stream.Stream
|
||||
|
||||
interface ProtocolAPIServer {
|
||||
val apiConnections: Stream<GenericConnection>
|
||||
}
|
||||
@@ -1,413 +0,0 @@
|
||||
package dev.slimevr.protocol.datafeed;
|
||||
|
||||
import com.google.flatbuffers.FlatBufferBuilder;
|
||||
import dev.slimevr.tracking.trackers.Device;
|
||||
import dev.slimevr.tracking.trackers.Tracker;
|
||||
import dev.slimevr.tracking.trackers.udp.MagnetometerStatus;
|
||||
import dev.slimevr.tracking.trackers.udp.UDPDevice;
|
||||
import io.github.axisangles.ktmath.Quaternion;
|
||||
import io.github.axisangles.ktmath.Vector3;
|
||||
import solarxr_protocol.data_feed.Bone;
|
||||
import solarxr_protocol.data_feed.DataFeedUpdate;
|
||||
import solarxr_protocol.data_feed.device_data.DeviceData;
|
||||
import solarxr_protocol.data_feed.device_data.DeviceDataMaskT;
|
||||
import solarxr_protocol.data_feed.tracker.TrackerData;
|
||||
import solarxr_protocol.data_feed.tracker.TrackerDataMaskT;
|
||||
import solarxr_protocol.data_feed.tracker.TrackerInfo;
|
||||
import solarxr_protocol.datatypes.DeviceId;
|
||||
import solarxr_protocol.datatypes.Ipv4Address;
|
||||
import solarxr_protocol.datatypes.Temperature;
|
||||
import solarxr_protocol.datatypes.TrackerId;
|
||||
import solarxr_protocol.datatypes.hardware_info.HardwareInfo;
|
||||
import solarxr_protocol.datatypes.hardware_info.HardwareStatus;
|
||||
import solarxr_protocol.datatypes.math.Quat;
|
||||
import solarxr_protocol.datatypes.math.Vec3f;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class DataFeedBuilder {
|
||||
|
||||
public static int createHardwareInfo(FlatBufferBuilder fbb, Device device) {
|
||||
int nameOffset = device.getFirmwareVersion() != null
|
||||
? fbb.createString(device.getFirmwareVersion())
|
||||
: 0;
|
||||
|
||||
int manufacturerOffset = device.getManufacturer() != null
|
||||
? fbb.createString(device.getManufacturer())
|
||||
: 0;
|
||||
|
||||
int firmwareDateOffset = device.getFirmwareDate() != null
|
||||
? fbb.createString(device.getFirmwareDate())
|
||||
: 0;
|
||||
|
||||
int hardwareIdentifierOffset = fbb.createString(device.getHardwareIdentifier());
|
||||
|
||||
HardwareInfo.startHardwareInfo(fbb);
|
||||
HardwareInfo.addFirmwareVersion(fbb, nameOffset);
|
||||
HardwareInfo.addManufacturer(fbb, manufacturerOffset);
|
||||
HardwareInfo.addHardwareIdentifier(fbb, hardwareIdentifierOffset);
|
||||
|
||||
HardwareInfo.addFirmwareDate(fbb, firmwareDateOffset);
|
||||
|
||||
if (device instanceof UDPDevice udpDevice) {
|
||||
var address = udpDevice.getIpAddress().getAddress();
|
||||
HardwareInfo
|
||||
.addIpAddress(
|
||||
fbb,
|
||||
Ipv4Address
|
||||
.createIpv4Address(
|
||||
fbb,
|
||||
ByteBuffer.wrap(address).getInt()
|
||||
)
|
||||
);
|
||||
|
||||
HardwareInfo.addNetworkProtocolVersion(fbb, udpDevice.protocolVersion);
|
||||
}
|
||||
|
||||
// BRUH MOMENT
|
||||
// TODO need support: HardwareInfo.addHardwareRevision(fbb,
|
||||
// hardwareRevisionOffset);
|
||||
// TODO need support: HardwareInfo.addDisplayName(fbb, de);
|
||||
|
||||
HardwareInfo.addMcuId(fbb, device.getMcuType().getSolarType());
|
||||
HardwareInfo.addOfficialBoardType(fbb, device.getBoardType().getSolarType());
|
||||
|
||||
return HardwareInfo.endHardwareInfo(fbb);
|
||||
}
|
||||
|
||||
public static int createTrackerId(FlatBufferBuilder fbb, Tracker tracker) {
|
||||
TrackerId.startTrackerId(fbb);
|
||||
|
||||
TrackerId.addTrackerNum(fbb, tracker.getTrackerNum());
|
||||
if (tracker.getDevice() != null)
|
||||
TrackerId.addDeviceId(fbb, DeviceId.createDeviceId(fbb, tracker.getDevice().getId()));
|
||||
|
||||
return TrackerId.endTrackerId(fbb);
|
||||
}
|
||||
|
||||
public static int createVec3(FlatBufferBuilder fbb, Vector3 vec) {
|
||||
return Vec3f
|
||||
.createVec3f(
|
||||
fbb,
|
||||
vec.getX(),
|
||||
vec.getY(),
|
||||
vec.getZ()
|
||||
);
|
||||
}
|
||||
|
||||
public static int createQuat(FlatBufferBuilder fbb, Quaternion quaternion) {
|
||||
return Quat
|
||||
.createQuat(
|
||||
fbb,
|
||||
quaternion.getX(),
|
||||
quaternion.getY(),
|
||||
quaternion.getZ(),
|
||||
quaternion.getW()
|
||||
);
|
||||
}
|
||||
|
||||
public static int createTrackerInfos(
|
||||
FlatBufferBuilder fbb,
|
||||
boolean infoMask,
|
||||
Tracker tracker
|
||||
) {
|
||||
|
||||
if (!infoMask)
|
||||
return 0;
|
||||
|
||||
int displayNameOffset = fbb.createString(tracker.getDisplayName());
|
||||
int customNameOffset = tracker.getCustomName() != null
|
||||
? fbb.createString(tracker.getCustomName())
|
||||
: 0;
|
||||
|
||||
TrackerInfo.startTrackerInfo(fbb);
|
||||
if (tracker.getTrackerPosition() != null)
|
||||
TrackerInfo.addBodyPart(fbb, tracker.getTrackerPosition().getBodyPart());
|
||||
TrackerInfo.addEditable(fbb, tracker.getUserEditable());
|
||||
TrackerInfo.addIsComputed(fbb, tracker.isComputed());
|
||||
TrackerInfo.addDisplayName(fbb, displayNameOffset);
|
||||
TrackerInfo.addCustomName(fbb, customNameOffset);
|
||||
if (tracker.getImuType() != null) {
|
||||
TrackerInfo.addImuType(fbb, tracker.getImuType().getSolarType());
|
||||
}
|
||||
|
||||
// TODO need support: TrackerInfo.addPollRate(fbb, tracker.);
|
||||
|
||||
if (tracker.isImu()) {
|
||||
TrackerInfo.addIsImu(fbb, true);
|
||||
TrackerInfo
|
||||
.addAllowDriftCompensation(
|
||||
fbb,
|
||||
tracker.getResetsHandler().getAllowDriftCompensation()
|
||||
);
|
||||
} else {
|
||||
TrackerInfo.addIsImu(fbb, false);
|
||||
TrackerInfo.addAllowDriftCompensation(fbb, false);
|
||||
}
|
||||
|
||||
if (tracker.getAllowMounting()) {
|
||||
Quaternion quaternion = tracker.getResetsHandler().getMountingOrientation();
|
||||
Quaternion mountResetFix = tracker.getResetsHandler().getMountRotFix();
|
||||
TrackerInfo.addMountingOrientation(fbb, createQuat(fbb, quaternion));
|
||||
TrackerInfo.addMountingResetOrientation(fbb, createQuat(fbb, mountResetFix));
|
||||
}
|
||||
|
||||
TrackerInfo.addMagnetometer(fbb, tracker.getMagStatus().getSolarType());
|
||||
TrackerInfo.addIsHmd(fbb, tracker.isHmd());
|
||||
|
||||
TrackerInfo.addDataSupport(fbb, tracker.getTrackerDataType().getSolarType());
|
||||
|
||||
return TrackerInfo.endTrackerInfo(fbb);
|
||||
}
|
||||
|
||||
public static int createTrackerPosition(FlatBufferBuilder fbb, Tracker tracker) {
|
||||
return createVec3(fbb, tracker.getPosition());
|
||||
}
|
||||
|
||||
public static int createTrackerRotation(FlatBufferBuilder fbb, Tracker tracker) {
|
||||
return createQuat(fbb, tracker.getRawRotation());
|
||||
}
|
||||
|
||||
public static int createTrackerAcceleration(FlatBufferBuilder fbb, Tracker tracker) {
|
||||
return createVec3(fbb, tracker.getAcceleration());
|
||||
}
|
||||
|
||||
public static int createTrackerMagneticVector(FlatBufferBuilder fbb, Tracker tracker) {
|
||||
return createVec3(fbb, tracker.getMagVector());
|
||||
}
|
||||
|
||||
public static int createTrackerTemperature(FlatBufferBuilder fbb, Tracker tracker) {
|
||||
if (tracker.getTemperature() == null)
|
||||
return 0;
|
||||
return Temperature.createTemperature(fbb, tracker.getTemperature());
|
||||
}
|
||||
|
||||
public static int createTrackerData(
|
||||
FlatBufferBuilder fbb,
|
||||
TrackerDataMaskT mask,
|
||||
Tracker tracker
|
||||
) {
|
||||
int trackerInfosOffset = DataFeedBuilder.createTrackerInfos(fbb, mask.getInfo(), tracker);
|
||||
int trackerIdOffset = DataFeedBuilder.createTrackerId(fbb, tracker);
|
||||
|
||||
int stayAlignedOffset = 0;
|
||||
if (mask.getStayAligned()) {
|
||||
stayAlignedOffset = DataFeedBuilderKotlin.INSTANCE
|
||||
.createTrackerStayAlignedTracker(fbb, tracker.getStayAligned());
|
||||
}
|
||||
|
||||
TrackerData.startTrackerData(fbb);
|
||||
|
||||
TrackerData.addTrackerId(fbb, trackerIdOffset);
|
||||
|
||||
if (trackerInfosOffset != 0)
|
||||
TrackerData.addInfo(fbb, trackerInfosOffset);
|
||||
if (mask.getStatus())
|
||||
TrackerData.addStatus(fbb, tracker.getStatus().getId() + 1);
|
||||
if (mask.getPosition() && tracker.getHasPosition())
|
||||
TrackerData.addPosition(fbb, DataFeedBuilder.createTrackerPosition(fbb, tracker));
|
||||
if (mask.getRotation() && tracker.getHasRotation())
|
||||
TrackerData.addRotation(fbb, DataFeedBuilder.createTrackerRotation(fbb, tracker));
|
||||
if (mask.getLinearAcceleration() && tracker.getHasAcceleration())
|
||||
TrackerData
|
||||
.addLinearAcceleration(
|
||||
fbb,
|
||||
DataFeedBuilder.createTrackerAcceleration(fbb, tracker)
|
||||
);
|
||||
if (mask.getTemp()) {
|
||||
int trackerTemperatureOffset = DataFeedBuilder.createTrackerTemperature(fbb, tracker);
|
||||
if (trackerTemperatureOffset != 0)
|
||||
TrackerData.addTemp(fbb, trackerTemperatureOffset);
|
||||
}
|
||||
if (tracker.getAllowMounting() && tracker.getHasRotation()) {
|
||||
if (mask.getRotationReferenceAdjusted()) {
|
||||
TrackerData
|
||||
.addRotationReferenceAdjusted(fbb, createQuat(fbb, tracker.getRotation()));
|
||||
}
|
||||
if (mask.getRotationIdentityAdjusted()) {
|
||||
TrackerData
|
||||
.addRotationIdentityAdjusted(
|
||||
fbb,
|
||||
createQuat(fbb, tracker.getIdentityAdjustedRotation())
|
||||
);
|
||||
}
|
||||
} else if (tracker.getAllowReset() && tracker.getHasRotation()) {
|
||||
if (mask.getRotationReferenceAdjusted()) {
|
||||
TrackerData
|
||||
.addRotationReferenceAdjusted(fbb, createQuat(fbb, tracker.getRotation()));
|
||||
}
|
||||
if (mask.getRotationIdentityAdjusted()) {
|
||||
TrackerData
|
||||
.addRotationIdentityAdjusted(fbb, createQuat(fbb, tracker.getRawRotation()));
|
||||
}
|
||||
}
|
||||
if (mask.getTps()) {
|
||||
TrackerData.addTps(fbb, (int) tracker.getTps());
|
||||
}
|
||||
if (mask.getRawMagneticVector() && tracker.getMagStatus() == MagnetometerStatus.ENABLED) {
|
||||
TrackerData.addRawMagneticVector(fbb, createTrackerMagneticVector(fbb, tracker));
|
||||
}
|
||||
if (mask.getStayAligned()) {
|
||||
TrackerData.addStayAligned(fbb, stayAlignedOffset);
|
||||
}
|
||||
|
||||
return TrackerData.endTrackerData(fbb);
|
||||
}
|
||||
|
||||
public static int createTrackersData(
|
||||
FlatBufferBuilder fbb,
|
||||
DeviceDataMaskT mask,
|
||||
Device device
|
||||
) {
|
||||
if (mask.getTrackerData() == null)
|
||||
return 0;
|
||||
|
||||
List<Integer> trackersOffsets = new ArrayList<>();
|
||||
|
||||
device
|
||||
.getTrackers()
|
||||
.forEach(
|
||||
(key, value) -> trackersOffsets
|
||||
.add(DataFeedBuilder.createTrackerData(fbb, mask.getTrackerData(), value))
|
||||
);
|
||||
|
||||
DeviceData.startTrackersVector(fbb, trackersOffsets.size());
|
||||
trackersOffsets.forEach(offset -> DeviceData.addTrackers(fbb, offset));
|
||||
return fbb.endVector();
|
||||
}
|
||||
|
||||
public static int createDeviceData(
|
||||
FlatBufferBuilder fbb,
|
||||
int id,
|
||||
DeviceDataMaskT mask,
|
||||
Device device
|
||||
) {
|
||||
if (!mask.getDeviceData())
|
||||
return 0;
|
||||
|
||||
if (device.getTrackers().size() <= 0)
|
||||
return 0;
|
||||
|
||||
Tracker firstTracker = device.getTrackers().get(0);
|
||||
if (firstTracker == null) {
|
||||
// Not actually the "first" tracker, but do we care?
|
||||
firstTracker = device.getTrackers().entrySet().iterator().next().getValue();
|
||||
}
|
||||
|
||||
Tracker tracker = firstTracker;
|
||||
if (tracker == null)
|
||||
return 0;
|
||||
|
||||
HardwareStatus.startHardwareStatus(fbb);
|
||||
HardwareStatus.addErrorStatus(fbb, tracker.getStatus().getId());
|
||||
|
||||
if (tracker.getBatteryVoltage() != null) {
|
||||
HardwareStatus.addBatteryVoltage(fbb, tracker.getBatteryVoltage());
|
||||
}
|
||||
if (tracker.getBatteryLevel() != null) {
|
||||
HardwareStatus.addBatteryPctEstimate(fbb, (int) tracker.getBatteryLevel().floatValue());
|
||||
}
|
||||
if (tracker.getPing() != null) {
|
||||
HardwareStatus.addPing(fbb, tracker.getPing());
|
||||
}
|
||||
if (tracker.getSignalStrength() != null) {
|
||||
HardwareStatus.addRssi(fbb, (short) tracker.getSignalStrength().floatValue());
|
||||
}
|
||||
|
||||
if (tracker.getBatteryRemainingRuntime() != null) {
|
||||
HardwareStatus.addBatteryRuntimeEstimate(fbb, tracker.getBatteryRemainingRuntime());
|
||||
}
|
||||
|
||||
int hardwareDataOffset = HardwareStatus.endHardwareStatus(fbb);
|
||||
int hardwareInfoOffset = DataFeedBuilder.createHardwareInfo(fbb, device);
|
||||
int trackersOffset = DataFeedBuilder.createTrackersData(fbb, mask, device);
|
||||
|
||||
int nameOffset = device.getName() != null
|
||||
? fbb.createString(device.getName())
|
||||
: 0;
|
||||
|
||||
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);
|
||||
|
||||
return DeviceData.endDeviceData(fbb);
|
||||
}
|
||||
|
||||
public static int createSyntheticTrackersData(
|
||||
FlatBufferBuilder fbb,
|
||||
TrackerDataMaskT trackerDataMaskT,
|
||||
List<Tracker> trackers
|
||||
) {
|
||||
if (trackerDataMaskT == null)
|
||||
return 0;
|
||||
|
||||
List<Integer> trackerOffsets = new ArrayList<>();
|
||||
|
||||
trackers
|
||||
.forEach(
|
||||
(tracker) -> trackerOffsets
|
||||
.add(DataFeedBuilder.createTrackerData(fbb, trackerDataMaskT, tracker))
|
||||
);
|
||||
|
||||
DataFeedUpdate.startSyntheticTrackersVector(fbb, trackerOffsets.size());
|
||||
trackerOffsets.forEach((tracker -> DataFeedUpdate.addSyntheticTrackers(fbb, tracker)));
|
||||
return fbb.endVector();
|
||||
}
|
||||
|
||||
public static int createDevicesData(
|
||||
FlatBufferBuilder fbb,
|
||||
DeviceDataMaskT deviceDataMaskT,
|
||||
List<Device> devices
|
||||
) {
|
||||
if (deviceDataMaskT == null)
|
||||
return 0;
|
||||
|
||||
int[] devicesDataOffsets = new int[devices.size()];
|
||||
for (int i = 0; i < devices.size(); i++) {
|
||||
Device device = devices.get(i);
|
||||
devicesDataOffsets[i] = DataFeedBuilder
|
||||
.createDeviceData(fbb, device.getId(), deviceDataMaskT, device);
|
||||
}
|
||||
|
||||
return DataFeedUpdate.createDevicesVector(fbb, devicesDataOffsets);
|
||||
}
|
||||
|
||||
public static int createBonesData(
|
||||
FlatBufferBuilder fbb,
|
||||
boolean shouldSend,
|
||||
List<dev.slimevr.tracking.processor.Bone> bones
|
||||
) {
|
||||
if (!shouldSend) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var boneOffsets = new int[bones.size()];
|
||||
for (var i = 0; i < bones.size(); ++i) {
|
||||
var bi = bones.get(i);
|
||||
|
||||
var headPosG = bi.getPosition();
|
||||
var rotG = bi.getGlobalRotation();
|
||||
var length = bi.getLength();
|
||||
|
||||
Bone.startBone(fbb);
|
||||
|
||||
var rotGOffset = createQuat(fbb, rotG);
|
||||
Bone.addRotationG(fbb, rotGOffset);
|
||||
var headPosGOffset = Vec3f
|
||||
.createVec3f(fbb, headPosG.getX(), headPosG.getY(), headPosG.getZ());
|
||||
Bone.addHeadPositionG(fbb, headPosGOffset);
|
||||
Bone.addBodyPart(fbb, bi.getBoneType().bodyPart);
|
||||
Bone.addBoneLength(fbb, length);
|
||||
|
||||
boneOffsets[i] = Bone.endBone(fbb);
|
||||
}
|
||||
|
||||
return DataFeedUpdate.createBonesVector(fbb, boneOffsets);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,503 @@
|
||||
package dev.slimevr.protocol.datafeed
|
||||
|
||||
import com.google.flatbuffers.FlatBufferBuilder
|
||||
import dev.slimevr.guards.ServerGuards
|
||||
import dev.slimevr.tracking.processor.Bone
|
||||
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton
|
||||
import dev.slimevr.tracking.processor.stayaligned.poses.RelaxedPose
|
||||
import dev.slimevr.tracking.processor.stayaligned.trackers.RestDetector
|
||||
import dev.slimevr.tracking.processor.stayaligned.trackers.StayAlignedTrackerState
|
||||
import dev.slimevr.tracking.trackers.Device
|
||||
import dev.slimevr.tracking.trackers.Tracker
|
||||
import dev.slimevr.tracking.trackers.udp.MagnetometerStatus
|
||||
import dev.slimevr.tracking.trackers.udp.UDPDevice
|
||||
import io.github.axisangles.ktmath.Quaternion
|
||||
import io.github.axisangles.ktmath.Vector3
|
||||
import solarxr_protocol.data_feed.DataFeedUpdate
|
||||
import solarxr_protocol.data_feed.device_data.DeviceData
|
||||
import solarxr_protocol.data_feed.device_data.DeviceDataMaskT
|
||||
import solarxr_protocol.data_feed.stay_aligned.StayAlignedPose
|
||||
import solarxr_protocol.data_feed.stay_aligned.StayAlignedTracker
|
||||
import solarxr_protocol.data_feed.tracker.TrackerData
|
||||
import solarxr_protocol.data_feed.tracker.TrackerDataMaskT
|
||||
import solarxr_protocol.data_feed.tracker.TrackerInfo
|
||||
import solarxr_protocol.datatypes.DeviceId
|
||||
import solarxr_protocol.datatypes.Ipv4Address
|
||||
import solarxr_protocol.datatypes.Temperature
|
||||
import solarxr_protocol.datatypes.TrackerId
|
||||
import solarxr_protocol.datatypes.hardware_info.HardwareInfo
|
||||
import solarxr_protocol.datatypes.hardware_info.HardwareStatus
|
||||
import solarxr_protocol.datatypes.math.Quat
|
||||
import solarxr_protocol.datatypes.math.Vec3f
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.function.Consumer
|
||||
|
||||
fun createHardwareInfo(fbb: FlatBufferBuilder, device: Device): Int {
|
||||
val nameOffset = if (device.firmwareVersion != null) {
|
||||
fbb.createString(device.firmwareVersion)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
||||
val manufacturerOffset = if (device.manufacturer != null) {
|
||||
fbb.createString(device.manufacturer)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
||||
val firmwareDateOffset = if (device.firmwareDate != null) {
|
||||
fbb.createString(device.firmwareDate)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
||||
val hardwareIdentifierOffset = fbb.createString(device.hardwareIdentifier)
|
||||
|
||||
HardwareInfo.startHardwareInfo(fbb)
|
||||
HardwareInfo.addFirmwareVersion(fbb, nameOffset)
|
||||
HardwareInfo.addFirmwareDate(fbb, firmwareDateOffset)
|
||||
HardwareInfo.addManufacturer(fbb, manufacturerOffset)
|
||||
HardwareInfo.addHardwareIdentifier(fbb, hardwareIdentifierOffset)
|
||||
|
||||
if (device is UDPDevice) {
|
||||
val address = device.ipAddress.address
|
||||
HardwareInfo
|
||||
.addIpAddress(
|
||||
fbb,
|
||||
Ipv4Address
|
||||
.createIpv4Address(
|
||||
fbb,
|
||||
ByteBuffer.wrap(address).getInt().toLong(),
|
||||
),
|
||||
)
|
||||
|
||||
HardwareInfo.addNetworkProtocolVersion(fbb, device.protocolVersion)
|
||||
}
|
||||
|
||||
// BRUH MOMENT
|
||||
// TODO need support: HardwareInfo.addHardwareRevision(fbb,
|
||||
// hardwareRevisionOffset);
|
||||
// TODO need support: HardwareInfo.addDisplayName(fbb, de);
|
||||
HardwareInfo.addMcuId(fbb, device.mcuType.getSolarType())
|
||||
HardwareInfo.addOfficialBoardType(fbb, device.boardType.getSolarType())
|
||||
return HardwareInfo.endHardwareInfo(fbb)
|
||||
}
|
||||
|
||||
fun createTrackerId(fbb: FlatBufferBuilder, tracker: Tracker): Int {
|
||||
TrackerId.startTrackerId(fbb)
|
||||
|
||||
TrackerId.addTrackerNum(fbb, tracker.trackerNum)
|
||||
if (tracker.device != null) {
|
||||
TrackerId.addDeviceId(
|
||||
fbb,
|
||||
DeviceId.createDeviceId(fbb, tracker.device.id),
|
||||
)
|
||||
}
|
||||
|
||||
return TrackerId.endTrackerId(fbb)
|
||||
}
|
||||
|
||||
fun createVec3(fbb: FlatBufferBuilder, vec: Vector3): Int = Vec3f
|
||||
.createVec3f(
|
||||
fbb,
|
||||
vec.x,
|
||||
vec.y,
|
||||
vec.z,
|
||||
)
|
||||
|
||||
fun createQuat(fbb: FlatBufferBuilder, quaternion: Quaternion): Int = Quat
|
||||
.createQuat(
|
||||
fbb,
|
||||
quaternion.x,
|
||||
quaternion.y,
|
||||
quaternion.z,
|
||||
quaternion.w,
|
||||
)
|
||||
|
||||
fun createTrackerInfos(
|
||||
fbb: FlatBufferBuilder,
|
||||
infoMask: Boolean,
|
||||
tracker: Tracker,
|
||||
): Int {
|
||||
if (!infoMask) return 0
|
||||
|
||||
val displayNameOffset = fbb.createString(tracker.displayName)
|
||||
val customNameOffset = if (tracker.customName != null) {
|
||||
fbb.createString(tracker.customName)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
||||
TrackerInfo.startTrackerInfo(fbb)
|
||||
if (tracker.trackerPosition != null) {
|
||||
TrackerInfo.addBodyPart(
|
||||
fbb,
|
||||
tracker.trackerPosition!!.bodyPart,
|
||||
)
|
||||
}
|
||||
TrackerInfo.addEditable(fbb, tracker.userEditable)
|
||||
TrackerInfo.addIsComputed(fbb, tracker.isComputed)
|
||||
TrackerInfo.addDisplayName(fbb, displayNameOffset)
|
||||
TrackerInfo.addCustomName(fbb, customNameOffset)
|
||||
if (tracker.imuType != null) {
|
||||
TrackerInfo.addImuType(fbb, tracker.imuType.getSolarType())
|
||||
}
|
||||
|
||||
// TODO need support: TrackerInfo.addPollRate(fbb, tracker.);
|
||||
if (tracker.isImu()) {
|
||||
TrackerInfo.addIsImu(fbb, true)
|
||||
TrackerInfo
|
||||
.addAllowDriftCompensation(
|
||||
fbb,
|
||||
tracker.resetsHandler.allowDriftCompensation,
|
||||
)
|
||||
} else {
|
||||
TrackerInfo.addIsImu(fbb, false)
|
||||
TrackerInfo.addAllowDriftCompensation(fbb, false)
|
||||
}
|
||||
|
||||
if (tracker.allowMounting) {
|
||||
val quaternion = tracker.resetsHandler.mountingOrientation
|
||||
val mountResetFix = tracker.resetsHandler.mountRotFix
|
||||
TrackerInfo.addMountingOrientation(fbb, createQuat(fbb, quaternion))
|
||||
TrackerInfo.addMountingResetOrientation(fbb, createQuat(fbb, mountResetFix))
|
||||
}
|
||||
|
||||
TrackerInfo.addMagnetometer(fbb, tracker.magStatus.getSolarType())
|
||||
TrackerInfo.addIsHmd(fbb, tracker.isHmd)
|
||||
|
||||
TrackerInfo.addDataSupport(fbb, tracker.trackerDataType.getSolarType())
|
||||
|
||||
return TrackerInfo.endTrackerInfo(fbb)
|
||||
}
|
||||
|
||||
fun createTrackerPosition(fbb: FlatBufferBuilder, tracker: Tracker): Int = createVec3(fbb, tracker.position)
|
||||
|
||||
fun createTrackerRotation(fbb: FlatBufferBuilder, tracker: Tracker): Int = createQuat(fbb, tracker.getRawRotation())
|
||||
|
||||
fun createTrackerAcceleration(fbb: FlatBufferBuilder, tracker: Tracker): Int = createVec3(fbb, tracker.getAcceleration())
|
||||
|
||||
fun createTrackerMagneticVector(fbb: FlatBufferBuilder, tracker: Tracker): Int = createVec3(fbb, tracker.getMagVector())
|
||||
|
||||
fun createTrackerTemperature(fbb: FlatBufferBuilder, tracker: Tracker): Int {
|
||||
if (tracker.temperature == null) return 0
|
||||
return Temperature.createTemperature(fbb, tracker.temperature!!)
|
||||
}
|
||||
|
||||
fun createTrackerData(
|
||||
fbb: FlatBufferBuilder,
|
||||
mask: TrackerDataMaskT,
|
||||
tracker: Tracker,
|
||||
): Int {
|
||||
val trackerInfosOffset = createTrackerInfos(fbb, mask.info, tracker)
|
||||
val trackerIdOffset = createTrackerId(fbb, tracker)
|
||||
|
||||
var stayAlignedOffset = 0
|
||||
if (mask.stayAligned) {
|
||||
stayAlignedOffset =
|
||||
createTrackerStayAlignedTracker(fbb, tracker.stayAligned)
|
||||
}
|
||||
|
||||
TrackerData.startTrackerData(fbb)
|
||||
|
||||
TrackerData.addTrackerId(fbb, trackerIdOffset)
|
||||
|
||||
if (trackerInfosOffset != 0) TrackerData.addInfo(fbb, trackerInfosOffset)
|
||||
if (mask.status) TrackerData.addStatus(fbb, tracker.status.id + 1)
|
||||
if (mask.position && tracker.hasPosition) {
|
||||
TrackerData.addPosition(
|
||||
fbb,
|
||||
createTrackerPosition(fbb, tracker),
|
||||
)
|
||||
}
|
||||
if (mask.rotation && tracker.hasRotation) {
|
||||
TrackerData.addRotation(
|
||||
fbb,
|
||||
createTrackerRotation(fbb, tracker),
|
||||
)
|
||||
}
|
||||
if (mask.linearAcceleration && tracker.hasAcceleration) {
|
||||
TrackerData
|
||||
.addLinearAcceleration(
|
||||
fbb,
|
||||
createTrackerAcceleration(fbb, tracker),
|
||||
)
|
||||
}
|
||||
if (mask.temp) {
|
||||
val trackerTemperatureOffset = createTrackerTemperature(fbb, tracker)
|
||||
if (trackerTemperatureOffset != 0) {
|
||||
TrackerData.addTemp(
|
||||
fbb,
|
||||
trackerTemperatureOffset,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (tracker.allowMounting && tracker.hasRotation) {
|
||||
if (mask.rotationReferenceAdjusted) {
|
||||
TrackerData
|
||||
.addRotationReferenceAdjusted(
|
||||
fbb,
|
||||
createQuat(fbb, tracker.getRotation()),
|
||||
)
|
||||
}
|
||||
if (mask.rotationIdentityAdjusted) {
|
||||
TrackerData
|
||||
.addRotationIdentityAdjusted(
|
||||
fbb,
|
||||
createQuat(fbb, tracker.getIdentityAdjustedRotation()),
|
||||
)
|
||||
}
|
||||
} else if (tracker.allowReset && tracker.hasRotation) {
|
||||
if (mask.rotationReferenceAdjusted) {
|
||||
TrackerData
|
||||
.addRotationReferenceAdjusted(
|
||||
fbb,
|
||||
createQuat(fbb, tracker.getRotation()),
|
||||
)
|
||||
}
|
||||
if (mask.rotationIdentityAdjusted) {
|
||||
TrackerData
|
||||
.addRotationIdentityAdjusted(
|
||||
fbb,
|
||||
createQuat(fbb, tracker.getRawRotation()),
|
||||
)
|
||||
}
|
||||
}
|
||||
if (mask.tps) {
|
||||
TrackerData.addTps(fbb, tracker.tps.toInt())
|
||||
}
|
||||
if (mask.rawMagneticVector && tracker.magStatus == MagnetometerStatus.ENABLED) {
|
||||
TrackerData.addRawMagneticVector(
|
||||
fbb,
|
||||
createTrackerMagneticVector(fbb, tracker),
|
||||
)
|
||||
}
|
||||
if (mask.stayAligned) {
|
||||
TrackerData.addStayAligned(fbb, stayAlignedOffset)
|
||||
}
|
||||
|
||||
return TrackerData.endTrackerData(fbb)
|
||||
}
|
||||
|
||||
fun createTrackersData(
|
||||
fbb: FlatBufferBuilder,
|
||||
mask: DeviceDataMaskT,
|
||||
device: Device,
|
||||
): Int {
|
||||
if (mask.trackerData == null) return 0
|
||||
|
||||
val trackersOffsets: MutableList<Int> = ArrayList()
|
||||
|
||||
device
|
||||
.trackers
|
||||
.forEach { (_: Int, value: Tracker) ->
|
||||
trackersOffsets
|
||||
.add(createTrackerData(fbb, mask.trackerData, value))
|
||||
}
|
||||
|
||||
DeviceData.startTrackersVector(fbb, trackersOffsets.size)
|
||||
trackersOffsets.forEach(
|
||||
Consumer { offset: Int ->
|
||||
DeviceData.addTrackers(
|
||||
fbb,
|
||||
offset,
|
||||
)
|
||||
},
|
||||
)
|
||||
return fbb.endVector()
|
||||
}
|
||||
|
||||
fun createDeviceData(
|
||||
fbb: FlatBufferBuilder,
|
||||
id: Int,
|
||||
mask: DeviceDataMaskT,
|
||||
device: Device,
|
||||
): Int {
|
||||
if (!mask.deviceData) return 0
|
||||
|
||||
if (device.trackers.isEmpty()) return 0
|
||||
|
||||
var firstTracker = device.trackers[0]
|
||||
if (firstTracker == null) {
|
||||
// Not actually the "first" tracker, but do we care?
|
||||
firstTracker = device.trackers.entries.iterator().next().value
|
||||
}
|
||||
|
||||
val tracker: Tracker = firstTracker
|
||||
|
||||
HardwareStatus.startHardwareStatus(fbb)
|
||||
HardwareStatus.addErrorStatus(fbb, tracker.status.id)
|
||||
|
||||
if (tracker.batteryVoltage != null) {
|
||||
HardwareStatus.addBatteryVoltage(fbb, tracker.batteryVoltage!!)
|
||||
}
|
||||
if (tracker.batteryLevel != null) {
|
||||
HardwareStatus.addBatteryPctEstimate(fbb, tracker.batteryLevel!!.toInt())
|
||||
}
|
||||
if (tracker.ping != null) {
|
||||
HardwareStatus.addPing(fbb, tracker.ping!!)
|
||||
}
|
||||
if (tracker.signalStrength != null) {
|
||||
HardwareStatus.addRssi(fbb, tracker.signalStrength!!.toShort())
|
||||
}
|
||||
if (tracker.packetLoss != null) {
|
||||
HardwareStatus.addPacketLoss(fbb, tracker.packetLoss!!)
|
||||
}
|
||||
if (tracker.packetsLost != null) {
|
||||
HardwareStatus.addPacketsLost(fbb, tracker.packetsLost!!)
|
||||
}
|
||||
if (tracker.packetsReceived != null) {
|
||||
HardwareStatus.addPacketsReceived(fbb, tracker.packetsReceived!!)
|
||||
}
|
||||
|
||||
val hardwareDataOffset = HardwareStatus.endHardwareStatus(fbb)
|
||||
val hardwareInfoOffset = createHardwareInfo(fbb, device)
|
||||
val trackersOffset = createTrackersData(fbb, mask, device)
|
||||
|
||||
val nameOffset = if (device.name != null) {
|
||||
fbb.createString(device.name)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
return DeviceData.endDeviceData(fbb)
|
||||
}
|
||||
|
||||
fun createSyntheticTrackersData(
|
||||
fbb: FlatBufferBuilder,
|
||||
trackerDataMaskT: TrackerDataMaskT?,
|
||||
trackers: MutableList<Tracker>,
|
||||
): Int {
|
||||
if (trackerDataMaskT == null) return 0
|
||||
|
||||
val trackerOffsets: MutableList<Int> = ArrayList()
|
||||
|
||||
trackers
|
||||
.forEach(
|
||||
Consumer { tracker: Tracker ->
|
||||
trackerOffsets
|
||||
.add(createTrackerData(fbb, trackerDataMaskT, tracker))
|
||||
},
|
||||
)
|
||||
|
||||
DataFeedUpdate.startSyntheticTrackersVector(fbb, trackerOffsets.size)
|
||||
trackerOffsets.forEach(
|
||||
(
|
||||
Consumer { tracker: Int ->
|
||||
DataFeedUpdate.addSyntheticTrackers(
|
||||
fbb,
|
||||
tracker,
|
||||
)
|
||||
}
|
||||
),
|
||||
)
|
||||
return fbb.endVector()
|
||||
}
|
||||
|
||||
fun createDevicesData(
|
||||
fbb: FlatBufferBuilder,
|
||||
deviceDataMaskT: DeviceDataMaskT?,
|
||||
devices: MutableList<Device>,
|
||||
): Int {
|
||||
if (deviceDataMaskT == null) return 0
|
||||
|
||||
val devicesDataOffsets = IntArray(devices.size)
|
||||
for (i in devices.indices) {
|
||||
val device = devices[i]
|
||||
devicesDataOffsets[i] =
|
||||
createDeviceData(fbb, device.id, deviceDataMaskT, device)
|
||||
}
|
||||
|
||||
return DataFeedUpdate.createDevicesVector(fbb, devicesDataOffsets)
|
||||
}
|
||||
|
||||
fun createBonesData(
|
||||
fbb: FlatBufferBuilder,
|
||||
shouldSend: Boolean,
|
||||
bones: MutableList<Bone>,
|
||||
): Int {
|
||||
if (!shouldSend) {
|
||||
return 0
|
||||
}
|
||||
|
||||
val boneOffsets = IntArray(bones.size)
|
||||
for (i in bones.indices) {
|
||||
val bi = bones[i]
|
||||
|
||||
val headPosG =
|
||||
bi.getPosition()
|
||||
val rotG =
|
||||
bi.getGlobalRotation()
|
||||
val length = bi.length
|
||||
|
||||
solarxr_protocol.data_feed.Bone.startBone(fbb)
|
||||
|
||||
val rotGOffset = createQuat(fbb, rotG)
|
||||
solarxr_protocol.data_feed.Bone.addRotationG(fbb, rotGOffset)
|
||||
val headPosGOffset = Vec3f
|
||||
.createVec3f(fbb, headPosG.x, headPosG.y, headPosG.z)
|
||||
solarxr_protocol.data_feed.Bone.addHeadPositionG(fbb, headPosGOffset)
|
||||
solarxr_protocol.data_feed.Bone.addBodyPart(fbb, bi.boneType.bodyPart)
|
||||
solarxr_protocol.data_feed.Bone.addBoneLength(fbb, length)
|
||||
|
||||
boneOffsets[i] = solarxr_protocol.data_feed.Bone.endBone(fbb)
|
||||
}
|
||||
|
||||
return DataFeedUpdate.createBonesVector(fbb, boneOffsets)
|
||||
}
|
||||
|
||||
fun createStayAlignedPose(
|
||||
fbb: FlatBufferBuilder,
|
||||
humanSkeleton: HumanSkeleton,
|
||||
): Int {
|
||||
val relaxedPose = RelaxedPose.fromTrackers(humanSkeleton)
|
||||
|
||||
StayAlignedPose.startStayAlignedPose(fbb)
|
||||
|
||||
StayAlignedPose.addUpperLegAngleInDeg(fbb, relaxedPose.upperLeg.toDeg())
|
||||
StayAlignedPose.addLowerLegAngleInDeg(fbb, relaxedPose.lowerLeg.toDeg())
|
||||
StayAlignedPose.addFootAngleInDeg(fbb, relaxedPose.foot.toDeg())
|
||||
|
||||
return StayAlignedPose.endStayAlignedPose(fbb)
|
||||
}
|
||||
|
||||
fun createTrackerStayAlignedTracker(
|
||||
fbb: FlatBufferBuilder,
|
||||
state: StayAlignedTrackerState,
|
||||
): Int {
|
||||
StayAlignedTracker.startStayAlignedTracker(fbb)
|
||||
|
||||
StayAlignedTracker.addYawCorrectionInDeg(fbb, state.yawCorrection.toDeg())
|
||||
StayAlignedTracker.addLockedErrorInDeg(
|
||||
fbb,
|
||||
state.yawErrors.lockedError.toL2Norm().toDeg(),
|
||||
)
|
||||
StayAlignedTracker.addCenterErrorInDeg(
|
||||
fbb,
|
||||
state.yawErrors.centerError.toL2Norm().toDeg(),
|
||||
)
|
||||
StayAlignedTracker.addNeighborErrorInDeg(
|
||||
fbb,
|
||||
state.yawErrors.neighborError.toL2Norm().toDeg(),
|
||||
)
|
||||
StayAlignedTracker.addLocked(
|
||||
fbb,
|
||||
state.restDetector.state == RestDetector.State.AT_REST,
|
||||
)
|
||||
|
||||
return StayAlignedTracker.endStayAlignedTracker(fbb)
|
||||
}
|
||||
|
||||
fun createServerGuard(fbb: FlatBufferBuilder, serverGuards: ServerGuards): Int = solarxr_protocol.data_feed.server.ServerGuards.createServerGuards(
|
||||
fbb,
|
||||
serverGuards.canDoMounting,
|
||||
serverGuards.canDoYawReset,
|
||||
serverGuards.canDoUserHeightCalibration,
|
||||
)
|
||||
@@ -1,50 +0,0 @@
|
||||
package dev.slimevr.protocol.datafeed
|
||||
|
||||
import com.google.flatbuffers.FlatBufferBuilder
|
||||
import dev.slimevr.guards.ServerGuards
|
||||
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton
|
||||
import dev.slimevr.tracking.processor.stayaligned.poses.RelaxedPose
|
||||
import dev.slimevr.tracking.processor.stayaligned.trackers.RestDetector
|
||||
import dev.slimevr.tracking.processor.stayaligned.trackers.StayAlignedTrackerState
|
||||
import solarxr_protocol.data_feed.stay_aligned.StayAlignedPose
|
||||
import solarxr_protocol.data_feed.stay_aligned.StayAlignedTracker
|
||||
|
||||
object DataFeedBuilderKotlin {
|
||||
|
||||
fun createStayAlignedPose(
|
||||
fbb: FlatBufferBuilder,
|
||||
humanSkeleton: HumanSkeleton,
|
||||
): Int {
|
||||
val relaxedPose = RelaxedPose.fromTrackers(humanSkeleton)
|
||||
|
||||
StayAlignedPose.startStayAlignedPose(fbb)
|
||||
|
||||
StayAlignedPose.addUpperLegAngleInDeg(fbb, relaxedPose.upperLeg.toDeg())
|
||||
StayAlignedPose.addLowerLegAngleInDeg(fbb, relaxedPose.lowerLeg.toDeg())
|
||||
StayAlignedPose.addFootAngleInDeg(fbb, relaxedPose.foot.toDeg())
|
||||
|
||||
return StayAlignedPose.endStayAlignedPose(fbb)
|
||||
}
|
||||
|
||||
fun createTrackerStayAlignedTracker(
|
||||
fbb: FlatBufferBuilder,
|
||||
state: StayAlignedTrackerState,
|
||||
): Int {
|
||||
StayAlignedTracker.startStayAlignedTracker(fbb)
|
||||
|
||||
StayAlignedTracker.addYawCorrectionInDeg(fbb, state.yawCorrection.toDeg())
|
||||
StayAlignedTracker.addLockedErrorInDeg(fbb, state.yawErrors.lockedError.toL2Norm().toDeg())
|
||||
StayAlignedTracker.addCenterErrorInDeg(fbb, state.yawErrors.centerError.toL2Norm().toDeg())
|
||||
StayAlignedTracker.addNeighborErrorInDeg(fbb, state.yawErrors.neighborError.toL2Norm().toDeg())
|
||||
StayAlignedTracker.addLocked(fbb, state.restDetector.state == RestDetector.State.AT_REST)
|
||||
|
||||
return StayAlignedTracker.endStayAlignedTracker(fbb)
|
||||
}
|
||||
|
||||
fun createServerGuard(fbb: FlatBufferBuilder, serverGuards: ServerGuards): Int = solarxr_protocol.data_feed.server.ServerGuards.createServerGuards(
|
||||
fbb,
|
||||
serverGuards.canDoMounting,
|
||||
serverGuards.canDoYawReset,
|
||||
serverGuards.canDoUserHeightCalibration,
|
||||
)
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
package dev.slimevr.protocol.datafeed;
|
||||
|
||||
import com.google.flatbuffers.FlatBufferBuilder;
|
||||
import dev.slimevr.protocol.DataFeed;
|
||||
import dev.slimevr.protocol.GenericConnection;
|
||||
import dev.slimevr.protocol.ProtocolAPI;
|
||||
import dev.slimevr.protocol.ProtocolHandler;
|
||||
import dev.slimevr.tracking.trackers.Tracker;
|
||||
import io.eiren.util.logging.LogManager;
|
||||
import solarxr_protocol.MessageBundle;
|
||||
import solarxr_protocol.data_feed.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
public class DataFeedHandler extends ProtocolHandler<DataFeedMessageHeader> {
|
||||
private final ProtocolAPI api;
|
||||
|
||||
public DataFeedHandler(ProtocolAPI api) {
|
||||
this.api = api;
|
||||
|
||||
registerPacketListener(DataFeedMessage.StartDataFeed, this::onStartDataFeed);
|
||||
registerPacketListener(DataFeedMessage.PollDataFeed, this::onPollDataFeedRequest);
|
||||
|
||||
this.api.server.addOnTick(this::sendDataFeedUpdate);
|
||||
}
|
||||
|
||||
private void onStartDataFeed(GenericConnection conn, DataFeedMessageHeader header) {
|
||||
StartDataFeed req = (StartDataFeed) header.message(new StartDataFeed());
|
||||
if (req == null)
|
||||
return;
|
||||
int dataFeeds = req.dataFeedsLength();
|
||||
|
||||
List<DataFeed> feedList = conn.getContext().getDataFeedList();
|
||||
synchronized (feedList) {
|
||||
feedList.clear();
|
||||
for (int i = 0; i < dataFeeds; i++) {
|
||||
// Using the object api here because we
|
||||
// need to copy from the buffer, anyway let's
|
||||
// do it from here and send the reference to an arraylist
|
||||
DataFeedConfigT config = req.dataFeeds(i).unpack();
|
||||
feedList.add(new DataFeed(config));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onPollDataFeedRequest(
|
||||
GenericConnection conn,
|
||||
DataFeedMessageHeader messageHeader
|
||||
) {
|
||||
|
||||
PollDataFeed req = (PollDataFeed) messageHeader.message(new PollDataFeed());
|
||||
if (req == null)
|
||||
return;
|
||||
|
||||
FlatBufferBuilder fbb = new FlatBufferBuilder(300);
|
||||
|
||||
int messageOffset = this.buildDatafeed(fbb, req.config().unpack(), 0);
|
||||
|
||||
DataFeedMessageHeader.startDataFeedMessageHeader(fbb);
|
||||
DataFeedMessageHeader.addMessage(fbb, messageOffset);
|
||||
DataFeedMessageHeader.addMessageType(fbb, DataFeedMessage.DataFeedUpdate);
|
||||
int headerOffset = DataFeedMessageHeader.endDataFeedMessageHeader(fbb);
|
||||
|
||||
MessageBundle.startDataFeedMsgsVector(fbb, 1);
|
||||
MessageBundle.addDataFeedMsgs(fbb, headerOffset);
|
||||
int datafeedMessagesOffset = fbb.endVector();
|
||||
|
||||
int packet = createMessage(fbb, datafeedMessagesOffset);
|
||||
fbb.finish(packet);
|
||||
conn.send(fbb.dataBuffer());
|
||||
}
|
||||
|
||||
public int buildDatafeed(FlatBufferBuilder fbb, DataFeedConfigT config, int index) {
|
||||
int devicesOffset = DataFeedBuilder
|
||||
.createDevicesData(
|
||||
fbb,
|
||||
config.getDataMask(),
|
||||
this.api.server.deviceManager
|
||||
.getDevices()
|
||||
);
|
||||
// Synthetic tracker is computed tracker apparently
|
||||
int trackersOffset = DataFeedBuilder
|
||||
.createSyntheticTrackersData(
|
||||
fbb,
|
||||
config.getSyntheticTrackersMask(),
|
||||
this.api.server
|
||||
.getAllTrackers()
|
||||
.stream()
|
||||
.filter(Tracker::isComputed)
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
|
||||
var h = this.api.server.humanPoseManager;
|
||||
int bonesOffset = DataFeedBuilder
|
||||
.createBonesData(
|
||||
fbb,
|
||||
config.getBoneMask(),
|
||||
h.getAllBones()
|
||||
);
|
||||
|
||||
int stayAlignedPoseOffset = 0;
|
||||
if (config.getStayAlignedPoseMask()) {
|
||||
stayAlignedPoseOffset = DataFeedBuilderKotlin.INSTANCE
|
||||
.createStayAlignedPose(fbb, this.api.server.humanPoseManager.skeleton);
|
||||
}
|
||||
|
||||
int serverGuardsOffset = 0;
|
||||
if (config.getServerGuardsMask()) {
|
||||
serverGuardsOffset = DataFeedBuilderKotlin.INSTANCE
|
||||
.createServerGuard(fbb, this.api.server.getServerGuards());
|
||||
}
|
||||
|
||||
return DataFeedUpdate
|
||||
.createDataFeedUpdate(
|
||||
fbb,
|
||||
devicesOffset,
|
||||
trackersOffset,
|
||||
bonesOffset,
|
||||
stayAlignedPoseOffset,
|
||||
index,
|
||||
serverGuardsOffset
|
||||
);
|
||||
}
|
||||
|
||||
public void sendDataFeedUpdate() {
|
||||
long currTime = System.currentTimeMillis();
|
||||
|
||||
this.api.getAPIServers().forEach((server) -> server.getAPIConnections().forEach((conn) -> {
|
||||
FlatBufferBuilder fbb = null;
|
||||
|
||||
List<DataFeed> feedList = conn.getContext().getDataFeedList();
|
||||
synchronized (feedList) {
|
||||
int configsCount = feedList.size();
|
||||
|
||||
int[] data = new int[configsCount];
|
||||
|
||||
for (int index = 0; index < configsCount; index++) {
|
||||
DataFeed feed = feedList.get(index);
|
||||
Long lastTimeSent = feed.getTimeLastSent();
|
||||
DataFeedConfigT configT = feed.getConfig();
|
||||
if (currTime - lastTimeSent > configT.getMinimumTimeSinceLast()) {
|
||||
if (fbb == null) {
|
||||
// That way we create a buffer only when needed
|
||||
fbb = new FlatBufferBuilder(300);
|
||||
}
|
||||
|
||||
int messageOffset = this.buildDatafeed(fbb, configT, index);
|
||||
|
||||
DataFeedMessageHeader.startDataFeedMessageHeader(fbb);
|
||||
DataFeedMessageHeader.addMessage(fbb, messageOffset);
|
||||
DataFeedMessageHeader.addMessageType(fbb, DataFeedMessage.DataFeedUpdate);
|
||||
data[index] = DataFeedMessageHeader.endDataFeedMessageHeader(fbb);
|
||||
|
||||
feed.setTimeLastSent(currTime);
|
||||
int messages = MessageBundle.createDataFeedMsgsVector(fbb, data);
|
||||
int packet = createMessage(fbb, messages);
|
||||
fbb.finish(packet);
|
||||
conn.send(fbb.dataBuffer());
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(GenericConnection conn, DataFeedMessageHeader message) {
|
||||
BiConsumer<GenericConnection, DataFeedMessageHeader> consumer = this.handlers[message
|
||||
.messageType()];
|
||||
if (consumer != null)
|
||||
consumer.accept(conn, message);
|
||||
else
|
||||
LogManager
|
||||
.info(
|
||||
"[ProtocolAPI] Unhandled Datafeed packet received id: " + message.messageType()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int messagesCount() {
|
||||
return DataFeedMessage.names.length;
|
||||
}
|
||||
|
||||
public int createMessage(FlatBufferBuilder fbb, int datafeedMessagesOffset) {
|
||||
MessageBundle.startMessageBundle(fbb);
|
||||
if (datafeedMessagesOffset > -1)
|
||||
MessageBundle.addDataFeedMsgs(fbb, datafeedMessagesOffset);
|
||||
return MessageBundle.endMessageBundle(fbb);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package dev.slimevr.protocol.datafeed
|
||||
|
||||
import com.google.flatbuffers.FlatBufferBuilder
|
||||
import dev.slimevr.protocol.DataFeed
|
||||
import dev.slimevr.protocol.GenericConnection
|
||||
import dev.slimevr.protocol.ProtocolAPI
|
||||
import dev.slimevr.protocol.ProtocolAPIServer
|
||||
import dev.slimevr.protocol.ProtocolHandler
|
||||
import dev.slimevr.tracking.trackers.Tracker
|
||||
import io.eiren.util.logging.LogManager
|
||||
import solarxr_protocol.MessageBundle
|
||||
import solarxr_protocol.data_feed.DataFeedConfigT
|
||||
import solarxr_protocol.data_feed.DataFeedMessage
|
||||
import solarxr_protocol.data_feed.DataFeedMessageHeader
|
||||
import solarxr_protocol.data_feed.DataFeedUpdate
|
||||
import solarxr_protocol.data_feed.PollDataFeed
|
||||
import solarxr_protocol.data_feed.StartDataFeed
|
||||
import java.util.function.Consumer
|
||||
import java.util.stream.Collectors
|
||||
|
||||
class DataFeedHandler(private val api: ProtocolAPI) : ProtocolHandler<DataFeedMessageHeader>() {
|
||||
init {
|
||||
registerPacketListener(DataFeedMessage.StartDataFeed, ::onStartDataFeed)
|
||||
registerPacketListener(DataFeedMessage.PollDataFeed, ::onPollDataFeedRequest)
|
||||
this.api.server.addOnTick { this.sendDataFeedUpdate() }
|
||||
}
|
||||
|
||||
private fun onStartDataFeed(conn: GenericConnection, header: DataFeedMessageHeader) {
|
||||
val req = header.message(StartDataFeed()) as StartDataFeed? ?: return
|
||||
val dataFeeds = req.dataFeedsLength()
|
||||
|
||||
val feedList = conn.context.dataFeedList
|
||||
synchronized(feedList) {
|
||||
feedList.clear()
|
||||
for (i in 0..<dataFeeds) {
|
||||
// Using the object api here because we
|
||||
// need to copy from the buffer, anyway let's
|
||||
// do it from here and send the reference to an arraylist
|
||||
val config = req.dataFeeds(i).unpack()
|
||||
feedList.add(DataFeed(config))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onPollDataFeedRequest(
|
||||
conn: GenericConnection,
|
||||
messageHeader: DataFeedMessageHeader,
|
||||
) {
|
||||
val req = messageHeader.message(PollDataFeed()) as PollDataFeed? ?: return
|
||||
|
||||
val fbb = FlatBufferBuilder(300)
|
||||
|
||||
val messageOffset = this.buildDatafeed(fbb, req.config().unpack(), 0)
|
||||
|
||||
DataFeedMessageHeader.startDataFeedMessageHeader(fbb)
|
||||
DataFeedMessageHeader.addMessage(fbb, messageOffset)
|
||||
DataFeedMessageHeader.addMessageType(fbb, DataFeedMessage.DataFeedUpdate)
|
||||
val headerOffset = DataFeedMessageHeader.endDataFeedMessageHeader(fbb)
|
||||
|
||||
MessageBundle.startDataFeedMsgsVector(fbb, 1)
|
||||
MessageBundle.addDataFeedMsgs(fbb, headerOffset)
|
||||
val datafeedMessagesOffset = fbb.endVector()
|
||||
|
||||
val packet = createMessage(fbb, datafeedMessagesOffset)
|
||||
fbb.finish(packet)
|
||||
conn.send(fbb.dataBuffer())
|
||||
}
|
||||
|
||||
fun buildDatafeed(fbb: FlatBufferBuilder, config: DataFeedConfigT, index: Int): Int {
|
||||
val devicesOffset = createDevicesData(
|
||||
fbb,
|
||||
config.dataMask,
|
||||
this.api.server.deviceManager
|
||||
.devices,
|
||||
)
|
||||
// Synthetic tracker is computed tracker apparently
|
||||
val trackersOffset = createSyntheticTrackersData(
|
||||
fbb,
|
||||
config.syntheticTrackersMask,
|
||||
this.api.server
|
||||
.allTrackers
|
||||
.stream()
|
||||
.filter(Tracker::isComputed)
|
||||
.collect(Collectors.toList()),
|
||||
)
|
||||
|
||||
val h = this.api.server.humanPoseManager
|
||||
val bonesOffset =
|
||||
createBonesData(
|
||||
fbb,
|
||||
config.boneMask,
|
||||
h.allBones.toMutableList(),
|
||||
)
|
||||
|
||||
var stayAlignedPoseOffset = 0
|
||||
if (config.stayAlignedPoseMask) {
|
||||
stayAlignedPoseOffset = createStayAlignedPose(fbb, this.api.server.humanPoseManager.skeleton)
|
||||
}
|
||||
|
||||
var serverGuardsOffset = 0
|
||||
if (config.serverGuardsMask) {
|
||||
serverGuardsOffset = createServerGuard(fbb, this.api.server.serverGuards)
|
||||
}
|
||||
|
||||
return DataFeedUpdate
|
||||
.createDataFeedUpdate(
|
||||
fbb,
|
||||
devicesOffset,
|
||||
trackersOffset,
|
||||
bonesOffset,
|
||||
stayAlignedPoseOffset,
|
||||
index,
|
||||
serverGuardsOffset,
|
||||
)
|
||||
}
|
||||
|
||||
fun sendDataFeedUpdate() {
|
||||
val currTime = System.currentTimeMillis()
|
||||
|
||||
this.api.apiServers.forEach(
|
||||
Consumer { server: ProtocolAPIServer ->
|
||||
server.apiConnections.forEach { conn: GenericConnection ->
|
||||
var fbb: FlatBufferBuilder? = null
|
||||
val feedList = conn.context.dataFeedList
|
||||
synchronized(feedList) {
|
||||
val configsCount = feedList.size
|
||||
val data = IntArray(configsCount)
|
||||
for (index in 0..<configsCount) {
|
||||
val feed = feedList[index]
|
||||
val lastTimeSent = feed.timeLastSent
|
||||
val configT = feed.config
|
||||
if (currTime - lastTimeSent > configT.minimumTimeSinceLast) {
|
||||
if (fbb == null) {
|
||||
// That way we create a buffer only when needed
|
||||
fbb = FlatBufferBuilder(300)
|
||||
}
|
||||
|
||||
val messageOffset = this.buildDatafeed(fbb, configT, index)
|
||||
|
||||
DataFeedMessageHeader.startDataFeedMessageHeader(fbb)
|
||||
DataFeedMessageHeader.addMessage(fbb, messageOffset)
|
||||
DataFeedMessageHeader.addMessageType(fbb, DataFeedMessage.DataFeedUpdate)
|
||||
data[index] = DataFeedMessageHeader.endDataFeedMessageHeader(fbb)
|
||||
|
||||
feed.timeLastSent = currTime
|
||||
val messages = MessageBundle.createDataFeedMsgsVector(fbb, data)
|
||||
val packet = createMessage(fbb, messages)
|
||||
fbb.finish(packet)
|
||||
conn.send(fbb.dataBuffer())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override fun onMessage(conn: GenericConnection, message: DataFeedMessageHeader) {
|
||||
val consumer = this.handlers[message.messageType().toInt()]
|
||||
if (consumer != null) {
|
||||
consumer.accept(conn, message)
|
||||
} else {
|
||||
LogManager
|
||||
.info(
|
||||
"[ProtocolAPI] Unhandled Datafeed packet received id: " + message.messageType(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun messagesCount(): Int = DataFeedMessage.names.size
|
||||
|
||||
fun createMessage(fbb: FlatBufferBuilder, datafeedMessagesOffset: Int): Int {
|
||||
MessageBundle.startMessageBundle(fbb)
|
||||
if (datafeedMessagesOffset > -1) MessageBundle.addDataFeedMsgs(fbb, datafeedMessagesOffset)
|
||||
return MessageBundle.endMessageBundle(fbb)
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package dev.slimevr.protocol.pubsub;
|
||||
|
||||
import solarxr_protocol.pub_sub.TopicIdT;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
// This class is so the HashMap referencing the TopicId as key works
|
||||
// it needs a unique hashcode based on the topicId and also an equals function
|
||||
// because equals hashcode does not mean equals strings
|
||||
public class HashedTopicId {
|
||||
|
||||
private final TopicIdT inner;
|
||||
private final int hashcode;
|
||||
|
||||
public HashedTopicId(TopicIdT topicIdT) {
|
||||
this.inner = topicIdT;
|
||||
this.hashcode = (inner.getAppName()
|
||||
+ "."
|
||||
+ inner.getOrganization()
|
||||
+ "."
|
||||
+ inner.getTopic()).hashCode();
|
||||
}
|
||||
|
||||
public TopicIdT getInner() {
|
||||
return inner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashcode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
HashedTopicId that = (HashedTopicId) o;
|
||||
return Objects.equals(inner.getOrganization(), that.getInner().getOrganization())
|
||||
&& Objects.equals(inner.getAppName(), that.getInner().getAppName())
|
||||
&& Objects.equals(inner.getTopic(), that.getInner().getTopic());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package dev.slimevr.protocol.pubsub
|
||||
|
||||
import solarxr_protocol.pub_sub.TopicIdT
|
||||
|
||||
// This class is so the HashMap referencing the TopicId as key works
|
||||
// it needs a unique hashcode based on the topicId and also an equals function
|
||||
// because equals hashcode does not mean equals strings
|
||||
class HashedTopicId(val inner: TopicIdT) {
|
||||
private val hashcode: Int = (
|
||||
(
|
||||
inner.appName +
|
||||
"." +
|
||||
inner.organization +
|
||||
"." +
|
||||
inner.topic
|
||||
)
|
||||
).hashCode()
|
||||
|
||||
override fun hashCode(): Int = hashcode
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || javaClass != other.javaClass) return false
|
||||
val that = other as HashedTopicId
|
||||
return inner.organization == that.inner.organization &&
|
||||
inner.appName == that.inner.appName &&
|
||||
inner.topic == that.inner.topic
|
||||
}
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
package dev.slimevr.protocol.pubsub;
|
||||
|
||||
import com.google.flatbuffers.FlatBufferBuilder;
|
||||
import dev.slimevr.protocol.GenericConnection;
|
||||
import dev.slimevr.protocol.ProtocolAPI;
|
||||
import dev.slimevr.protocol.ProtocolHandler;
|
||||
import io.eiren.util.logging.LogManager;
|
||||
import solarxr_protocol.MessageBundle;
|
||||
import solarxr_protocol.pub_sub.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
|
||||
public class PubSubHandler extends ProtocolHandler<PubSubHeader> {
|
||||
|
||||
private final ProtocolAPI api;
|
||||
|
||||
// Two ways maps for faster reading when handling lots of packets
|
||||
public HashMap<HashedTopicId, Integer> topicsHandle = new HashMap<>();
|
||||
public HashMap<Integer, HashedTopicId> handleTopics = new HashMap<>();
|
||||
|
||||
public AtomicInteger nextLocalHandle = new AtomicInteger();
|
||||
|
||||
public PubSubHandler(ProtocolAPI api) {
|
||||
super();
|
||||
this.api = api;
|
||||
|
||||
registerPacketListener(PubSubUnion.SubscriptionRequest, this::onSubscriptionRequest);
|
||||
registerPacketListener(PubSubUnion.TopicHandleRequest, this::onTopicHandleRequest);
|
||||
registerPacketListener(PubSubUnion.Message, this::onTopicMessage);
|
||||
}
|
||||
|
||||
private int getTopicHandle(TopicIdT topicIdT) {
|
||||
HashedTopicId hashedTopicId = new HashedTopicId(topicIdT);
|
||||
Integer handleT = topicsHandle.get(hashedTopicId);
|
||||
// if no handle exists for this topic id we create one and return it
|
||||
// anyway
|
||||
if (handleT == null) {
|
||||
handleT = nextLocalHandle.incrementAndGet();
|
||||
topicsHandle.put(hashedTopicId, handleT);
|
||||
handleTopics.put(handleT, hashedTopicId);
|
||||
}
|
||||
|
||||
return handleT;
|
||||
}
|
||||
|
||||
public void onSubscriptionRequest(GenericConnection conn, PubSubHeader messageHeader) {
|
||||
SubscriptionRequest req = (SubscriptionRequest) messageHeader.u(new SubscriptionRequest());
|
||||
|
||||
if (req == null)
|
||||
return;
|
||||
|
||||
int subHandle = -1;
|
||||
if (req.topicType() == Topic.TopicHandle) {
|
||||
TopicHandle handle = (TopicHandle) req.topic(new TopicHandle());
|
||||
if (handle != null && handleTopics.containsKey(handle.id()))
|
||||
subHandle = handle.id();
|
||||
} else if (req.topicType() == Topic.TopicId) {
|
||||
TopicId topicId = (TopicId) req.topic(new TopicId());
|
||||
if (topicId != null)
|
||||
subHandle = getTopicHandle(topicId.unpack());
|
||||
}
|
||||
|
||||
assert subHandle != -1;
|
||||
|
||||
final int finalSubHandle = subHandle;
|
||||
Optional<Integer> first = conn
|
||||
.getContext()
|
||||
.getSubscribedTopics()
|
||||
.stream()
|
||||
.filter((handle) -> handle == finalSubHandle)
|
||||
.findFirst();
|
||||
if (!first.isPresent()) {
|
||||
conn.getContext().getSubscribedTopics().add(finalSubHandle);
|
||||
}
|
||||
|
||||
|
||||
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
|
||||
int topicIdOffset = TopicId.pack(fbb, handleTopics.get(finalSubHandle).getInner());
|
||||
int topicHandleOffset = TopicHandle.createTopicHandle(fbb, finalSubHandle);
|
||||
|
||||
int outbound = createMessage(
|
||||
fbb,
|
||||
PubSubUnion.TopicMapping,
|
||||
TopicMapping.createTopicMapping(fbb, topicIdOffset, topicHandleOffset)
|
||||
);
|
||||
fbb.finish(outbound);
|
||||
conn.send(fbb.dataBuffer());
|
||||
}
|
||||
|
||||
public void onTopicHandleRequest(GenericConnection conn, PubSubHeader messageHeader) {
|
||||
|
||||
TopicHandleRequest req = (TopicHandleRequest) messageHeader.u(new TopicHandleRequest());
|
||||
|
||||
if (req == null)
|
||||
return;
|
||||
|
||||
TopicHandleRequestT topicRequest = req.unpack();
|
||||
int handle = getTopicHandle(topicRequest.getId());
|
||||
|
||||
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
|
||||
int topicIdOffset = TopicId.pack(fbb, topicRequest.getId());
|
||||
int topicHandleOffset = TopicHandle.createTopicHandle(fbb, handle);
|
||||
|
||||
int outbound = createMessage(
|
||||
fbb,
|
||||
PubSubUnion.TopicMapping,
|
||||
TopicMapping.createTopicMapping(fbb, topicIdOffset, topicHandleOffset)
|
||||
);
|
||||
fbb.finish(outbound);
|
||||
conn.send(fbb.dataBuffer());
|
||||
}
|
||||
|
||||
|
||||
public void onTopicMessage(GenericConnection c, PubSubHeader messageHeader) {
|
||||
Message req = (Message) messageHeader.u(new Message());
|
||||
|
||||
if (req == null)
|
||||
return;
|
||||
|
||||
MessageT messageT = req.unpack();
|
||||
|
||||
int subHandle = 1;
|
||||
if (messageT.getTopic().getType() == Topic.TopicHandle) {
|
||||
subHandle = messageT.getTopic().asTopicHandle().getId();
|
||||
} else if (messageT.getTopic().getType() == Topic.TopicId) {
|
||||
subHandle = getTopicHandle(messageT.getTopic().asTopicId());
|
||||
}
|
||||
|
||||
|
||||
assert subHandle != -1;
|
||||
|
||||
int finalSubHandle = subHandle;
|
||||
|
||||
this.api.getAPIServers().forEach((server) -> {
|
||||
server.getAPIConnections().forEach((conn) -> {
|
||||
// Make sure that we are not sending a message to ourselves
|
||||
// And check that the receiver has subscribed to the topic
|
||||
if (
|
||||
!conn.getConnectionId().equals(c.getConnectionId())
|
||||
&& conn.getContext().getSubscribedTopics().contains(finalSubHandle)
|
||||
) {
|
||||
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
|
||||
int outbound = createMessage(
|
||||
fbb,
|
||||
PubSubUnion.Message,
|
||||
Message.pack(fbb, messageT)
|
||||
);
|
||||
fbb.finish(outbound);
|
||||
conn.send(fbb.dataBuffer());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(GenericConnection conn, PubSubHeader message) {
|
||||
BiConsumer<GenericConnection, PubSubHeader> consumer = this.handlers[message.uType()];
|
||||
if (consumer != null)
|
||||
consumer.accept(conn, message);
|
||||
else
|
||||
LogManager
|
||||
.info("[ProtocolAPI] Unhandled PubSub packet received id: " + message.uType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int messagesCount() {
|
||||
return PubSubUnion.names.length;
|
||||
}
|
||||
|
||||
public int createMessage(FlatBufferBuilder fbb, byte messageType, int messageOffset) {
|
||||
int[] data = new int[1];
|
||||
|
||||
data[0] = PubSubHeader.createPubSubHeader(fbb, messageType, messageOffset);
|
||||
|
||||
int messages = MessageBundle.createPubSubMsgsVector(fbb, data);
|
||||
|
||||
MessageBundle.startMessageBundle(fbb);
|
||||
MessageBundle.addPubSubMsgs(fbb, messages);
|
||||
return MessageBundle.endMessageBundle(fbb);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
package dev.slimevr.protocol.pubsub
|
||||
|
||||
import com.google.flatbuffers.FlatBufferBuilder
|
||||
import dev.slimevr.protocol.GenericConnection
|
||||
import dev.slimevr.protocol.ProtocolAPI
|
||||
import dev.slimevr.protocol.ProtocolAPIServer
|
||||
import dev.slimevr.protocol.ProtocolHandler
|
||||
import io.eiren.util.logging.LogManager
|
||||
import solarxr_protocol.MessageBundle
|
||||
import solarxr_protocol.pub_sub.Message
|
||||
import solarxr_protocol.pub_sub.PubSubHeader
|
||||
import solarxr_protocol.pub_sub.PubSubUnion
|
||||
import solarxr_protocol.pub_sub.SubscriptionRequest
|
||||
import solarxr_protocol.pub_sub.Topic
|
||||
import solarxr_protocol.pub_sub.TopicHandle
|
||||
import solarxr_protocol.pub_sub.TopicHandleRequest
|
||||
import solarxr_protocol.pub_sub.TopicId
|
||||
import solarxr_protocol.pub_sub.TopicIdT
|
||||
import solarxr_protocol.pub_sub.TopicMapping
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.function.Consumer
|
||||
|
||||
class PubSubHandler(private val api: ProtocolAPI) : ProtocolHandler<PubSubHeader>() {
|
||||
// Two ways maps for faster reading when handling lots of packets
|
||||
var topicsHandle: HashMap<HashedTopicId, Int> = HashMap()
|
||||
var handleTopics: HashMap<Int, HashedTopicId> = HashMap()
|
||||
|
||||
var nextLocalHandle: AtomicInteger = AtomicInteger()
|
||||
|
||||
init {
|
||||
registerPacketListener(PubSubUnion.SubscriptionRequest, ::onSubscriptionRequest)
|
||||
registerPacketListener(PubSubUnion.TopicHandleRequest, ::onTopicHandleRequest)
|
||||
registerPacketListener(PubSubUnion.Message, ::onTopicMessage)
|
||||
}
|
||||
|
||||
private fun getTopicHandle(topicIdT: TopicIdT): Int {
|
||||
val hashedTopicId = HashedTopicId(topicIdT)
|
||||
var handleT = topicsHandle.get(hashedTopicId)
|
||||
// if no handle exists for this topic id we create one and return it
|
||||
// anyway
|
||||
if (handleT == null) {
|
||||
handleT = nextLocalHandle.incrementAndGet()
|
||||
topicsHandle[hashedTopicId] = handleT
|
||||
handleTopics[handleT] = hashedTopicId
|
||||
}
|
||||
|
||||
return handleT
|
||||
}
|
||||
|
||||
fun onSubscriptionRequest(conn: GenericConnection, messageHeader: PubSubHeader) {
|
||||
val req =
|
||||
messageHeader.u(SubscriptionRequest()) as SubscriptionRequest? ?: return
|
||||
|
||||
var subHandle = -1
|
||||
if (req.topicType() == Topic.TopicHandle) {
|
||||
val handle = req.topic(TopicHandle()) as TopicHandle?
|
||||
if (handle != null && handleTopics.containsKey(handle.id())) subHandle = handle.id()
|
||||
} else if (req.topicType() == Topic.TopicId) {
|
||||
val topicId = req.topic(TopicId()) as TopicId?
|
||||
if (topicId != null) subHandle = getTopicHandle(topicId.unpack())
|
||||
}
|
||||
|
||||
assert(subHandle != -1)
|
||||
|
||||
val finalSubHandle = subHandle
|
||||
val first = conn
|
||||
.context
|
||||
.subscribedTopics
|
||||
.stream()
|
||||
.filter { handle: Int -> handle == finalSubHandle }
|
||||
.findFirst()
|
||||
if (!first.isPresent) {
|
||||
conn.context.subscribedTopics.add(finalSubHandle)
|
||||
}
|
||||
|
||||
val fbb = FlatBufferBuilder(32)
|
||||
val topicIdOffset = TopicId.pack(fbb, handleTopics.get(finalSubHandle)!!.inner)
|
||||
val topicHandleOffset = TopicHandle.createTopicHandle(fbb, finalSubHandle)
|
||||
|
||||
val outbound = createMessage(
|
||||
fbb,
|
||||
PubSubUnion.TopicMapping,
|
||||
TopicMapping.createTopicMapping(fbb, topicIdOffset, topicHandleOffset),
|
||||
)
|
||||
fbb.finish(outbound)
|
||||
conn.send(fbb.dataBuffer())
|
||||
}
|
||||
|
||||
fun onTopicHandleRequest(conn: GenericConnection, messageHeader: PubSubHeader) {
|
||||
val req = messageHeader.u(TopicHandleRequest()) as TopicHandleRequest? ?: return
|
||||
|
||||
val topicRequest = req.unpack()
|
||||
val handle = getTopicHandle(topicRequest.id)
|
||||
|
||||
val fbb = FlatBufferBuilder(32)
|
||||
val topicIdOffset = TopicId.pack(fbb, topicRequest.id)
|
||||
val topicHandleOffset = TopicHandle.createTopicHandle(fbb, handle)
|
||||
|
||||
val outbound = createMessage(
|
||||
fbb,
|
||||
PubSubUnion.TopicMapping,
|
||||
TopicMapping.createTopicMapping(fbb, topicIdOffset, topicHandleOffset),
|
||||
)
|
||||
fbb.finish(outbound)
|
||||
conn.send(fbb.dataBuffer())
|
||||
}
|
||||
|
||||
fun onTopicMessage(c: GenericConnection, messageHeader: PubSubHeader) {
|
||||
val req = messageHeader.u(Message()) as Message? ?: return
|
||||
|
||||
val messageT = req.unpack()
|
||||
|
||||
var subHandle = 1
|
||||
if (messageT.topic.type == Topic.TopicHandle) {
|
||||
subHandle = messageT.topic.asTopicHandle().id
|
||||
} else if (messageT.topic.type == Topic.TopicId) {
|
||||
subHandle = getTopicHandle(messageT.topic.asTopicId())
|
||||
}
|
||||
|
||||
assert(subHandle != -1)
|
||||
|
||||
val finalSubHandle = subHandle
|
||||
|
||||
this.api.apiServers.forEach(
|
||||
Consumer { server: ProtocolAPIServer ->
|
||||
server.apiConnections.forEach { conn: GenericConnection ->
|
||||
// Make sure that we are not sending a message to ourselves
|
||||
// And check that the receiver has subscribed to the topic
|
||||
if (conn.connectionId != c.connectionId &&
|
||||
conn.context.subscribedTopics
|
||||
.contains(finalSubHandle)
|
||||
) {
|
||||
val fbb = FlatBufferBuilder(32)
|
||||
val outbound = createMessage(
|
||||
fbb,
|
||||
PubSubUnion.Message,
|
||||
Message.pack(fbb, messageT),
|
||||
)
|
||||
fbb.finish(outbound)
|
||||
conn.send(fbb.dataBuffer())
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override fun onMessage(conn: GenericConnection, message: PubSubHeader) {
|
||||
val consumer = this.handlers[message.uType().toInt()]
|
||||
if (consumer != null) {
|
||||
consumer.accept(conn, message)
|
||||
} else {
|
||||
LogManager
|
||||
.info("[ProtocolAPI] Unhandled PubSub packet received id: " + message.uType())
|
||||
}
|
||||
}
|
||||
|
||||
override fun messagesCount(): Int = PubSubUnion.names.size
|
||||
|
||||
fun createMessage(fbb: FlatBufferBuilder, messageType: Byte, messageOffset: Int): Int {
|
||||
val data = IntArray(1)
|
||||
|
||||
data[0] = PubSubHeader.createPubSubHeader(fbb, messageType, messageOffset)
|
||||
|
||||
val messages = MessageBundle.createPubSubMsgsVector(fbb, data)
|
||||
|
||||
MessageBundle.startMessageBundle(fbb)
|
||||
MessageBundle.addPubSubMsgs(fbb, messages)
|
||||
return MessageBundle.endMessageBundle(fbb)
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package dev.slimevr.protocol.rpc;
|
||||
|
||||
import com.google.flatbuffers.FlatBufferBuilder;
|
||||
import dev.slimevr.tracking.processor.HumanPoseManager;
|
||||
import dev.slimevr.tracking.processor.config.SkeletonConfigOffsets;
|
||||
import solarxr_protocol.rpc.SkeletonConfigResponse;
|
||||
import solarxr_protocol.rpc.SkeletonPart;
|
||||
|
||||
|
||||
public class RPCBuilder {
|
||||
|
||||
public static int createSkeletonConfig(
|
||||
FlatBufferBuilder fbb,
|
||||
HumanPoseManager humanPoseManager
|
||||
) {
|
||||
int[] partsOffsets = new int[SkeletonConfigOffsets.values().length];
|
||||
|
||||
for (int index = 0; index < SkeletonConfigOffsets.values().length; index++) {
|
||||
SkeletonConfigOffsets val = SkeletonConfigOffsets.values[index];
|
||||
int part = SkeletonPart
|
||||
.createSkeletonPart(fbb, val.id, humanPoseManager.getOffset(val));
|
||||
partsOffsets[index] = part;
|
||||
}
|
||||
|
||||
int parts = SkeletonConfigResponse.createSkeletonPartsVector(fbb, partsOffsets);
|
||||
return SkeletonConfigResponse.createSkeletonConfigResponse(fbb, parts, 0);
|
||||
}
|
||||
}
|
||||
@@ -6,15 +6,15 @@ 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.datafeed.createTrackerId
|
||||
import dev.slimevr.protocol.rpc.autobone.RPCAutoBoneHandler
|
||||
import dev.slimevr.protocol.rpc.firmware.RPCFirmwareUpdateHandler
|
||||
import dev.slimevr.protocol.rpc.games.vrchat.RPCVRChatHandler
|
||||
import dev.slimevr.protocol.rpc.reset.RPCResetHandler
|
||||
import dev.slimevr.protocol.rpc.serial.RPCProvisioningHandler
|
||||
import dev.slimevr.protocol.rpc.serial.RPCSerialHandler
|
||||
import dev.slimevr.protocol.rpc.settings.RPCSettingsBuilder
|
||||
import dev.slimevr.protocol.rpc.settings.RPCSettingsHandler
|
||||
import dev.slimevr.protocol.rpc.settings.createSettingsResponse
|
||||
import dev.slimevr.protocol.rpc.setup.RPCHandshakeHandler
|
||||
import dev.slimevr.protocol.rpc.setup.RPCTapSetupHandler
|
||||
import dev.slimevr.protocol.rpc.setup.RPCUtil.getLocalIp
|
||||
@@ -460,7 +460,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
|
||||
}
|
||||
|
||||
val tracker = api.server.getTrackerById(req.trackerId().unpack()) ?: return
|
||||
val trackerId = DataFeedBuilder.createTrackerId(fbb, tracker)
|
||||
val trackerId = createTrackerId(fbb, tracker)
|
||||
val response = MagToggleResponse.createMagToggleResponse(
|
||||
fbb,
|
||||
trackerId,
|
||||
@@ -499,7 +499,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
|
||||
// Don't apply magnetometer setting if use magnetometer global setting is not enabled
|
||||
if (!api.server.configManager.vrConfig.server.useMagnetometerOnAllTrackers) {
|
||||
val fbb = FlatBufferBuilder(32)
|
||||
val trackerId = DataFeedBuilder.createTrackerId(fbb, tracker)
|
||||
val trackerId = createTrackerId(fbb, tracker)
|
||||
val response = MagToggleResponse.createMagToggleResponse(
|
||||
fbb,
|
||||
trackerId,
|
||||
@@ -516,7 +516,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
|
||||
}
|
||||
|
||||
val fbb = FlatBufferBuilder(32)
|
||||
val trackerId = DataFeedBuilder.createTrackerId(fbb, tracker)
|
||||
val trackerId = createTrackerId(fbb, tracker)
|
||||
val response = MagToggleResponse.createMagToggleResponse(
|
||||
fbb,
|
||||
trackerId,
|
||||
@@ -607,7 +607,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
|
||||
|
||||
fun sendSettingsChangedResponse(conn: GenericConnection, messageHeader: RpcMessageHeader?) {
|
||||
val fbb = FlatBufferBuilder(32)
|
||||
val settings = RPCSettingsBuilder.createSettingsResponse(fbb, api.server)
|
||||
val settings = createSettingsResponse(fbb, api.server)
|
||||
val outbound = createRPCMessage(fbb, RpcMessage.SettingsResponse, settings, messageHeader)
|
||||
fbb.finish(outbound)
|
||||
conn.send(fbb.dataBuffer())
|
||||
|
||||
@@ -50,8 +50,8 @@ class RPCAutoBoneHandler(
|
||||
) {
|
||||
val req = messageHeader
|
||||
.message(AutoBoneProcessRequest()) as AutoBoneProcessRequest
|
||||
if (conn.context.useAutoBone()) return
|
||||
conn.context.setUseAutoBone(true)
|
||||
if (conn.context.useAutoBone) return
|
||||
conn.context.useAutoBone = true
|
||||
api.server
|
||||
.autoBoneHandler
|
||||
.startProcessByType(getById(req.processType()))
|
||||
@@ -67,7 +67,7 @@ class RPCAutoBoneHandler(
|
||||
success: Boolean,
|
||||
) {
|
||||
forAllListeners { conn ->
|
||||
if (!conn.context.useAutoBone()) {
|
||||
if (!conn.context.useAutoBone) {
|
||||
return@forAllListeners
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ class RPCAutoBoneHandler(
|
||||
fbb.finish(outbound)
|
||||
conn.send(fbb.dataBuffer())
|
||||
if (completed) {
|
||||
conn.context.setUseAutoBone(false)
|
||||
conn.context.useAutoBone = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,7 +106,7 @@ class RPCAutoBoneHandler(
|
||||
|
||||
override fun onAutoBoneEpoch(epoch: Epoch) {
|
||||
forAllListeners { conn ->
|
||||
if (!conn.context.useAutoBone()) {
|
||||
if (!conn.context.useAutoBone) {
|
||||
return@forAllListeners
|
||||
}
|
||||
|
||||
|
||||
@@ -39,24 +39,39 @@ class RPCResetHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) : ResetL
|
||||
}
|
||||
|
||||
if (req.resetType() == ResetType.Yaw) {
|
||||
if (bodyParts.isEmpty()) {
|
||||
api.server.scheduleResetTrackersYaw(RESET_SOURCE_NAME, (resetsConfig.yawResetDelay * 1000).toLong())
|
||||
val delay = if (req.hasDelay()) {
|
||||
req.delay()
|
||||
} else {
|
||||
api.server.scheduleResetTrackersYaw(RESET_SOURCE_NAME, (resetsConfig.yawResetDelay * 1000).toLong(), bodyParts.toList())
|
||||
resetsConfig.yawResetDelay
|
||||
}
|
||||
if (bodyParts.isEmpty()) {
|
||||
api.server.scheduleResetTrackersYaw(RESET_SOURCE_NAME, (delay * 1000).toLong())
|
||||
} else {
|
||||
api.server.scheduleResetTrackersYaw(RESET_SOURCE_NAME, (delay * 1000).toLong(), bodyParts.toList())
|
||||
}
|
||||
}
|
||||
if (req.resetType() == ResetType.Full) {
|
||||
if (bodyParts.isEmpty()) {
|
||||
api.server.scheduleResetTrackersFull(RESET_SOURCE_NAME, (resetsConfig.fullResetDelay * 1000).toLong())
|
||||
val delay = if (req.hasDelay()) {
|
||||
req.delay()
|
||||
} else {
|
||||
api.server.scheduleResetTrackersFull(RESET_SOURCE_NAME, (resetsConfig.fullResetDelay * 1000).toLong(), bodyParts.toList())
|
||||
resetsConfig.fullResetDelay
|
||||
}
|
||||
if (bodyParts.isEmpty()) {
|
||||
api.server.scheduleResetTrackersFull(RESET_SOURCE_NAME, (delay * 1000).toLong())
|
||||
} else {
|
||||
api.server.scheduleResetTrackersFull(RESET_SOURCE_NAME, (delay * 1000).toLong(), bodyParts.toList())
|
||||
}
|
||||
}
|
||||
if (req.resetType() == ResetType.Mounting) {
|
||||
if (bodyParts.isEmpty()) {
|
||||
api.server.scheduleResetTrackersMounting(RESET_SOURCE_NAME, (resetsConfig.mountingResetDelay * 1000).toLong())
|
||||
val delay = if (req.hasDelay()) {
|
||||
req.delay()
|
||||
} else {
|
||||
api.server.scheduleResetTrackersMounting(RESET_SOURCE_NAME, (resetsConfig.mountingResetDelay * 1000).toLong(), bodyParts.toList())
|
||||
resetsConfig.mountingResetDelay
|
||||
}
|
||||
if (bodyParts.isEmpty()) {
|
||||
api.server.scheduleResetTrackersMounting(RESET_SOURCE_NAME, (delay * 1000).toLong())
|
||||
} else {
|
||||
api.server.scheduleResetTrackersMounting(RESET_SOURCE_NAME, (delay * 1000).toLong(), bodyParts.toList())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,11 +123,11 @@ class RPCResetHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) : ResetL
|
||||
|
||||
fun forAllListeners(action: Consumer<in GenericConnection?>?) {
|
||||
this.api
|
||||
.getAPIServers()
|
||||
.apiServers
|
||||
.forEach(
|
||||
Consumer { server: ProtocolAPIServer? ->
|
||||
server!!
|
||||
.getAPIConnections()
|
||||
Consumer { server: ProtocolAPIServer ->
|
||||
server
|
||||
.apiConnections
|
||||
.forEach(action)
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
package dev.slimevr.protocol.rpc.serial;
|
||||
|
||||
import com.google.flatbuffers.FlatBufferBuilder;
|
||||
import dev.slimevr.protocol.GenericConnection;
|
||||
import dev.slimevr.protocol.ProtocolAPI;
|
||||
import dev.slimevr.protocol.rpc.RPCHandler;
|
||||
import dev.slimevr.serial.ProvisioningListener;
|
||||
import dev.slimevr.serial.ProvisioningStatus;
|
||||
import dev.slimevr.serial.SerialPort;
|
||||
import solarxr_protocol.rpc.*;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
|
||||
public class RPCProvisioningHandler implements ProvisioningListener {
|
||||
|
||||
public RPCHandler rpcHandler;
|
||||
public ProtocolAPI api;
|
||||
|
||||
public RPCProvisioningHandler(RPCHandler rpcHandler, ProtocolAPI api) {
|
||||
this.rpcHandler = rpcHandler;
|
||||
this.api = api;
|
||||
|
||||
rpcHandler
|
||||
.registerPacketListener(
|
||||
RpcMessage.StartWifiProvisioningRequest,
|
||||
this::onStartWifiProvisioningRequest
|
||||
);
|
||||
rpcHandler
|
||||
.registerPacketListener(
|
||||
RpcMessage.StopWifiProvisioningRequest,
|
||||
this::onStopWifiProvisioningRequest
|
||||
);
|
||||
|
||||
this.api.server.provisioningHandler.addListener(this);
|
||||
}
|
||||
|
||||
public void onStartWifiProvisioningRequest(
|
||||
GenericConnection conn,
|
||||
RpcMessageHeader messageHeader
|
||||
) {
|
||||
StartWifiProvisioningRequest req = (StartWifiProvisioningRequest) messageHeader
|
||||
.message(new StartWifiProvisioningRequest());
|
||||
if (req == null)
|
||||
return;
|
||||
this.api.server.provisioningHandler.start(req.ssid(), req.password(), req.port());
|
||||
conn.getContext().setUseProvisioning(true);
|
||||
}
|
||||
|
||||
public void onStopWifiProvisioningRequest(
|
||||
GenericConnection conn,
|
||||
RpcMessageHeader messageHeader
|
||||
) {
|
||||
StopWifiProvisioningRequest req = (StopWifiProvisioningRequest) messageHeader
|
||||
.message(new StopWifiProvisioningRequest());
|
||||
if (req == null)
|
||||
return;
|
||||
conn.getContext().setUseProvisioning(false);
|
||||
this.api.server.provisioningHandler.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProvisioningStatusChange(ProvisioningStatus status, SerialPort port) {
|
||||
|
||||
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
|
||||
|
||||
WifiProvisioningStatusResponse.startWifiProvisioningStatusResponse(fbb);
|
||||
WifiProvisioningStatusResponse.addStatus(fbb, status.id);
|
||||
int update = WifiProvisioningStatusResponse.endWifiProvisioningStatusResponse(fbb);
|
||||
int outbound = rpcHandler
|
||||
.createRPCMessage(fbb, RpcMessage.WifiProvisioningStatusResponse, update);
|
||||
fbb.finish(outbound);
|
||||
|
||||
this.forAllListeners((conn) -> conn.send(fbb.dataBuffer()));
|
||||
}
|
||||
|
||||
private void forAllListeners(Consumer<? super GenericConnection> action) {
|
||||
this.api
|
||||
.getAPIServers()
|
||||
.forEach(
|
||||
(server) -> server
|
||||
.getAPIConnections()
|
||||
.filter(conn -> conn.getContext().useProvisioning())
|
||||
.forEach(action)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package dev.slimevr.protocol.rpc.serial
|
||||
|
||||
import com.google.flatbuffers.FlatBufferBuilder
|
||||
import dev.slimevr.protocol.GenericConnection
|
||||
import dev.slimevr.protocol.ProtocolAPI
|
||||
import dev.slimevr.protocol.ProtocolAPIServer
|
||||
import dev.slimevr.protocol.rpc.RPCHandler
|
||||
import dev.slimevr.serial.ProvisioningListener
|
||||
import dev.slimevr.serial.ProvisioningStatus
|
||||
import dev.slimevr.serial.SerialPort
|
||||
import solarxr_protocol.rpc.*
|
||||
import java.util.function.Consumer
|
||||
|
||||
class RPCProvisioningHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) : ProvisioningListener {
|
||||
init {
|
||||
rpcHandler.registerPacketListener(RpcMessage.StartWifiProvisioningRequest, ::onStartWifiProvisioningRequest)
|
||||
rpcHandler.registerPacketListener(RpcMessage.StopWifiProvisioningRequest, ::onStopWifiProvisioningRequest)
|
||||
this.api.server.provisioningHandler.addListener(this)
|
||||
}
|
||||
|
||||
fun onStartWifiProvisioningRequest(
|
||||
conn: GenericConnection,
|
||||
messageHeader: RpcMessageHeader,
|
||||
) {
|
||||
val req = messageHeader
|
||||
.message(StartWifiProvisioningRequest()) as StartWifiProvisioningRequest?
|
||||
if (req == null) return
|
||||
this.api.server.provisioningHandler.start(req.ssid(), req.password(), req.port())
|
||||
conn.context.useProvisioning = true
|
||||
}
|
||||
|
||||
fun onStopWifiProvisioningRequest(
|
||||
conn: GenericConnection,
|
||||
messageHeader: RpcMessageHeader,
|
||||
) {
|
||||
val req = messageHeader
|
||||
.message(StopWifiProvisioningRequest()) as StopWifiProvisioningRequest?
|
||||
if (req == null) return
|
||||
conn.context.useProvisioning = false
|
||||
this.api.server.provisioningHandler.stop()
|
||||
}
|
||||
|
||||
override fun onProvisioningStatusChange(status: ProvisioningStatus, port: SerialPort?) {
|
||||
val fbb = FlatBufferBuilder(32)
|
||||
|
||||
WifiProvisioningStatusResponse.startWifiProvisioningStatusResponse(fbb)
|
||||
WifiProvisioningStatusResponse.addStatus(fbb, status.id)
|
||||
val update = WifiProvisioningStatusResponse.endWifiProvisioningStatusResponse(fbb)
|
||||
val outbound = rpcHandler
|
||||
.createRPCMessage(fbb, RpcMessage.WifiProvisioningStatusResponse, update)
|
||||
fbb.finish(outbound)
|
||||
|
||||
this.forAllListeners(Consumer { conn: GenericConnection -> conn.send(fbb.dataBuffer()) })
|
||||
}
|
||||
|
||||
private fun forAllListeners(action: Consumer<in GenericConnection?>?) {
|
||||
this.api
|
||||
.apiServers
|
||||
.forEach(
|
||||
Consumer { server: ProtocolAPIServer ->
|
||||
server
|
||||
.apiConnections
|
||||
.filter { conn: GenericConnection -> conn.context.useProvisioning }
|
||||
.forEach(action)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,303 +0,0 @@
|
||||
package dev.slimevr.protocol.rpc.serial;
|
||||
|
||||
import com.google.flatbuffers.FlatBufferBuilder;
|
||||
import dev.slimevr.protocol.GenericConnection;
|
||||
import dev.slimevr.protocol.ProtocolAPI;
|
||||
import dev.slimevr.protocol.rpc.RPCHandler;
|
||||
import dev.slimevr.serial.SerialListener;
|
||||
import dev.slimevr.serial.SerialPort;
|
||||
import io.eiren.util.logging.LogManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import solarxr_protocol.rpc.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
|
||||
public class RPCSerialHandler implements SerialListener {
|
||||
|
||||
public RPCHandler rpcHandler;
|
||||
public ProtocolAPI api;
|
||||
|
||||
public RPCSerialHandler(RPCHandler rpcHandler, ProtocolAPI api) {
|
||||
this.rpcHandler = rpcHandler;
|
||||
this.api = api;
|
||||
|
||||
rpcHandler
|
||||
.registerPacketListener(
|
||||
RpcMessage.SerialTrackerRebootRequest,
|
||||
this::onSerialTrackerRebootRequest
|
||||
);
|
||||
rpcHandler
|
||||
.registerPacketListener(
|
||||
RpcMessage.SerialTrackerGetInfoRequest,
|
||||
this::onSerialTrackerGetInfoRequest
|
||||
);
|
||||
rpcHandler
|
||||
.registerPacketListener(
|
||||
RpcMessage.SerialTrackerFactoryResetRequest,
|
||||
this::onSerialTrackerFactoryResetRequest
|
||||
);
|
||||
rpcHandler
|
||||
.registerPacketListener(
|
||||
RpcMessage.SerialTrackerGetWifiScanRequest,
|
||||
this::onSerialTrackerGetWifiScanRequest
|
||||
);
|
||||
rpcHandler
|
||||
.registerPacketListener(
|
||||
RpcMessage.SerialTrackerCustomCommandRequest,
|
||||
this::onSerialTrackerCustomCommandRequest
|
||||
);
|
||||
rpcHandler.registerPacketListener(RpcMessage.SetWifiRequest, this::onSetWifiRequest);
|
||||
rpcHandler.registerPacketListener(RpcMessage.OpenSerialRequest, this::onOpenSerialRequest);
|
||||
rpcHandler
|
||||
.registerPacketListener(RpcMessage.CloseSerialRequest, this::onCloseSerialRequest);
|
||||
rpcHandler
|
||||
.registerPacketListener(RpcMessage.SerialDevicesRequest, this::onRequestSerialDevices);
|
||||
this.api.server.serialHandler.addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSerialDisconnected() {
|
||||
|
||||
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
|
||||
|
||||
SerialUpdateResponse.startSerialUpdateResponse(fbb);
|
||||
SerialUpdateResponse.addClosed(fbb, true);
|
||||
int update = SerialUpdateResponse.endSerialUpdateResponse(fbb);
|
||||
int outbound = rpcHandler.createRPCMessage(fbb, RpcMessage.SerialUpdateResponse, update);
|
||||
fbb.finish(outbound);
|
||||
|
||||
this.forAllListeners((conn) -> {
|
||||
conn.send(fbb.dataBuffer());
|
||||
conn.getContext().setUseSerial(false);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSerialLog(String str, boolean server) {
|
||||
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
|
||||
|
||||
int logOffset = fbb.createString(str);
|
||||
|
||||
SerialUpdateResponse.startSerialUpdateResponse(fbb);
|
||||
SerialUpdateResponse.addLog(fbb, logOffset);
|
||||
int update = SerialUpdateResponse.endSerialUpdateResponse(fbb);
|
||||
int outbound = rpcHandler.createRPCMessage(fbb, RpcMessage.SerialUpdateResponse, update);
|
||||
fbb.finish(outbound);
|
||||
|
||||
this.forAllListeners((conn) -> {
|
||||
conn.send(fbb.dataBuffer());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewSerialDevice(SerialPort port) {
|
||||
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
|
||||
|
||||
int portOffset = fbb.createString(port.getPortLocation());
|
||||
int nameOffset = fbb.createString(port.getDescriptivePortName());
|
||||
int deviceOffset = SerialDevice.createSerialDevice(fbb, portOffset, nameOffset);
|
||||
int newSerialOffset = NewSerialDeviceResponse
|
||||
.createNewSerialDeviceResponse(fbb, deviceOffset);
|
||||
int outbound = rpcHandler
|
||||
.createRPCMessage(fbb, RpcMessage.NewSerialDeviceResponse, newSerialOffset);
|
||||
fbb.finish(outbound);
|
||||
|
||||
|
||||
this.api
|
||||
.getAPIServers()
|
||||
.forEach(
|
||||
(server) -> server
|
||||
.getAPIConnections()
|
||||
.forEach((conn) -> {
|
||||
conn.send(fbb.dataBuffer());
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSerialConnected(SerialPort port) {
|
||||
|
||||
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
|
||||
|
||||
SerialUpdateResponse.startSerialUpdateResponse(fbb);
|
||||
SerialUpdateResponse.addClosed(fbb, false);
|
||||
int update = SerialUpdateResponse.endSerialUpdateResponse(fbb);
|
||||
int outbound = rpcHandler.createRPCMessage(fbb, RpcMessage.SerialUpdateResponse, update);
|
||||
fbb.finish(outbound);
|
||||
|
||||
this.forAllListeners((conn) -> {
|
||||
conn.send(fbb.dataBuffer());
|
||||
});
|
||||
}
|
||||
|
||||
public void onSerialTrackerRebootRequest(
|
||||
GenericConnection conn,
|
||||
RpcMessageHeader messageHeader
|
||||
) {
|
||||
SerialTrackerRebootRequest req = (SerialTrackerRebootRequest) messageHeader
|
||||
.message(new SerialTrackerRebootRequest());
|
||||
if (req == null)
|
||||
return;
|
||||
|
||||
this.api.server.serialHandler.rebootRequest();
|
||||
}
|
||||
|
||||
public void onSerialTrackerGetInfoRequest(
|
||||
GenericConnection conn,
|
||||
RpcMessageHeader messageHeader
|
||||
) {
|
||||
SerialTrackerGetInfoRequest req = (SerialTrackerGetInfoRequest) messageHeader
|
||||
.message(new SerialTrackerGetInfoRequest());
|
||||
if (req == null)
|
||||
return;
|
||||
|
||||
this.api.server.serialHandler.infoRequest();
|
||||
}
|
||||
|
||||
public void onSerialTrackerFactoryResetRequest(
|
||||
GenericConnection conn,
|
||||
RpcMessageHeader messageHeader
|
||||
) {
|
||||
SerialTrackerFactoryResetRequest req = (SerialTrackerFactoryResetRequest) messageHeader
|
||||
.message(new SerialTrackerFactoryResetRequest());
|
||||
if (req == null)
|
||||
return;
|
||||
|
||||
this.api.server.serialHandler.factoryResetRequest();
|
||||
}
|
||||
|
||||
public void onSerialTrackerGetWifiScanRequest(
|
||||
GenericConnection conn,
|
||||
RpcMessageHeader messageHeader
|
||||
) {
|
||||
SerialTrackerGetWifiScanRequest req = (SerialTrackerGetWifiScanRequest) messageHeader
|
||||
.message(new SerialTrackerGetWifiScanRequest());
|
||||
if (req == null)
|
||||
return;
|
||||
|
||||
this.api.server.serialHandler.wifiScanRequest();
|
||||
}
|
||||
|
||||
public void onSerialTrackerCustomCommandRequest(
|
||||
GenericConnection conn,
|
||||
RpcMessageHeader messageHeader
|
||||
) {
|
||||
SerialTrackerCustomCommandRequest req = (SerialTrackerCustomCommandRequest) messageHeader
|
||||
.message(new SerialTrackerCustomCommandRequest());
|
||||
|
||||
if (req == null || req.command() == null)
|
||||
return;
|
||||
|
||||
this.api.server.serialHandler.customCommandRequest(Objects.requireNonNull(req.command()));
|
||||
}
|
||||
|
||||
private void onRequestSerialDevices(GenericConnection conn, RpcMessageHeader messageHeader) {
|
||||
SerialDevicesRequest req = (SerialDevicesRequest) messageHeader
|
||||
.message(new SerialDevicesRequest());
|
||||
if (req == null)
|
||||
return;
|
||||
|
||||
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
|
||||
|
||||
|
||||
List<Integer> devicesOffsets = new ArrayList<>();
|
||||
|
||||
try {
|
||||
this.api.server.serialHandler.getKnownPorts().forEach((port) -> {
|
||||
int portOffset = fbb.createString(port.getPortLocation());
|
||||
int nameOffset = fbb.createString(port.getDescriptivePortName());
|
||||
devicesOffsets.add(SerialDevice.createSerialDevice(fbb, portOffset, nameOffset));
|
||||
});
|
||||
} catch (Throwable e) {
|
||||
LogManager.severe("Using serial ports is not supported on this platform", e);
|
||||
}
|
||||
|
||||
SerialDevicesResponse.startDevicesVector(fbb, devicesOffsets.size());
|
||||
devicesOffsets.forEach(offset -> SerialDevicesResponse.addDevices(fbb, offset));
|
||||
int devices = fbb.endVector();
|
||||
int serialDeviceOffsets = SerialDevicesResponse.createSerialDevicesResponse(fbb, devices);
|
||||
int outbound = rpcHandler
|
||||
.createRPCMessage(fbb, RpcMessage.SerialDevicesResponse, serialDeviceOffsets);
|
||||
fbb.finish(outbound);
|
||||
conn.send(fbb.dataBuffer());
|
||||
}
|
||||
|
||||
public void onSetWifiRequest(GenericConnection conn, RpcMessageHeader messageHeader) {
|
||||
SetWifiRequest req = (SetWifiRequest) messageHeader.message(new SetWifiRequest());
|
||||
if (req == null)
|
||||
return;
|
||||
|
||||
if (
|
||||
req.password() == null
|
||||
|| req.ssid() == null
|
||||
|| !this.api.server.serialHandler.isConnected()
|
||||
)
|
||||
return;
|
||||
this.api.server.serialHandler.setWifi(req.ssid(), req.password());
|
||||
}
|
||||
|
||||
public void onOpenSerialRequest(GenericConnection conn, RpcMessageHeader messageHeader) {
|
||||
OpenSerialRequest req = (OpenSerialRequest) messageHeader.message(new OpenSerialRequest());
|
||||
if (req == null)
|
||||
return;
|
||||
|
||||
conn.getContext().setUseSerial(true);
|
||||
|
||||
this.api.server.queueTask(() -> {
|
||||
try {
|
||||
this.api.server.serialHandler.openSerial(req.port(), req.auto());
|
||||
} catch (Exception e) {
|
||||
LogManager.severe("Unable to open serial port", e);
|
||||
} catch (Throwable e) {
|
||||
LogManager.severe("Using serial ports is not supported on this platform", e);
|
||||
}
|
||||
|
||||
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
|
||||
SerialUpdateResponse.startSerialUpdateResponse(fbb);
|
||||
SerialUpdateResponse.addClosed(fbb, !this.api.server.serialHandler.isConnected());
|
||||
int update = SerialUpdateResponse.endSerialUpdateResponse(fbb);
|
||||
int outbound = rpcHandler
|
||||
.createRPCMessage(fbb, RpcMessage.SerialUpdateResponse, update);
|
||||
fbb.finish(outbound);
|
||||
conn.send(fbb.dataBuffer());
|
||||
});
|
||||
}
|
||||
|
||||
public void onCloseSerialRequest(GenericConnection conn, RpcMessageHeader messageHeader) {
|
||||
CloseSerialRequest req = (CloseSerialRequest) messageHeader
|
||||
.message(new CloseSerialRequest());
|
||||
if (req == null)
|
||||
return;
|
||||
|
||||
conn.getContext().setUseSerial(false);
|
||||
|
||||
this.api.server.serialHandler.closeSerial();
|
||||
|
||||
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
|
||||
SerialUpdateResponse.startSerialUpdateResponse(fbb);
|
||||
SerialUpdateResponse.addClosed(fbb, !this.api.server.serialHandler.isConnected());
|
||||
int update = SerialUpdateResponse.endSerialUpdateResponse(fbb);
|
||||
int outbound = rpcHandler.createRPCMessage(fbb, RpcMessage.SerialUpdateResponse, update);
|
||||
fbb.finish(outbound);
|
||||
conn.send(fbb.dataBuffer());
|
||||
}
|
||||
|
||||
public void forAllListeners(Consumer<? super GenericConnection> action) {
|
||||
this.api
|
||||
.getAPIServers()
|
||||
.forEach(
|
||||
(server) -> server
|
||||
.getAPIConnections()
|
||||
.filter(conn -> conn.getContext().useSerial())
|
||||
.forEach(action)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSerialDeviceDeleted(@NotNull SerialPort port) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
package dev.slimevr.protocol.rpc.serial
|
||||
|
||||
import com.google.flatbuffers.FlatBufferBuilder
|
||||
import dev.slimevr.protocol.GenericConnection
|
||||
import dev.slimevr.protocol.ProtocolAPI
|
||||
import dev.slimevr.protocol.ProtocolAPIServer
|
||||
import dev.slimevr.protocol.rpc.RPCHandler
|
||||
import dev.slimevr.serial.SerialListener
|
||||
import dev.slimevr.serial.SerialPort
|
||||
import io.eiren.util.logging.LogManager
|
||||
import solarxr_protocol.rpc.*
|
||||
import java.util.*
|
||||
import java.util.function.Consumer
|
||||
|
||||
class RPCSerialHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) : SerialListener {
|
||||
init {
|
||||
rpcHandler.registerPacketListener(RpcMessage.SerialTrackerRebootRequest, ::onSerialTrackerRebootRequest)
|
||||
rpcHandler.registerPacketListener(RpcMessage.SerialTrackerGetInfoRequest, ::onSerialTrackerGetInfoRequest)
|
||||
rpcHandler.registerPacketListener(RpcMessage.SerialTrackerFactoryResetRequest, ::onSerialTrackerFactoryResetRequest)
|
||||
rpcHandler.registerPacketListener(RpcMessage.SerialTrackerGetWifiScanRequest, ::onSerialTrackerGetWifiScanRequest)
|
||||
rpcHandler.registerPacketListener(RpcMessage.SerialTrackerCustomCommandRequest, ::onSerialTrackerCustomCommandRequest)
|
||||
rpcHandler.registerPacketListener(RpcMessage.SetWifiRequest, ::onSetWifiRequest)
|
||||
rpcHandler.registerPacketListener(RpcMessage.OpenSerialRequest, ::onOpenSerialRequest)
|
||||
rpcHandler.registerPacketListener(RpcMessage.CloseSerialRequest, ::onCloseSerialRequest)
|
||||
rpcHandler.registerPacketListener(RpcMessage.SerialDevicesRequest, ::onRequestSerialDevices)
|
||||
this.api.server.serialHandler.addListener(this)
|
||||
}
|
||||
|
||||
override fun onSerialDisconnected() {
|
||||
val fbb = FlatBufferBuilder(32)
|
||||
|
||||
SerialUpdateResponse.startSerialUpdateResponse(fbb)
|
||||
SerialUpdateResponse.addClosed(fbb, true)
|
||||
val update = SerialUpdateResponse.endSerialUpdateResponse(fbb)
|
||||
val outbound = rpcHandler.createRPCMessage(fbb, RpcMessage.SerialUpdateResponse, update)
|
||||
fbb.finish(outbound)
|
||||
|
||||
this.forAllListeners(
|
||||
Consumer { conn: GenericConnection ->
|
||||
conn.send(fbb.dataBuffer())
|
||||
conn.context.useSerial = false
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override fun onSerialLog(str: String, server: Boolean) {
|
||||
val fbb = FlatBufferBuilder(32)
|
||||
|
||||
val logOffset = fbb.createString(str)
|
||||
|
||||
SerialUpdateResponse.startSerialUpdateResponse(fbb)
|
||||
SerialUpdateResponse.addLog(fbb, logOffset)
|
||||
val update = SerialUpdateResponse.endSerialUpdateResponse(fbb)
|
||||
val outbound = rpcHandler.createRPCMessage(fbb, RpcMessage.SerialUpdateResponse, update)
|
||||
fbb.finish(outbound)
|
||||
|
||||
this.forAllListeners(
|
||||
Consumer { conn: GenericConnection ->
|
||||
conn.send(fbb.dataBuffer())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override fun onNewSerialDevice(port: SerialPort) {
|
||||
val fbb = FlatBufferBuilder(32)
|
||||
|
||||
val portOffset = fbb.createString(port.portLocation)
|
||||
val nameOffset = fbb.createString(port.descriptivePortName)
|
||||
val deviceOffset = SerialDevice.createSerialDevice(fbb, portOffset, nameOffset)
|
||||
val newSerialOffset = NewSerialDeviceResponse
|
||||
.createNewSerialDeviceResponse(fbb, deviceOffset)
|
||||
val outbound = rpcHandler
|
||||
.createRPCMessage(fbb, RpcMessage.NewSerialDeviceResponse, newSerialOffset)
|
||||
fbb.finish(outbound)
|
||||
|
||||
this.api
|
||||
.apiServers
|
||||
.forEach(
|
||||
Consumer { server: ProtocolAPIServer ->
|
||||
server
|
||||
.apiConnections
|
||||
.forEach { conn: GenericConnection ->
|
||||
conn.send(fbb.dataBuffer())
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override fun onSerialConnected(port: SerialPort) {
|
||||
val fbb = FlatBufferBuilder(32)
|
||||
|
||||
SerialUpdateResponse.startSerialUpdateResponse(fbb)
|
||||
SerialUpdateResponse.addClosed(fbb, false)
|
||||
val update = SerialUpdateResponse.endSerialUpdateResponse(fbb)
|
||||
val outbound = rpcHandler.createRPCMessage(fbb, RpcMessage.SerialUpdateResponse, update)
|
||||
fbb.finish(outbound)
|
||||
|
||||
this.forAllListeners(
|
||||
Consumer { conn: GenericConnection ->
|
||||
conn.send(fbb.dataBuffer())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fun onSerialTrackerRebootRequest(
|
||||
conn: GenericConnection,
|
||||
messageHeader: RpcMessageHeader,
|
||||
) {
|
||||
val req = messageHeader
|
||||
.message(SerialTrackerRebootRequest()) as SerialTrackerRebootRequest?
|
||||
if (req == null) return
|
||||
|
||||
this.api.server.serialHandler.rebootRequest()
|
||||
}
|
||||
|
||||
fun onSerialTrackerGetInfoRequest(
|
||||
conn: GenericConnection,
|
||||
messageHeader: RpcMessageHeader,
|
||||
) {
|
||||
val req = messageHeader
|
||||
.message(SerialTrackerGetInfoRequest()) as SerialTrackerGetInfoRequest?
|
||||
if (req == null) return
|
||||
|
||||
this.api.server.serialHandler.infoRequest()
|
||||
}
|
||||
|
||||
fun onSerialTrackerFactoryResetRequest(
|
||||
conn: GenericConnection,
|
||||
messageHeader: RpcMessageHeader,
|
||||
) {
|
||||
val req = messageHeader
|
||||
.message(SerialTrackerFactoryResetRequest()) as SerialTrackerFactoryResetRequest?
|
||||
if (req == null) return
|
||||
|
||||
this.api.server.serialHandler.factoryResetRequest()
|
||||
}
|
||||
|
||||
fun onSerialTrackerGetWifiScanRequest(
|
||||
conn: GenericConnection,
|
||||
messageHeader: RpcMessageHeader,
|
||||
) {
|
||||
val req = messageHeader
|
||||
.message(SerialTrackerGetWifiScanRequest()) as SerialTrackerGetWifiScanRequest?
|
||||
if (req == null) return
|
||||
|
||||
this.api.server.serialHandler.wifiScanRequest()
|
||||
}
|
||||
|
||||
fun onSerialTrackerCustomCommandRequest(
|
||||
conn: GenericConnection,
|
||||
messageHeader: RpcMessageHeader,
|
||||
) {
|
||||
val req = messageHeader
|
||||
.message(SerialTrackerCustomCommandRequest()) as SerialTrackerCustomCommandRequest?
|
||||
|
||||
if (req == null || req.command() == null) return
|
||||
|
||||
this.api.server.serialHandler.customCommandRequest(Objects.requireNonNull(req.command()))
|
||||
}
|
||||
|
||||
private fun onRequestSerialDevices(conn: GenericConnection, messageHeader: RpcMessageHeader) {
|
||||
val req = messageHeader
|
||||
.message(SerialDevicesRequest()) as SerialDevicesRequest?
|
||||
if (req == null) return
|
||||
|
||||
val fbb = FlatBufferBuilder(32)
|
||||
|
||||
val devicesOffsets: MutableList<Int> = ArrayList()
|
||||
|
||||
try {
|
||||
this.api.server.serialHandler.knownPorts.forEach { port: SerialPort ->
|
||||
val portOffset = fbb.createString(port.portLocation)
|
||||
val nameOffset = fbb.createString(port.descriptivePortName)
|
||||
devicesOffsets.add(SerialDevice.createSerialDevice(fbb, portOffset, nameOffset))
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
LogManager.severe("Using serial ports is not supported on this platform", e)
|
||||
}
|
||||
|
||||
SerialDevicesResponse.startDevicesVector(fbb, devicesOffsets.size)
|
||||
devicesOffsets.forEach(Consumer { offset: Int -> SerialDevicesResponse.addDevices(fbb, offset) })
|
||||
val devices = fbb.endVector()
|
||||
val serialDeviceOffsets = SerialDevicesResponse.createSerialDevicesResponse(fbb, devices)
|
||||
val outbound = rpcHandler
|
||||
.createRPCMessage(fbb, RpcMessage.SerialDevicesResponse, serialDeviceOffsets)
|
||||
fbb.finish(outbound)
|
||||
conn.send(fbb.dataBuffer())
|
||||
}
|
||||
|
||||
fun onSetWifiRequest(conn: GenericConnection, messageHeader: RpcMessageHeader) {
|
||||
val req = messageHeader.message(SetWifiRequest()) as SetWifiRequest? ?: return
|
||||
|
||||
if (req.password() == null || req.ssid() == null || !this.api.server.serialHandler.isConnected) {
|
||||
return
|
||||
}
|
||||
this.api.server.serialHandler.setWifi(req.ssid(), req.password())
|
||||
}
|
||||
|
||||
fun onOpenSerialRequest(conn: GenericConnection, messageHeader: RpcMessageHeader) {
|
||||
val req =
|
||||
messageHeader.message(OpenSerialRequest()) as OpenSerialRequest? ?: return
|
||||
|
||||
conn.context.useSerial = true
|
||||
|
||||
this.api.server.queueTask {
|
||||
try {
|
||||
this.api.server.serialHandler.openSerial(req.port(), req.auto())
|
||||
} catch (e: Exception) {
|
||||
LogManager.severe("Unable to open serial port", e)
|
||||
} catch (e: Throwable) {
|
||||
LogManager.severe(
|
||||
"Using serial ports is not supported on this platform",
|
||||
e,
|
||||
)
|
||||
}
|
||||
val fbb = FlatBufferBuilder(32)
|
||||
SerialUpdateResponse.startSerialUpdateResponse(fbb)
|
||||
SerialUpdateResponse.addClosed(
|
||||
fbb,
|
||||
!this.api.server.serialHandler.isConnected,
|
||||
)
|
||||
val update = SerialUpdateResponse.endSerialUpdateResponse(fbb)
|
||||
val outbound = rpcHandler
|
||||
.createRPCMessage(fbb, RpcMessage.SerialUpdateResponse, update)
|
||||
fbb.finish(outbound)
|
||||
conn.send(fbb.dataBuffer())
|
||||
}
|
||||
}
|
||||
|
||||
fun onCloseSerialRequest(conn: GenericConnection, messageHeader: RpcMessageHeader) {
|
||||
val req = messageHeader
|
||||
.message(CloseSerialRequest()) as CloseSerialRequest?
|
||||
if (req == null) return
|
||||
|
||||
conn.context.useSerial = false
|
||||
|
||||
this.api.server.serialHandler.closeSerial()
|
||||
|
||||
val fbb = FlatBufferBuilder(32)
|
||||
SerialUpdateResponse.startSerialUpdateResponse(fbb)
|
||||
SerialUpdateResponse.addClosed(fbb, !this.api.server.serialHandler.isConnected)
|
||||
val update = SerialUpdateResponse.endSerialUpdateResponse(fbb)
|
||||
val outbound = rpcHandler.createRPCMessage(fbb, RpcMessage.SerialUpdateResponse, update)
|
||||
fbb.finish(outbound)
|
||||
conn.send(fbb.dataBuffer())
|
||||
}
|
||||
|
||||
fun forAllListeners(action: Consumer<in GenericConnection?>?) {
|
||||
this.api
|
||||
.apiServers
|
||||
.forEach(
|
||||
Consumer { server: ProtocolAPIServer ->
|
||||
server
|
||||
.apiConnections
|
||||
.filter { conn: GenericConnection -> conn.context.useSerial }
|
||||
.forEach(action)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override fun onSerialDeviceDeleted(port: SerialPort) {
|
||||
}
|
||||
}
|
||||
@@ -1,423 +0,0 @@
|
||||
package dev.slimevr.protocol.rpc.settings;
|
||||
|
||||
import com.google.flatbuffers.FlatBufferBuilder;
|
||||
import dev.slimevr.VRServer;
|
||||
import dev.slimevr.bridge.ISteamVRBridge;
|
||||
import dev.slimevr.config.*;
|
||||
import dev.slimevr.filtering.TrackerFilters;
|
||||
import dev.slimevr.tracking.processor.HumanPoseManager;
|
||||
import dev.slimevr.tracking.processor.config.SkeletonConfigToggles;
|
||||
import dev.slimevr.tracking.processor.config.SkeletonConfigValues;
|
||||
import dev.slimevr.tracking.trackers.TrackerRole;
|
||||
import solarxr_protocol.rpc.*;
|
||||
import solarxr_protocol.rpc.settings.*;
|
||||
|
||||
|
||||
public class RPCSettingsBuilder {
|
||||
|
||||
public static int createOSCRouterSettings(
|
||||
FlatBufferBuilder fbb,
|
||||
OSCConfig config
|
||||
) {
|
||||
int addressStringOffset = fbb.createString(config.getAddress());
|
||||
|
||||
int oscSettingOffset = OSCSettings
|
||||
.createOSCSettings(
|
||||
fbb,
|
||||
config.getEnabled(),
|
||||
config.getPortIn(),
|
||||
config.getPortOut(),
|
||||
addressStringOffset
|
||||
);
|
||||
|
||||
OSCRouterSettings.startOSCRouterSettings(fbb);
|
||||
OSCRouterSettings.addOscSettings(fbb, oscSettingOffset);
|
||||
|
||||
return OSCRouterSettings.endOSCRouterSettings(fbb);
|
||||
}
|
||||
|
||||
public static int createVRCOSCSettings(
|
||||
FlatBufferBuilder fbb,
|
||||
VRCOSCConfig config
|
||||
) {
|
||||
int addressStringOffset = fbb.createString(config.getAddress());
|
||||
int generalSettingOffset = OSCSettings
|
||||
.createOSCSettings(
|
||||
fbb,
|
||||
config.getEnabled(),
|
||||
config.getPortIn(),
|
||||
config.getPortOut(),
|
||||
addressStringOffset
|
||||
);
|
||||
int oscSettingOffset = OSCTrackersSetting
|
||||
.createOSCTrackersSetting(
|
||||
fbb,
|
||||
config.getOSCTrackerRole(TrackerRole.HEAD, false),
|
||||
config.getOSCTrackerRole(TrackerRole.CHEST, false),
|
||||
config.getOSCTrackerRole(TrackerRole.WAIST, false),
|
||||
config.getOSCTrackerRole(TrackerRole.LEFT_KNEE, false)
|
||||
&& config.getOSCTrackerRole(TrackerRole.RIGHT_KNEE, false),
|
||||
config.getOSCTrackerRole(TrackerRole.LEFT_FOOT, false)
|
||||
&& config.getOSCTrackerRole(TrackerRole.RIGHT_FOOT, false),
|
||||
config.getOSCTrackerRole(TrackerRole.LEFT_ELBOW, false)
|
||||
&& config.getOSCTrackerRole(TrackerRole.RIGHT_ELBOW, false),
|
||||
config.getOSCTrackerRole(TrackerRole.LEFT_HAND, false)
|
||||
&& config.getOSCTrackerRole(TrackerRole.RIGHT_HAND, false)
|
||||
);
|
||||
VRCOSCSettings.startVRCOSCSettings(fbb);
|
||||
VRCOSCSettings.addOscSettings(fbb, generalSettingOffset);
|
||||
VRCOSCSettings.addTrackers(fbb, oscSettingOffset);
|
||||
VRCOSCSettings.addOscqueryEnabled(fbb, config.getOscqueryEnabled());
|
||||
|
||||
return VRCOSCSettings.endVRCOSCSettings(fbb);
|
||||
}
|
||||
|
||||
public static int createVMCOSCSettings(
|
||||
FlatBufferBuilder fbb,
|
||||
VMCConfig config
|
||||
) {
|
||||
int addressStringOffset = fbb.createString(config.getAddress());
|
||||
int generalSettingOffset = OSCSettings
|
||||
.createOSCSettings(
|
||||
fbb,
|
||||
config.getEnabled(),
|
||||
config.getPortIn(),
|
||||
config.getPortOut(),
|
||||
addressStringOffset
|
||||
);
|
||||
|
||||
String vrmJson = config.getVrmJson();
|
||||
int vrmJsonOffset = 0;
|
||||
if (vrmJson != null)
|
||||
vrmJsonOffset = fbb.createString(vrmJson);
|
||||
|
||||
VMCOSCSettings.startVMCOSCSettings(fbb);
|
||||
VMCOSCSettings.addOscSettings(fbb, generalSettingOffset);
|
||||
if (vrmJson != null)
|
||||
VMCOSCSettings.addVrmJson(fbb, vrmJsonOffset);
|
||||
VMCOSCSettings.addAnchorHip(fbb, config.getAnchorHip());
|
||||
VMCOSCSettings.addMirrorTracking(fbb, config.getMirrorTracking());
|
||||
|
||||
return VMCOSCSettings.endVMCOSCSettings(fbb);
|
||||
}
|
||||
|
||||
public static int createFilterSettings(
|
||||
FlatBufferBuilder fbb,
|
||||
FiltersConfig filtersConfig
|
||||
) {
|
||||
return FilteringSettings
|
||||
.createFilteringSettings(
|
||||
fbb,
|
||||
TrackerFilters.getByConfigkey(filtersConfig.getType()).getId(),
|
||||
filtersConfig.getAmount()
|
||||
);
|
||||
}
|
||||
|
||||
public static int createDriftCompensationSettings(
|
||||
FlatBufferBuilder fbb,
|
||||
DriftCompensationConfig driftCompensationConfig
|
||||
) {
|
||||
return DriftCompensationSettings
|
||||
.createDriftCompensationSettings(
|
||||
fbb,
|
||||
driftCompensationConfig.getEnabled(),
|
||||
driftCompensationConfig.getPrediction(),
|
||||
driftCompensationConfig.getAmount(),
|
||||
driftCompensationConfig.getMaxResets()
|
||||
);
|
||||
}
|
||||
|
||||
public static int createTapDetectionSettings(
|
||||
FlatBufferBuilder fbb,
|
||||
TapDetectionConfig tapDetectionConfig
|
||||
) {
|
||||
return TapDetectionSettings
|
||||
.createTapDetectionSettings(
|
||||
fbb,
|
||||
tapDetectionConfig.getFullResetDelay(),
|
||||
tapDetectionConfig.getFullResetEnabled(),
|
||||
tapDetectionConfig.getFullResetTaps(),
|
||||
tapDetectionConfig.getYawResetDelay(),
|
||||
tapDetectionConfig.getYawResetEnabled(),
|
||||
tapDetectionConfig.getYawResetTaps(),
|
||||
tapDetectionConfig.getMountingResetDelay(),
|
||||
tapDetectionConfig.getMountingResetEnabled(),
|
||||
tapDetectionConfig.getMountingResetTaps(),
|
||||
tapDetectionConfig.getSetupMode(),
|
||||
tapDetectionConfig.getNumberTrackersOverThreshold()
|
||||
);
|
||||
}
|
||||
|
||||
public static int createSteamVRSettings(FlatBufferBuilder fbb, ISteamVRBridge bridge) {
|
||||
int steamvrTrackerSettings = 0;
|
||||
if (bridge != null) {
|
||||
steamvrTrackerSettings = SteamVRTrackersSetting
|
||||
.createSteamVRTrackersSetting(
|
||||
fbb,
|
||||
bridge.getShareSetting(TrackerRole.WAIST),
|
||||
bridge.getShareSetting(TrackerRole.CHEST),
|
||||
bridge.getAutomaticSharedTrackers(),
|
||||
|
||||
bridge.getShareSetting(TrackerRole.LEFT_FOOT),
|
||||
bridge.getShareSetting(TrackerRole.RIGHT_FOOT),
|
||||
bridge.getShareSetting(TrackerRole.LEFT_KNEE),
|
||||
bridge.getShareSetting(TrackerRole.RIGHT_KNEE),
|
||||
bridge.getShareSetting(TrackerRole.LEFT_ELBOW),
|
||||
bridge.getShareSetting(TrackerRole.RIGHT_ELBOW),
|
||||
bridge.getShareSetting(TrackerRole.LEFT_HAND),
|
||||
bridge.getShareSetting(TrackerRole.RIGHT_HAND)
|
||||
);
|
||||
}
|
||||
return steamvrTrackerSettings;
|
||||
}
|
||||
|
||||
public static int createModelSettings(
|
||||
FlatBufferBuilder fbb,
|
||||
HumanPoseManager humanPoseManager,
|
||||
LegTweaksConfig legTweaksConfig,
|
||||
SkeletonConfig skeletonConfig
|
||||
) {
|
||||
int togglesOffset = ModelToggles
|
||||
.createModelToggles(
|
||||
fbb,
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.EXTENDED_SPINE_MODEL),
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.EXTENDED_PELVIS_MODEL),
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.EXTENDED_KNEE_MODEL),
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.FORCE_ARMS_FROM_HMD),
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.FLOOR_CLIP),
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.SKATING_CORRECTION),
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.TOE_SNAP),
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.FOOT_PLANT),
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.SELF_LOCALIZATION),
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.USE_POSITION),
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.ENFORCE_CONSTRAINTS),
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.CORRECT_CONSTRAINTS)
|
||||
);
|
||||
int ratiosOffset = ModelRatios
|
||||
.createModelRatios(
|
||||
fbb,
|
||||
humanPoseManager.getValue(SkeletonConfigValues.WAIST_FROM_CHEST_HIP_AVERAGING),
|
||||
humanPoseManager.getValue(SkeletonConfigValues.WAIST_FROM_CHEST_LEGS_AVERAGING),
|
||||
humanPoseManager.getValue(SkeletonConfigValues.HIP_FROM_CHEST_LEGS_AVERAGING),
|
||||
humanPoseManager.getValue(SkeletonConfigValues.HIP_FROM_WAIST_LEGS_AVERAGING),
|
||||
humanPoseManager.getValue(SkeletonConfigValues.HIP_LEGS_AVERAGING),
|
||||
humanPoseManager.getValue(SkeletonConfigValues.KNEE_TRACKER_ANKLE_AVERAGING),
|
||||
humanPoseManager.getValue(SkeletonConfigValues.KNEE_ANKLE_AVERAGING)
|
||||
);
|
||||
int legTweaksOffset = LegTweaksSettings
|
||||
.createLegTweaksSettings(
|
||||
fbb,
|
||||
legTweaksConfig.getCorrectionStrength()
|
||||
);
|
||||
int skeletonConfigOffset = SkeletonHeight
|
||||
.createSkeletonHeight(
|
||||
fbb,
|
||||
skeletonConfig.getHmdHeight(),
|
||||
skeletonConfig.getFloorHeight()
|
||||
);
|
||||
return ModelSettings
|
||||
.createModelSettings(
|
||||
fbb,
|
||||
togglesOffset,
|
||||
ratiosOffset,
|
||||
legTweaksOffset,
|
||||
skeletonConfigOffset
|
||||
);
|
||||
}
|
||||
|
||||
public static int createAutoBoneSettings(FlatBufferBuilder fbb, AutoBoneConfig autoBoneConfig) {
|
||||
return AutoBoneSettings
|
||||
.createAutoBoneSettings(
|
||||
fbb,
|
||||
autoBoneConfig.getCursorIncrement(),
|
||||
autoBoneConfig.getMinDataDistance(),
|
||||
autoBoneConfig.getMaxDataDistance(),
|
||||
autoBoneConfig.getNumEpochs(),
|
||||
autoBoneConfig.getPrintEveryNumEpochs(),
|
||||
autoBoneConfig.getInitialAdjustRate(),
|
||||
autoBoneConfig.getAdjustRateDecay(),
|
||||
autoBoneConfig.getSlideErrorFactor(),
|
||||
autoBoneConfig.getOffsetSlideErrorFactor(),
|
||||
autoBoneConfig.getFootHeightOffsetErrorFactor(),
|
||||
autoBoneConfig.getBodyProportionErrorFactor(),
|
||||
autoBoneConfig.getHeightErrorFactor(),
|
||||
autoBoneConfig.getPositionErrorFactor(),
|
||||
autoBoneConfig.getPositionOffsetErrorFactor(),
|
||||
autoBoneConfig.getCalcInitError(),
|
||||
autoBoneConfig.getRandomizeFrameOrder(),
|
||||
autoBoneConfig.getScaleEachStep(),
|
||||
autoBoneConfig.getSampleCount(),
|
||||
autoBoneConfig.getSampleRateMs(),
|
||||
autoBoneConfig.getSaveRecordings(),
|
||||
autoBoneConfig.getUseSkeletonHeight(),
|
||||
autoBoneConfig.getRandSeed()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes values from AutoBoneSettings to an AutoBoneConfig.
|
||||
*
|
||||
* @param autoBoneSettings The settings to read from.
|
||||
* @param autoBoneConfig The config to write to.
|
||||
* @return The autoBoneConfig parameter.
|
||||
*/
|
||||
public static AutoBoneConfig readAutoBoneSettings(
|
||||
AutoBoneSettings autoBoneSettings,
|
||||
AutoBoneConfig autoBoneConfig
|
||||
) {
|
||||
if (autoBoneSettings.hasCursorIncrement()) {
|
||||
autoBoneConfig.setCursorIncrement(autoBoneSettings.cursorIncrement());
|
||||
}
|
||||
if (autoBoneSettings.hasMinDataDistance()) {
|
||||
autoBoneConfig.setMinDataDistance(autoBoneSettings.minDataDistance());
|
||||
}
|
||||
if (autoBoneSettings.hasMaxDataDistance()) {
|
||||
autoBoneConfig.setMaxDataDistance(autoBoneSettings.maxDataDistance());
|
||||
}
|
||||
if (autoBoneSettings.hasNumEpochs()) {
|
||||
autoBoneConfig.setNumEpochs(autoBoneSettings.numEpochs());
|
||||
}
|
||||
if (autoBoneSettings.hasPrintEveryNumEpochs()) {
|
||||
autoBoneConfig.setPrintEveryNumEpochs(autoBoneSettings.printEveryNumEpochs());
|
||||
}
|
||||
if (autoBoneSettings.hasInitialAdjustRate()) {
|
||||
autoBoneConfig.setInitialAdjustRate(autoBoneSettings.initialAdjustRate());
|
||||
}
|
||||
if (autoBoneSettings.hasAdjustRateDecay()) {
|
||||
autoBoneConfig.setAdjustRateDecay(autoBoneSettings.adjustRateDecay());
|
||||
}
|
||||
if (autoBoneSettings.hasSlideErrorFactor()) {
|
||||
autoBoneConfig.setSlideErrorFactor(autoBoneSettings.slideErrorFactor());
|
||||
}
|
||||
if (autoBoneSettings.hasOffsetSlideErrorFactor()) {
|
||||
autoBoneConfig.setOffsetSlideErrorFactor(autoBoneSettings.offsetSlideErrorFactor());
|
||||
}
|
||||
if (autoBoneSettings.hasFootHeightOffsetErrorFactor()) {
|
||||
autoBoneConfig
|
||||
.setFootHeightOffsetErrorFactor(autoBoneSettings.footHeightOffsetErrorFactor());
|
||||
}
|
||||
if (autoBoneSettings.hasBodyProportionErrorFactor()) {
|
||||
autoBoneConfig
|
||||
.setBodyProportionErrorFactor(autoBoneSettings.bodyProportionErrorFactor());
|
||||
}
|
||||
if (autoBoneSettings.hasHeightErrorFactor()) {
|
||||
autoBoneConfig.setHeightErrorFactor(autoBoneSettings.heightErrorFactor());
|
||||
}
|
||||
if (autoBoneSettings.hasPositionErrorFactor()) {
|
||||
autoBoneConfig.setPositionErrorFactor(autoBoneSettings.positionErrorFactor());
|
||||
}
|
||||
if (autoBoneSettings.hasPositionOffsetErrorFactor()) {
|
||||
autoBoneConfig
|
||||
.setPositionOffsetErrorFactor(autoBoneSettings.positionOffsetErrorFactor());
|
||||
}
|
||||
if (autoBoneSettings.hasCalcInitError()) {
|
||||
autoBoneConfig.setCalcInitError(autoBoneSettings.calcInitError());
|
||||
}
|
||||
if (autoBoneSettings.hasRandomizeFrameOrder()) {
|
||||
autoBoneConfig.setRandomizeFrameOrder(autoBoneSettings.randomizeFrameOrder());
|
||||
}
|
||||
if (autoBoneSettings.hasScaleEachStep()) {
|
||||
autoBoneConfig.setScaleEachStep(autoBoneSettings.scaleEachStep());
|
||||
}
|
||||
if (autoBoneSettings.hasSampleCount()) {
|
||||
autoBoneConfig.setSampleCount(autoBoneSettings.sampleCount());
|
||||
}
|
||||
if (autoBoneSettings.hasSampleRateMs()) {
|
||||
autoBoneConfig.setSampleRateMs(autoBoneSettings.sampleRateMs());
|
||||
}
|
||||
if (autoBoneSettings.hasSaveRecordings()) {
|
||||
autoBoneConfig.setSaveRecordings(autoBoneSettings.saveRecordings());
|
||||
}
|
||||
if (autoBoneSettings.hasUseSkeletonHeight()) {
|
||||
autoBoneConfig.setUseSkeletonHeight(autoBoneSettings.useSkeletonHeight());
|
||||
}
|
||||
if (autoBoneSettings.hasRandSeed()) {
|
||||
autoBoneConfig.setRandSeed(autoBoneSettings.randSeed());
|
||||
}
|
||||
|
||||
return autoBoneConfig;
|
||||
}
|
||||
|
||||
public static int createArmsResetModeSettings(
|
||||
FlatBufferBuilder fbb,
|
||||
ResetsConfig resetsConfig
|
||||
) {
|
||||
return ResetsSettings
|
||||
.createResetsSettings(
|
||||
fbb,
|
||||
resetsConfig.getResetMountingFeet(),
|
||||
resetsConfig.getMode().getId(),
|
||||
resetsConfig.getYawResetSmoothTime(),
|
||||
resetsConfig.getSaveMountingReset(),
|
||||
resetsConfig.getResetHmdPitch()
|
||||
);
|
||||
}
|
||||
|
||||
public static int createSettingsResponse(FlatBufferBuilder fbb, VRServer server) {
|
||||
ISteamVRBridge bridge = server.getVRBridge(ISteamVRBridge.class);
|
||||
|
||||
return SettingsResponse
|
||||
.createSettingsResponse(
|
||||
fbb,
|
||||
RPCSettingsBuilder.createSteamVRSettings(fbb, bridge),
|
||||
RPCSettingsBuilder
|
||||
.createFilterSettings(
|
||||
fbb,
|
||||
server.configManager.getVrConfig().getFilters()
|
||||
),
|
||||
RPCSettingsBuilder
|
||||
.createDriftCompensationSettings(
|
||||
fbb,
|
||||
server.configManager.getVrConfig().getDriftCompensation()
|
||||
),
|
||||
RPCSettingsBuilder
|
||||
.createOSCRouterSettings(
|
||||
fbb,
|
||||
server.configManager.getVrConfig().getOscRouter()
|
||||
),
|
||||
RPCSettingsBuilder
|
||||
.createVRCOSCSettings(
|
||||
fbb,
|
||||
server.configManager.getVrConfig().getVrcOSC()
|
||||
),
|
||||
RPCSettingsBuilder
|
||||
.createVMCOSCSettings(
|
||||
fbb,
|
||||
server.configManager.getVrConfig().getVMC()
|
||||
),
|
||||
RPCSettingsBuilder
|
||||
.createModelSettings(
|
||||
fbb,
|
||||
server.humanPoseManager,
|
||||
server.configManager.getVrConfig().getLegTweaks(),
|
||||
server.configManager.getVrConfig().getSkeleton()
|
||||
),
|
||||
RPCSettingsBuilder
|
||||
.createTapDetectionSettings(
|
||||
fbb,
|
||||
server.configManager.getVrConfig().getTapDetection()
|
||||
),
|
||||
RPCSettingsBuilder
|
||||
.createAutoBoneSettings(
|
||||
fbb,
|
||||
server.configManager.getVrConfig().getAutoBone()
|
||||
),
|
||||
RPCSettingsBuilder
|
||||
.createArmsResetModeSettings(
|
||||
fbb,
|
||||
server.configManager.getVrConfig().getResetsConfig()
|
||||
),
|
||||
RPCSettingsBuilderKotlin.INSTANCE
|
||||
.createStayAlignedSettings(
|
||||
fbb,
|
||||
server.configManager.getVrConfig().getStayAlignedConfig()
|
||||
),
|
||||
RPCSettingsBuilderKotlin.INSTANCE
|
||||
.createHIDSettings(
|
||||
fbb,
|
||||
server.configManager.getVrConfig().getHidConfig()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,458 @@
|
||||
package dev.slimevr.protocol.rpc.settings
|
||||
|
||||
import com.google.flatbuffers.FlatBufferBuilder
|
||||
import dev.slimevr.VRServer
|
||||
import dev.slimevr.bridge.ISteamVRBridge
|
||||
import dev.slimevr.config.AutoBoneConfig
|
||||
import dev.slimevr.config.DriftCompensationConfig
|
||||
import dev.slimevr.config.FiltersConfig
|
||||
import dev.slimevr.config.HIDConfig
|
||||
import dev.slimevr.config.LegTweaksConfig
|
||||
import dev.slimevr.config.OSCConfig
|
||||
import dev.slimevr.config.ResetsConfig
|
||||
import dev.slimevr.config.SkeletonConfig
|
||||
import dev.slimevr.config.StayAlignedConfig
|
||||
import dev.slimevr.config.TapDetectionConfig
|
||||
import dev.slimevr.config.VMCConfig
|
||||
import dev.slimevr.config.VRCOSCConfig
|
||||
import dev.slimevr.filtering.TrackerFilters.Companion.getByConfigkey
|
||||
import dev.slimevr.tracking.processor.HumanPoseManager
|
||||
import dev.slimevr.tracking.processor.config.SkeletonConfigToggles
|
||||
import dev.slimevr.tracking.processor.config.SkeletonConfigValues
|
||||
import dev.slimevr.tracking.trackers.TrackerRole
|
||||
import solarxr_protocol.rpc.AutoBoneSettings
|
||||
import solarxr_protocol.rpc.DriftCompensationSettings
|
||||
import solarxr_protocol.rpc.FilteringSettings
|
||||
import solarxr_protocol.rpc.HIDSettings
|
||||
import solarxr_protocol.rpc.OSCRouterSettings
|
||||
import solarxr_protocol.rpc.OSCSettings
|
||||
import solarxr_protocol.rpc.OSCTrackersSetting
|
||||
import solarxr_protocol.rpc.ResetsSettings
|
||||
import solarxr_protocol.rpc.SettingsResponse
|
||||
import solarxr_protocol.rpc.StayAlignedSettings
|
||||
import solarxr_protocol.rpc.SteamVRTrackersSetting
|
||||
import solarxr_protocol.rpc.TapDetectionSettings
|
||||
import solarxr_protocol.rpc.VMCOSCSettings
|
||||
import solarxr_protocol.rpc.VRCOSCSettings
|
||||
import solarxr_protocol.rpc.settings.LegTweaksSettings
|
||||
import solarxr_protocol.rpc.settings.ModelRatios
|
||||
import solarxr_protocol.rpc.settings.ModelSettings
|
||||
import solarxr_protocol.rpc.settings.ModelToggles
|
||||
import solarxr_protocol.rpc.settings.SkeletonHeight
|
||||
|
||||
fun createOSCRouterSettings(
|
||||
fbb: FlatBufferBuilder,
|
||||
config: OSCConfig,
|
||||
): Int {
|
||||
val addressStringOffset = fbb.createString(config.address)
|
||||
|
||||
val oscSettingOffset = OSCSettings
|
||||
.createOSCSettings(
|
||||
fbb,
|
||||
config.enabled,
|
||||
config.portIn,
|
||||
config.portOut,
|
||||
addressStringOffset,
|
||||
)
|
||||
|
||||
OSCRouterSettings.startOSCRouterSettings(fbb)
|
||||
OSCRouterSettings.addOscSettings(fbb, oscSettingOffset)
|
||||
|
||||
return OSCRouterSettings.endOSCRouterSettings(fbb)
|
||||
}
|
||||
|
||||
fun createVRCOSCSettings(
|
||||
fbb: FlatBufferBuilder,
|
||||
config: VRCOSCConfig,
|
||||
): Int {
|
||||
val addressStringOffset = fbb.createString(config.address)
|
||||
val generalSettingOffset = OSCSettings
|
||||
.createOSCSettings(
|
||||
fbb,
|
||||
config.enabled,
|
||||
config.portIn,
|
||||
config.portOut,
|
||||
addressStringOffset,
|
||||
)
|
||||
val oscSettingOffset = OSCTrackersSetting
|
||||
.createOSCTrackersSetting(
|
||||
fbb,
|
||||
config.getOSCTrackerRole(TrackerRole.HEAD, false),
|
||||
config.getOSCTrackerRole(TrackerRole.CHEST, false),
|
||||
config.getOSCTrackerRole(TrackerRole.WAIST, false),
|
||||
config.getOSCTrackerRole(TrackerRole.LEFT_KNEE, false) &&
|
||||
config.getOSCTrackerRole(TrackerRole.RIGHT_KNEE, false),
|
||||
config.getOSCTrackerRole(TrackerRole.LEFT_FOOT, false) &&
|
||||
config.getOSCTrackerRole(TrackerRole.RIGHT_FOOT, false),
|
||||
config.getOSCTrackerRole(TrackerRole.LEFT_ELBOW, false) &&
|
||||
config.getOSCTrackerRole(TrackerRole.RIGHT_ELBOW, false),
|
||||
config.getOSCTrackerRole(TrackerRole.LEFT_HAND, false) &&
|
||||
config.getOSCTrackerRole(TrackerRole.RIGHT_HAND, false),
|
||||
)
|
||||
VRCOSCSettings.startVRCOSCSettings(fbb)
|
||||
VRCOSCSettings.addOscSettings(fbb, generalSettingOffset)
|
||||
VRCOSCSettings.addTrackers(fbb, oscSettingOffset)
|
||||
VRCOSCSettings.addOscqueryEnabled(fbb, config.oscqueryEnabled)
|
||||
|
||||
return VRCOSCSettings.endVRCOSCSettings(fbb)
|
||||
}
|
||||
|
||||
fun createVMCOSCSettings(
|
||||
fbb: FlatBufferBuilder,
|
||||
config: VMCConfig,
|
||||
): Int {
|
||||
val addressStringOffset = fbb.createString(config.address)
|
||||
val generalSettingOffset = OSCSettings
|
||||
.createOSCSettings(
|
||||
fbb,
|
||||
config.enabled,
|
||||
config.portIn,
|
||||
config.portOut,
|
||||
addressStringOffset,
|
||||
)
|
||||
|
||||
val vrmJson = config.vrmJson
|
||||
var vrmJsonOffset = 0
|
||||
if (vrmJson != null) vrmJsonOffset = fbb.createString(vrmJson)
|
||||
|
||||
VMCOSCSettings.startVMCOSCSettings(fbb)
|
||||
VMCOSCSettings.addOscSettings(fbb, generalSettingOffset)
|
||||
if (vrmJson != null) VMCOSCSettings.addVrmJson(fbb, vrmJsonOffset)
|
||||
VMCOSCSettings.addAnchorHip(fbb, config.anchorHip)
|
||||
VMCOSCSettings.addMirrorTracking(fbb, config.mirrorTracking)
|
||||
|
||||
return VMCOSCSettings.endVMCOSCSettings(fbb)
|
||||
}
|
||||
|
||||
fun createFilterSettings(
|
||||
fbb: FlatBufferBuilder,
|
||||
filtersConfig: FiltersConfig,
|
||||
): Int = FilteringSettings
|
||||
.createFilteringSettings(
|
||||
fbb,
|
||||
getByConfigkey(filtersConfig.type)!!.id,
|
||||
filtersConfig.amount,
|
||||
)
|
||||
|
||||
fun createDriftCompensationSettings(
|
||||
fbb: FlatBufferBuilder,
|
||||
driftCompensationConfig: DriftCompensationConfig,
|
||||
): Int = DriftCompensationSettings
|
||||
.createDriftCompensationSettings(
|
||||
fbb,
|
||||
driftCompensationConfig.enabled,
|
||||
driftCompensationConfig.prediction,
|
||||
driftCompensationConfig.amount,
|
||||
driftCompensationConfig.maxResets,
|
||||
)
|
||||
|
||||
fun createTapDetectionSettings(
|
||||
fbb: FlatBufferBuilder,
|
||||
tapDetectionConfig: TapDetectionConfig,
|
||||
): Int = TapDetectionSettings
|
||||
.createTapDetectionSettings(
|
||||
fbb,
|
||||
tapDetectionConfig.fullResetDelay,
|
||||
tapDetectionConfig.fullResetEnabled,
|
||||
tapDetectionConfig.fullResetTaps,
|
||||
tapDetectionConfig.yawResetDelay,
|
||||
tapDetectionConfig.yawResetEnabled,
|
||||
tapDetectionConfig.yawResetTaps,
|
||||
tapDetectionConfig.mountingResetDelay,
|
||||
tapDetectionConfig.mountingResetEnabled,
|
||||
tapDetectionConfig.mountingResetTaps,
|
||||
tapDetectionConfig.setupMode,
|
||||
tapDetectionConfig.numberTrackersOverThreshold,
|
||||
)
|
||||
|
||||
fun createSteamVRSettings(fbb: FlatBufferBuilder, bridge: ISteamVRBridge?): Int {
|
||||
var steamvrTrackerSettings = 0
|
||||
if (bridge != null) {
|
||||
steamvrTrackerSettings = SteamVRTrackersSetting
|
||||
.createSteamVRTrackersSetting(
|
||||
fbb,
|
||||
bridge.getShareSetting(TrackerRole.WAIST),
|
||||
bridge.getShareSetting(TrackerRole.CHEST),
|
||||
bridge.getAutomaticSharedTrackers(),
|
||||
|
||||
bridge.getShareSetting(TrackerRole.LEFT_FOOT),
|
||||
bridge.getShareSetting(TrackerRole.RIGHT_FOOT),
|
||||
bridge.getShareSetting(TrackerRole.LEFT_KNEE),
|
||||
bridge.getShareSetting(TrackerRole.RIGHT_KNEE),
|
||||
bridge.getShareSetting(TrackerRole.LEFT_ELBOW),
|
||||
bridge.getShareSetting(TrackerRole.RIGHT_ELBOW),
|
||||
bridge.getShareSetting(TrackerRole.LEFT_HAND),
|
||||
bridge.getShareSetting(TrackerRole.RIGHT_HAND),
|
||||
)
|
||||
}
|
||||
return steamvrTrackerSettings
|
||||
}
|
||||
|
||||
fun createModelSettings(
|
||||
fbb: FlatBufferBuilder,
|
||||
humanPoseManager: HumanPoseManager,
|
||||
legTweaksConfig: LegTweaksConfig,
|
||||
skeletonConfig: SkeletonConfig,
|
||||
): Int {
|
||||
val togglesOffset = ModelToggles
|
||||
.createModelToggles(
|
||||
fbb,
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.EXTENDED_SPINE_MODEL),
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.EXTENDED_PELVIS_MODEL),
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.EXTENDED_KNEE_MODEL),
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.FORCE_ARMS_FROM_HMD),
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.FLOOR_CLIP),
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.SKATING_CORRECTION),
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.TOE_SNAP),
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.FOOT_PLANT),
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.SELF_LOCALIZATION),
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.USE_POSITION),
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.ENFORCE_CONSTRAINTS),
|
||||
humanPoseManager.getToggle(SkeletonConfigToggles.CORRECT_CONSTRAINTS),
|
||||
)
|
||||
val ratiosOffset = ModelRatios
|
||||
.createModelRatios(
|
||||
fbb,
|
||||
humanPoseManager.getValue(SkeletonConfigValues.WAIST_FROM_CHEST_HIP_AVERAGING),
|
||||
humanPoseManager.getValue(SkeletonConfigValues.WAIST_FROM_CHEST_LEGS_AVERAGING),
|
||||
humanPoseManager.getValue(SkeletonConfigValues.HIP_FROM_CHEST_LEGS_AVERAGING),
|
||||
humanPoseManager.getValue(SkeletonConfigValues.HIP_FROM_WAIST_LEGS_AVERAGING),
|
||||
humanPoseManager.getValue(SkeletonConfigValues.HIP_LEGS_AVERAGING),
|
||||
humanPoseManager.getValue(SkeletonConfigValues.KNEE_TRACKER_ANKLE_AVERAGING),
|
||||
humanPoseManager.getValue(SkeletonConfigValues.KNEE_ANKLE_AVERAGING),
|
||||
)
|
||||
val legTweaksOffset = LegTweaksSettings
|
||||
.createLegTweaksSettings(
|
||||
fbb,
|
||||
legTweaksConfig.correctionStrength,
|
||||
)
|
||||
val skeletonConfigOffset = SkeletonHeight
|
||||
.createSkeletonHeight(
|
||||
fbb,
|
||||
skeletonConfig.hmdHeight,
|
||||
skeletonConfig.floorHeight,
|
||||
)
|
||||
return ModelSettings
|
||||
.createModelSettings(
|
||||
fbb,
|
||||
togglesOffset,
|
||||
ratiosOffset,
|
||||
legTweaksOffset,
|
||||
skeletonConfigOffset,
|
||||
)
|
||||
}
|
||||
|
||||
fun createAutoBoneSettings(
|
||||
fbb: FlatBufferBuilder,
|
||||
autoBoneConfig: AutoBoneConfig,
|
||||
): Int = AutoBoneSettings
|
||||
.createAutoBoneSettings(
|
||||
fbb,
|
||||
autoBoneConfig.cursorIncrement,
|
||||
autoBoneConfig.minDataDistance,
|
||||
autoBoneConfig.maxDataDistance,
|
||||
autoBoneConfig.numEpochs,
|
||||
autoBoneConfig.printEveryNumEpochs,
|
||||
autoBoneConfig.initialAdjustRate,
|
||||
autoBoneConfig.adjustRateDecay,
|
||||
autoBoneConfig.slideErrorFactor,
|
||||
autoBoneConfig.offsetSlideErrorFactor,
|
||||
autoBoneConfig.footHeightOffsetErrorFactor,
|
||||
autoBoneConfig.bodyProportionErrorFactor,
|
||||
autoBoneConfig.heightErrorFactor,
|
||||
autoBoneConfig.positionErrorFactor,
|
||||
autoBoneConfig.positionOffsetErrorFactor,
|
||||
autoBoneConfig.calcInitError,
|
||||
autoBoneConfig.randomizeFrameOrder,
|
||||
autoBoneConfig.scaleEachStep,
|
||||
autoBoneConfig.sampleCount,
|
||||
autoBoneConfig.sampleRateMs,
|
||||
autoBoneConfig.saveRecordings,
|
||||
autoBoneConfig.useSkeletonHeight,
|
||||
autoBoneConfig.randSeed,
|
||||
)
|
||||
|
||||
/**
|
||||
* Writes values from AutoBoneSettings to an AutoBoneConfig.
|
||||
*
|
||||
* @param autoBoneSettings The settings to read from.
|
||||
* @param autoBoneConfig The config to write to.
|
||||
* @return The autoBoneConfig parameter.
|
||||
*/
|
||||
fun readAutoBoneSettings(
|
||||
autoBoneSettings: AutoBoneSettings,
|
||||
autoBoneConfig: AutoBoneConfig,
|
||||
): AutoBoneConfig {
|
||||
if (autoBoneSettings.hasCursorIncrement()) {
|
||||
autoBoneConfig.cursorIncrement = autoBoneSettings.cursorIncrement()
|
||||
}
|
||||
if (autoBoneSettings.hasMinDataDistance()) {
|
||||
autoBoneConfig.minDataDistance = autoBoneSettings.minDataDistance()
|
||||
}
|
||||
if (autoBoneSettings.hasMaxDataDistance()) {
|
||||
autoBoneConfig.maxDataDistance = autoBoneSettings.maxDataDistance()
|
||||
}
|
||||
if (autoBoneSettings.hasNumEpochs()) {
|
||||
autoBoneConfig.numEpochs = autoBoneSettings.numEpochs()
|
||||
}
|
||||
if (autoBoneSettings.hasPrintEveryNumEpochs()) {
|
||||
autoBoneConfig.printEveryNumEpochs = autoBoneSettings.printEveryNumEpochs()
|
||||
}
|
||||
if (autoBoneSettings.hasInitialAdjustRate()) {
|
||||
autoBoneConfig.initialAdjustRate = autoBoneSettings.initialAdjustRate()
|
||||
}
|
||||
if (autoBoneSettings.hasAdjustRateDecay()) {
|
||||
autoBoneConfig.adjustRateDecay = autoBoneSettings.adjustRateDecay()
|
||||
}
|
||||
if (autoBoneSettings.hasSlideErrorFactor()) {
|
||||
autoBoneConfig.slideErrorFactor = autoBoneSettings.slideErrorFactor()
|
||||
}
|
||||
if (autoBoneSettings.hasOffsetSlideErrorFactor()) {
|
||||
autoBoneConfig.offsetSlideErrorFactor =
|
||||
autoBoneSettings.offsetSlideErrorFactor()
|
||||
}
|
||||
if (autoBoneSettings.hasFootHeightOffsetErrorFactor()) {
|
||||
autoBoneConfig
|
||||
.footHeightOffsetErrorFactor =
|
||||
autoBoneSettings.footHeightOffsetErrorFactor()
|
||||
}
|
||||
if (autoBoneSettings.hasBodyProportionErrorFactor()) {
|
||||
autoBoneConfig
|
||||
.bodyProportionErrorFactor = autoBoneSettings.bodyProportionErrorFactor()
|
||||
}
|
||||
if (autoBoneSettings.hasHeightErrorFactor()) {
|
||||
autoBoneConfig.heightErrorFactor = autoBoneSettings.heightErrorFactor()
|
||||
}
|
||||
if (autoBoneSettings.hasPositionErrorFactor()) {
|
||||
autoBoneConfig.positionErrorFactor = autoBoneSettings.positionErrorFactor()
|
||||
}
|
||||
if (autoBoneSettings.hasPositionOffsetErrorFactor()) {
|
||||
autoBoneConfig
|
||||
.positionOffsetErrorFactor = autoBoneSettings.positionOffsetErrorFactor()
|
||||
}
|
||||
if (autoBoneSettings.hasCalcInitError()) {
|
||||
autoBoneConfig.calcInitError = autoBoneSettings.calcInitError()
|
||||
}
|
||||
if (autoBoneSettings.hasRandomizeFrameOrder()) {
|
||||
autoBoneConfig.randomizeFrameOrder = autoBoneSettings.randomizeFrameOrder()
|
||||
}
|
||||
if (autoBoneSettings.hasScaleEachStep()) {
|
||||
autoBoneConfig.scaleEachStep = autoBoneSettings.scaleEachStep()
|
||||
}
|
||||
if (autoBoneSettings.hasSampleCount()) {
|
||||
autoBoneConfig.sampleCount = autoBoneSettings.sampleCount()
|
||||
}
|
||||
if (autoBoneSettings.hasSampleRateMs()) {
|
||||
autoBoneConfig.sampleRateMs = autoBoneSettings.sampleRateMs()
|
||||
}
|
||||
if (autoBoneSettings.hasSaveRecordings()) {
|
||||
autoBoneConfig.saveRecordings = autoBoneSettings.saveRecordings()
|
||||
}
|
||||
if (autoBoneSettings.hasUseSkeletonHeight()) {
|
||||
autoBoneConfig.useSkeletonHeight = autoBoneSettings.useSkeletonHeight()
|
||||
}
|
||||
if (autoBoneSettings.hasRandSeed()) {
|
||||
autoBoneConfig.randSeed = autoBoneSettings.randSeed()
|
||||
}
|
||||
|
||||
return autoBoneConfig
|
||||
}
|
||||
|
||||
fun createArmsResetModeSettings(
|
||||
fbb: FlatBufferBuilder,
|
||||
resetsConfig: ResetsConfig,
|
||||
): Int = ResetsSettings
|
||||
.createResetsSettings(
|
||||
fbb,
|
||||
resetsConfig.resetMountingFeet,
|
||||
resetsConfig.mode.id,
|
||||
resetsConfig.yawResetSmoothTime,
|
||||
resetsConfig.saveMountingReset,
|
||||
resetsConfig.resetHmdPitch,
|
||||
)
|
||||
|
||||
fun createSettingsResponse(fbb: FlatBufferBuilder, server: VRServer): Int {
|
||||
val bridge = server.getVRBridge(ISteamVRBridge::class.java)
|
||||
|
||||
return SettingsResponse
|
||||
.createSettingsResponse(
|
||||
fbb,
|
||||
createSteamVRSettings(fbb, bridge),
|
||||
createFilterSettings(
|
||||
fbb,
|
||||
server.configManager.vrConfig.filters,
|
||||
),
|
||||
createDriftCompensationSettings(
|
||||
fbb,
|
||||
server.configManager.vrConfig.driftCompensation,
|
||||
),
|
||||
createOSCRouterSettings(
|
||||
fbb,
|
||||
server.configManager.vrConfig.oscRouter,
|
||||
),
|
||||
createVRCOSCSettings(
|
||||
fbb,
|
||||
server.configManager.vrConfig.vrcOSC,
|
||||
),
|
||||
createVMCOSCSettings(
|
||||
fbb,
|
||||
server.configManager.vrConfig.vmc,
|
||||
),
|
||||
createModelSettings(
|
||||
fbb,
|
||||
server.humanPoseManager,
|
||||
server.configManager.vrConfig.legTweaks,
|
||||
server.configManager.vrConfig.skeleton,
|
||||
),
|
||||
createTapDetectionSettings(
|
||||
fbb,
|
||||
server.configManager.vrConfig.tapDetection,
|
||||
),
|
||||
createAutoBoneSettings(
|
||||
fbb,
|
||||
server.configManager.vrConfig.autoBone,
|
||||
),
|
||||
createArmsResetModeSettings(
|
||||
fbb,
|
||||
server.configManager.vrConfig.resetsConfig,
|
||||
),
|
||||
createStayAlignedSettings(
|
||||
fbb,
|
||||
server.configManager.vrConfig.stayAlignedConfig,
|
||||
),
|
||||
createHIDSettings(fbb, server.configManager.vrConfig.hidConfig),
|
||||
)
|
||||
}
|
||||
|
||||
fun createStayAlignedSettings(
|
||||
fbb: FlatBufferBuilder,
|
||||
config: StayAlignedConfig,
|
||||
): Int = StayAlignedSettings
|
||||
.createStayAlignedSettings(
|
||||
fbb,
|
||||
config.enabled,
|
||||
false, // deprecated
|
||||
config.hideYawCorrection,
|
||||
config.standingRelaxedPose.enabled,
|
||||
config.standingRelaxedPose.upperLegAngleInDeg,
|
||||
config.standingRelaxedPose.lowerLegAngleInDeg,
|
||||
config.standingRelaxedPose.footAngleInDeg,
|
||||
config.sittingRelaxedPose.enabled,
|
||||
config.sittingRelaxedPose.upperLegAngleInDeg,
|
||||
config.sittingRelaxedPose.lowerLegAngleInDeg,
|
||||
config.sittingRelaxedPose.footAngleInDeg,
|
||||
config.flatRelaxedPose.enabled,
|
||||
config.flatRelaxedPose.upperLegAngleInDeg,
|
||||
config.flatRelaxedPose.lowerLegAngleInDeg,
|
||||
config.flatRelaxedPose.footAngleInDeg,
|
||||
config.setupComplete,
|
||||
)
|
||||
|
||||
fun createHIDSettings(
|
||||
fbb: FlatBufferBuilder,
|
||||
config: HIDConfig,
|
||||
): Int = HIDSettings
|
||||
.createHIDSettings(
|
||||
fbb,
|
||||
config.trackersOverHID,
|
||||
)
|
||||
@@ -1,43 +0,0 @@
|
||||
package dev.slimevr.protocol.rpc.settings
|
||||
|
||||
import com.google.flatbuffers.FlatBufferBuilder
|
||||
import dev.slimevr.config.HIDConfig
|
||||
import dev.slimevr.config.StayAlignedConfig
|
||||
import solarxr_protocol.rpc.HIDSettings
|
||||
import solarxr_protocol.rpc.StayAlignedSettings
|
||||
|
||||
object RPCSettingsBuilderKotlin {
|
||||
|
||||
fun createStayAlignedSettings(
|
||||
fbb: FlatBufferBuilder,
|
||||
config: StayAlignedConfig,
|
||||
): Int = StayAlignedSettings
|
||||
.createStayAlignedSettings(
|
||||
fbb,
|
||||
config.enabled,
|
||||
false, // deprecated
|
||||
config.hideYawCorrection,
|
||||
config.standingRelaxedPose.enabled,
|
||||
config.standingRelaxedPose.upperLegAngleInDeg,
|
||||
config.standingRelaxedPose.lowerLegAngleInDeg,
|
||||
config.standingRelaxedPose.footAngleInDeg,
|
||||
config.sittingRelaxedPose.enabled,
|
||||
config.sittingRelaxedPose.upperLegAngleInDeg,
|
||||
config.sittingRelaxedPose.lowerLegAngleInDeg,
|
||||
config.sittingRelaxedPose.footAngleInDeg,
|
||||
config.flatRelaxedPose.enabled,
|
||||
config.flatRelaxedPose.upperLegAngleInDeg,
|
||||
config.flatRelaxedPose.lowerLegAngleInDeg,
|
||||
config.flatRelaxedPose.footAngleInDeg,
|
||||
config.setupComplete,
|
||||
)
|
||||
|
||||
fun createHIDSettings(
|
||||
fbb: FlatBufferBuilder,
|
||||
config: HIDConfig,
|
||||
): Int = HIDSettings
|
||||
.createHIDSettings(
|
||||
fbb,
|
||||
config.trackersOverHID,
|
||||
)
|
||||
}
|
||||
@@ -18,24 +18,9 @@ import kotlin.math.*
|
||||
|
||||
class RPCSettingsHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) {
|
||||
init {
|
||||
rpcHandler.registerPacketListener(RpcMessage.SettingsRequest) { conn: GenericConnection, messageHeader: RpcMessageHeader? ->
|
||||
this.onSettingsRequest(
|
||||
conn,
|
||||
messageHeader,
|
||||
)
|
||||
}
|
||||
rpcHandler
|
||||
.registerPacketListener(
|
||||
RpcMessage.ChangeSettingsRequest,
|
||||
) { conn: GenericConnection?, messageHeader: RpcMessageHeader ->
|
||||
this.onChangeSettingsRequest(
|
||||
conn,
|
||||
messageHeader,
|
||||
)
|
||||
}
|
||||
rpcHandler.registerPacketListener(RpcMessage.SettingsResetRequest) { conn: GenericConnection, messageHeader: RpcMessageHeader? ->
|
||||
this.onSettingsResetRequest(conn, messageHeader)
|
||||
}
|
||||
rpcHandler.registerPacketListener(RpcMessage.SettingsRequest, ::onSettingsRequest)
|
||||
rpcHandler.registerPacketListener(RpcMessage.ChangeSettingsRequest, ::onChangeSettingsRequest)
|
||||
rpcHandler.registerPacketListener(RpcMessage.SettingsResetRequest, ::onSettingsResetRequest)
|
||||
}
|
||||
|
||||
fun onSettingsRequest(conn: GenericConnection, messageHeader: RpcMessageHeader?) {
|
||||
@@ -331,7 +316,7 @@ class RPCSettingsHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) {
|
||||
.vrConfig
|
||||
.autoBone
|
||||
|
||||
RPCSettingsBuilder.readAutoBoneSettings(autoBoneSettings, autoBoneConfig)
|
||||
readAutoBoneSettings(autoBoneSettings, autoBoneConfig)
|
||||
}
|
||||
|
||||
if (req.resetsSettings() != null) {
|
||||
@@ -391,7 +376,7 @@ class RPCSettingsHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) {
|
||||
val settings = SettingsResponse
|
||||
.createSettingsResponse(
|
||||
fbb,
|
||||
RPCSettingsBuilder.createSteamVRSettings(fbb, bridge), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
createSteamVRSettings(fbb, bridge), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
)
|
||||
val outbound =
|
||||
rpcHandler.createRPCMessage(fbb, RpcMessage.SettingsResponse, settings)
|
||||
|
||||
@@ -3,7 +3,7 @@ package dev.slimevr.protocol.rpc.setup
|
||||
import com.google.flatbuffers.FlatBufferBuilder
|
||||
import dev.slimevr.protocol.GenericConnection
|
||||
import dev.slimevr.protocol.ProtocolAPI
|
||||
import dev.slimevr.protocol.datafeed.DataFeedBuilder
|
||||
import dev.slimevr.protocol.datafeed.createTrackerId
|
||||
import dev.slimevr.protocol.rpc.RPCHandler
|
||||
import dev.slimevr.setup.TapSetupListener
|
||||
import dev.slimevr.tracking.trackers.Tracker
|
||||
@@ -20,7 +20,7 @@ class RPCTapSetupHandler(
|
||||
|
||||
override fun onStarted(tracker: Tracker) {
|
||||
val fbb = FlatBufferBuilder(32)
|
||||
val idOffset = DataFeedBuilder.createTrackerId(fbb, tracker)
|
||||
val idOffset = createTrackerId(fbb, tracker)
|
||||
val update = TapDetectionSetupNotification.createTapDetectionSetupNotification(fbb, idOffset)
|
||||
val outbound =
|
||||
rpcHandler.createRPCMessage(fbb, RpcMessage.TapDetectionSetupNotification, update)
|
||||
|
||||
@@ -117,6 +117,9 @@ class Tracker @JvmOverloads constructor(
|
||||
var signalStrength: Int? = null
|
||||
var temperature: Float? = null
|
||||
var button: Int? = null
|
||||
var packetsReceived: Int? = null
|
||||
var packetsLost: Int? = null
|
||||
var packetLoss: Float? = null
|
||||
var customName: String? = null
|
||||
var magStatus: MagnetometerStatus = magStatus
|
||||
private set
|
||||
|
||||
@@ -155,6 +155,10 @@ class HIDCommon {
|
||||
var svr_status: Int? = null
|
||||
// var status: Int? = null // raw status from tracker
|
||||
var rssi: Int? = null
|
||||
var packets_received: Int? = null
|
||||
var packets_lost: Int? = null
|
||||
var windows_hit: Int? = null
|
||||
var windows_missed: Int? = null
|
||||
|
||||
// Tracker packets
|
||||
when (packetType) {
|
||||
@@ -209,6 +213,10 @@ class HIDCommon {
|
||||
3 -> { // status
|
||||
svr_status = dataReceived[i + 2].toUByte().toInt()
|
||||
// status = dataReceived[i + 3].toUByte().toInt()
|
||||
packets_received = dataReceived[i + 4].toUByte().toInt()
|
||||
packets_lost = dataReceived[i + 5].toUByte().toInt()
|
||||
windows_hit = dataReceived[i + 6].toUByte().toInt()
|
||||
windows_missed = dataReceived[i + 7].toUByte().toInt()
|
||||
rssi = dataReceived[i + 15].toUByte().toInt()
|
||||
}
|
||||
|
||||
@@ -309,6 +317,11 @@ class HIDCommon {
|
||||
if (rssi != null) {
|
||||
tracker.signalStrength = -rssi
|
||||
}
|
||||
if (packets_received != null && packets_lost != null) {
|
||||
tracker.packetsReceived = packets_received
|
||||
tracker.packetsLost = packets_lost
|
||||
tracker.packetLoss = if (packets_lost == 0) 0.0f else packets_lost.toFloat() / (packets_received + packets_lost).toFloat()
|
||||
}
|
||||
|
||||
// Assign rotation and acceleration
|
||||
if (packetType == 1 || packetType == 4) {
|
||||
|
||||
@@ -216,7 +216,7 @@ class TrackingChecklistManager(private val vrServer: VRServer) : VRCConfigListen
|
||||
}
|
||||
}
|
||||
val hmd =
|
||||
assignedTrackers.firstOrNull { it.isHmd && !it.isInternal && it.status.sendData }
|
||||
vrServer.allTrackers.firstOrNull { it.status != TrackerStatus.DISCONNECTED && it.isHmd && !it.isInternal && it.status.sendData }
|
||||
val assignedHmd = hmd == null || vrServer.humanPoseManager.skeleton.headTracker != null
|
||||
updateValidity(TrackingChecklistStepId.UNASSIGNED_HMD, assignedHmd) {
|
||||
if (!assignedHmd) {
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.java_websocket.WebSocket;
|
||||
import org.java_websocket.drafts.Draft_6455;
|
||||
import org.java_websocket.handshake.ClientHandshake;
|
||||
import org.java_websocket.server.WebSocketServer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -101,7 +102,7 @@ public class WebsocketAPI extends WebSocketServer implements ProtocolAPIServer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<GenericConnection> getAPIConnections() {
|
||||
public @NotNull Stream<GenericConnection> getApiConnections() {
|
||||
return this.getConnections().stream().map(conn -> {
|
||||
var c = conn.<WebsocketConnection>getAttachment();
|
||||
return (GenericConnection) c;
|
||||
|
||||
@@ -141,7 +141,7 @@ public class UnixSocketRpcBridge implements dev.slimevr.bridge.Bridge,
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.util.stream.Stream<GenericConnection> getAPIConnections() {
|
||||
public java.util.stream.Stream<GenericConnection> getApiConnections() {
|
||||
return this.selector
|
||||
.keys()
|
||||
.stream()
|
||||
|
||||
@@ -190,7 +190,7 @@ class DesktopSerialHandler :
|
||||
}
|
||||
|
||||
override fun write(buff: ByteArray) {
|
||||
LogManager.info("[SerialHandler] WRITING $buff")
|
||||
LogManager.info("[SerialHandler] WRITING ${buff.toString(Charsets.UTF_8)}")
|
||||
currentPort?.outputStream?.write(buff)
|
||||
}
|
||||
|
||||
|
||||
Submodule solarxr-protocol updated: 10a0ea778c...fa2895b19a
Reference in New Issue
Block a user