mirror of
https://github.com/SlimeVR/SlimeVR-Server.git
synced 2026-04-06 02:01:58 +02:00
Compare commits
62 Commits
v18.1.0
...
bscotch/pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
340cff8be5 | ||
|
|
0236a05f26 | ||
|
|
28deb357da | ||
|
|
4d93f87a01 | ||
|
|
88adfce242 | ||
|
|
3d02795dbc | ||
|
|
7ff50f78eb | ||
|
|
0e3aaf105c | ||
|
|
f638540886 | ||
|
|
343d69d690 | ||
|
|
e2d7d354c6 | ||
|
|
cc6f297b92 | ||
|
|
2add43e71a | ||
|
|
0a493ac345 | ||
|
|
17bb2703d1 | ||
|
|
f0981bf709 | ||
|
|
99de554c18 | ||
|
|
f95a4d56d7 | ||
|
|
1df3c9d322 | ||
|
|
e0838cce6c | ||
|
|
e25d3201c2 | ||
|
|
5d14f14139 | ||
|
|
09e81f5ace | ||
|
|
8f57ef2de4 | ||
|
|
ea242960b3 | ||
|
|
35ac14a7de | ||
|
|
690a8b5c6e | ||
|
|
255b8b2865 | ||
|
|
e27ec63985 | ||
|
|
9f8be6551c | ||
|
|
f09cd687c7 | ||
|
|
686499f8dd | ||
|
|
a3bcc61892 | ||
|
|
faf70c9a39 | ||
|
|
2aa8d3a056 | ||
|
|
23df46ca33 | ||
|
|
8407f52777 | ||
|
|
b44dcaa9c2 | ||
|
|
ff0d823aff | ||
|
|
2e8bfa5373 | ||
|
|
87940ddd03 | ||
|
|
6208979ce9 | ||
|
|
9a27fb1320 | ||
|
|
53129328d0 | ||
|
|
2d79c5a0e9 | ||
|
|
74f5a92ce1 | ||
|
|
146930279c | ||
|
|
0c33579858 | ||
|
|
c9783d097b | ||
|
|
d3eafb8d06 | ||
|
|
09d44b51d6 | ||
|
|
cf357e71f5 | ||
|
|
122efacc52 | ||
|
|
7f536528d0 | ||
|
|
3982249ebf | ||
|
|
388bea2e72 | ||
|
|
921a760817 | ||
|
|
55bcec4dda | ||
|
|
bb08e8dc6a | ||
|
|
a82f950eb6 | ||
|
|
e2dbaab8ba | ||
|
|
3611bb5cc7 |
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@@ -10,7 +10,7 @@
|
||||
/pnpm-workspace.yaml @loucass003
|
||||
|
||||
# loucass003 and Erimel responsible for i18n
|
||||
/gui/public/i18n/ @loucass003 @Erimelowo
|
||||
/gui/public/i18n/ @loucass003 @Erimelowo @ImSapphire
|
||||
/gui/src/i18n/ @loucass003 @Erimelowo
|
||||
/l10n.toml @loucass003 @Erimelowo
|
||||
|
||||
|
||||
6
.github/workflows/build-gui.yml
vendored
6
.github/workflows/build-gui.yml
vendored
@@ -103,7 +103,7 @@ jobs:
|
||||
|
||||
- if: startsWith(matrix.os, 'windows')
|
||||
name: Upload a Build Artifact (Windows)
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
# Artifact name
|
||||
name: ${{ format('SlimeVR-GUI-Windows-{0}', env.BUILD_ARCH) }}
|
||||
@@ -112,7 +112,7 @@ jobs:
|
||||
|
||||
- if: startsWith(matrix.os, 'ubuntu')
|
||||
name: Upload a Build Artifact (Linux)
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
# Artifact name
|
||||
name: ${{ format('SlimeVR-GUI-Linux-{0}', env.BUILD_ARCH) }}
|
||||
@@ -121,7 +121,7 @@ jobs:
|
||||
|
||||
- if: matrix.os == 'macos-latest'
|
||||
name: Upload a Build Artifact (macOS)
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
# Artifact name
|
||||
name: SlimeVR-GUI-macOS
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
run: |
|
||||
npx @slimevr/update-manifest-generator@latest
|
||||
|
||||
- uses: actions/upload-artifact@v5
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: "update-manifest.json"
|
||||
path: ./update-manifest.json
|
||||
|
||||
26
.github/workflows/gradle.yaml
vendored
26
.github/workflows/gradle.yaml
vendored
@@ -68,7 +68,7 @@ jobs:
|
||||
run: ./gradlew :server:desktop:shadowJar
|
||||
|
||||
- name: Upload the Server JAR as a Build Artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
# Artifact name
|
||||
name: 'SlimeVR-Server' # optional, default is artifact
|
||||
@@ -127,7 +127,7 @@ jobs:
|
||||
ANDROID_KEY_PASSWD: ${{ secrets.ANDROID_KEY_PASSWD }}
|
||||
|
||||
- name: Upload the Android build artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
# Artifact name
|
||||
name: 'SlimeVR-Android' # optional, default is artifact
|
||||
@@ -158,7 +158,7 @@ jobs:
|
||||
ANDROID_KEY_PASSWD: ${{ secrets.ANDROID_GPLAY_KEY_PASSWD }}
|
||||
|
||||
- name: Upload the Google Play artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
# Artifact name
|
||||
@@ -181,7 +181,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: actions/download-artifact@v6
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: 'SlimeVR-Server'
|
||||
path: server/desktop/build/libs/
|
||||
@@ -230,23 +230,23 @@ jobs:
|
||||
run: |
|
||||
tar czf slimevr-gui-dist.tar.gz -C gui/dist/ .
|
||||
|
||||
- uses: actions/upload-artifact@v5
|
||||
- uses: actions/upload-artifact@v6
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
with:
|
||||
name: SlimeVR-GUI-Dist
|
||||
path: ./slimevr-gui-dist.tar.gz
|
||||
|
||||
- uses: actions/upload-artifact@v5
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ format('SlimeVR-GUI-Deb-{0}', env.BUILD_ARCH) }}
|
||||
path: target/release/bundle/deb/slimevr*.deb
|
||||
|
||||
- uses: actions/upload-artifact@v5
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ format('SlimeVR-GUI-AppImage-{0}', env.BUILD_ARCH) }}
|
||||
path: target/release/bundle/appimage/slimevr*.AppImage
|
||||
|
||||
- uses: actions/upload-artifact@v5
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ format('SlimeVR-GUI-RPM-{0}', env.BUILD_ARCH) }}
|
||||
path: target/release/bundle/rpm/slimevr*.rpm
|
||||
@@ -279,7 +279,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: actions/download-artifact@v6
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: 'SlimeVR-Server'
|
||||
path: server/desktop/build/libs/
|
||||
@@ -320,12 +320,12 @@ jobs:
|
||||
--volicon ../macos/SlimeVR.app/Contents/Resources/icon.icns --skip-jenkins \
|
||||
--eula ../../../../../LICENSE-MIT slimevr.dmg ../macos/SlimeVR.app
|
||||
|
||||
- uses: actions/upload-artifact@v5
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: SlimeVR-GUI-MacApp
|
||||
path: target/universal-apple-darwin/release/bundle/macos/SlimeVR*.app
|
||||
|
||||
- uses: actions/upload-artifact@v5
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: SlimeVR-GUI-MacDmg
|
||||
path: target/universal-apple-darwin/release/bundle/dmg/slimevr.dmg
|
||||
@@ -359,7 +359,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: actions/download-artifact@v6
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: 'SlimeVR-Server'
|
||||
path: server/desktop/build/libs/
|
||||
@@ -399,7 +399,7 @@ jobs:
|
||||
cp target/release/slimevr.exe ./SlimeVR/
|
||||
7z a -tzip "SlimeVR-$BUILD_ARCH.zip" ./SlimeVR/
|
||||
|
||||
- uses: actions/upload-artifact@v5
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ format('SlimeVR-GUI-Windows-{0}', env.BUILD_ARCH) }}
|
||||
path: ./SlimeVR*.zip
|
||||
|
||||
@@ -116,3 +116,9 @@ licensed under `GPL-v3`.
|
||||
## Discord
|
||||
We use discord *a lot* to coordinate and discuss development. Come join us at
|
||||
https://discord.gg/SlimeVR!
|
||||
|
||||
## Use of AI
|
||||
We DO NOT accept contributions that are generated with AI (for example, "vibe-coding").
|
||||
|
||||
If you do use AI, and you believe your usage of AI is reasonable, you must clearly disclose
|
||||
how you used AI in your submission.
|
||||
|
||||
@@ -109,6 +109,11 @@ board_type-XIAO_ESP32C3 = Seeed Studio XIAO ESP32C3
|
||||
board_type-HARITORA = Haritora
|
||||
board_type-ESP32C6DEVKITC1 = Espressif ESP32-C6 DevKitC-1
|
||||
board_type-GLOVE_IMU_SLIMEVR_DEV = SlimeVR Dev IMU Glove
|
||||
board_type-GESTURES = Gestures
|
||||
board_type-ESP32S3_SUPERMINI = ESP32-S3 Supermini
|
||||
board_type-GENERIC_NRF = Generic nRF
|
||||
board_type-SLIMEVR_BUTTERFLY_DEV = SlimeVR Dev Butterfly
|
||||
board_type-SLIMEVR_BUTTERFLY = SlimeVR Butterfly
|
||||
|
||||
## Proportions
|
||||
skeleton_bone-NONE = None
|
||||
@@ -331,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
|
||||
@@ -370,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
|
||||
@@ -407,6 +417,7 @@ tracker-settings-update = Update now
|
||||
tracker-settings-update-title = Firmware version
|
||||
tracker-settings-current-version = Current
|
||||
tracker-settings-latest-version = Latest
|
||||
tracker-settings-build-date = Build Date
|
||||
|
||||
|
||||
## Tracker part card info
|
||||
@@ -567,6 +578,10 @@ settings-general-tracker_mechanics-use_mag_on_all_trackers-description =
|
||||
Uses magnetometer on all trackers that have a compatible firmware for it, reducing drift in stable magnetic environments.
|
||||
Can be disabled per tracker in the tracker's settings. <b>Please don't shutdown any of the trackers while toggling this!</b>
|
||||
settings-general-tracker_mechanics-use_mag_on_all_trackers-label = Use magnetometer on trackers
|
||||
settings-general-tracker_mechanics-trackers_over_usb = Trackers over USB
|
||||
settings-general-tracker_mechanics-trackers_over_usb-description =
|
||||
Enables receiving HID tracker data over USB. Make sure connected trackers have <b>connection over HID</b> enabled!
|
||||
settings-general-tracker_mechanics-trackers_over_usb-enabled-label = Allow HID trackers to connect directly over USB
|
||||
|
||||
settings-stay_aligned = Stay Aligned
|
||||
settings-stay_aligned-description = Stay Aligned reduces drift by gradually adjusting your trackers to match your relaxed poses.
|
||||
@@ -1342,6 +1357,7 @@ onboarding-stay_aligned-previous_step = Previous
|
||||
onboarding-stay_aligned-next_step = Next
|
||||
onboarding-stay_aligned-restart = Restart
|
||||
onboarding-stay_aligned-done = Done
|
||||
onboarding-stay_aligned-manual_mounting-done = Done
|
||||
|
||||
## Home
|
||||
home-no_trackers = No trackers detected or assigned
|
||||
@@ -1393,6 +1409,9 @@ firmware_tool-select_source-firmware = Firmware Source
|
||||
firmware_tool-select_source-version = Firmware Version
|
||||
firmware_tool-select_source-official = Official
|
||||
firmware_tool-select_source-dev = Dev
|
||||
firmware_tool-select_source-not_selected = No source selected
|
||||
firmware_tool-select_source-no_boards = No available boards for this source
|
||||
firmware_tool-select_source-no_versions = No available versions for this source
|
||||
|
||||
firmware_tool-board_defaults = Configure your board
|
||||
firmware_tool-board_defaults-description = Set the pins or settings relative to your hardware
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -18,6 +18,8 @@ SUBSYSTEMS=="usb", ATTRS{idVendor}=="1A86", ATTRS{idProduct}=="7522", MODE="0660
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1A86", ATTRS{idProduct}=="7523", MODE="0660", TAG+="uaccess"
|
||||
# CH341
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1A86", ATTRS{idProduct}=="5523", MODE="0660", TAG+="uaccess"
|
||||
# CH343
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1A86", ATTRS{idProduct}=="55D3", MODE="0660", TAG+="uaccess"
|
||||
# CH9102x
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1A86", ATTRS{idProduct}=="55D4", MODE="0660", TAG+="uaccess"
|
||||
|
||||
@@ -26,8 +28,10 @@ SUBSYSTEMS=="usb", ATTRS{idVendor}=="1A86", ATTRS{idProduct}=="55D4", MODE="0660
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="10C4", ATTRS{idProduct}=="EA60", MODE="0660", TAG+="uaccess"
|
||||
|
||||
## Espressif
|
||||
# ESP32-C3
|
||||
# ESP32-S3 / ESP32-C3 / ESP32-C5 / ESP32-C6 / ESP32-C61 / ESP32-H2 / ESP32-P4
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="303A", ATTRS{idProduct}=="1001", MODE="0660", TAG+="uaccess"
|
||||
# ESP32-S2
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="303A", ATTRS{idProduct}=="0002", MODE="0660", TAG+="uaccess"
|
||||
|
||||
## FTDI
|
||||
# FT232BM/L/Q, FT245BM/L/Q
|
||||
|
||||
@@ -156,9 +156,9 @@ export function TopBar({
|
||||
<>
|
||||
<div className="flex gap-0 flex-col">
|
||||
<div className="h-[3px]" />
|
||||
<div data-tauri-drag-region className="flex gap-2 h-[38px] z-50">
|
||||
<div data-tauri-drag-region className="flex gap-2 h-[38px] z-49">
|
||||
<div
|
||||
className="flex px-2 py-2 justify-around z-50"
|
||||
className="flex px-2 py-2 justify-around z-49"
|
||||
data-tauri-drag-region
|
||||
>
|
||||
<div className="flex gap-2" data-tauri-drag-region>
|
||||
|
||||
@@ -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]}
|
||||
|
||||
@@ -10,7 +10,7 @@ export function TipBox({
|
||||
whitespace = false,
|
||||
className,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
children?: ReactNode;
|
||||
hideIcon?: boolean;
|
||||
whitespace?: boolean;
|
||||
className?: string;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -101,13 +101,10 @@ export function SelectSourceSetep({
|
||||
curr.push({
|
||||
name: source.source,
|
||||
official: source.official,
|
||||
disabled:
|
||||
!partialBoard?.board ||
|
||||
!source.availableBoards.includes(partialBoard.board),
|
||||
});
|
||||
return curr;
|
||||
},
|
||||
[] as { name: string; official: boolean; disabled: boolean }[]
|
||||
[] as { name: string; official: boolean }[]
|
||||
)
|
||||
.sort((a, b) => {
|
||||
if (a.official !== b.official) return a.official ? -1 : 1;
|
||||
@@ -115,6 +112,7 @@ export function SelectSourceSetep({
|
||||
}),
|
||||
possibleBoards: sources
|
||||
?.reduce((curr, source) => {
|
||||
if (source.source !== partialBoard?.source) return curr;
|
||||
const unknownBoards = source.availableBoards.filter(
|
||||
(b) => !curr.includes(b)
|
||||
);
|
||||
@@ -136,6 +134,7 @@ export function SelectSourceSetep({
|
||||
possibleVersions: sources
|
||||
?.reduce(
|
||||
(curr, source) => {
|
||||
if (source.source !== partialBoard?.source) return curr;
|
||||
if (!curr.find(({ name }) => source.version === name))
|
||||
curr.push({
|
||||
disabled:
|
||||
@@ -193,6 +192,25 @@ export function SelectSourceSetep({
|
||||
{!isFetching && !isError && (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="grid md:grid-cols-3 gap-4">
|
||||
<div className="flex flex-col gap-1 w-full">
|
||||
<Localized id="firmware_tool-select_source-firmware">
|
||||
<Typography variant="section-title" />
|
||||
</Localized>
|
||||
<div className="flex flex-col gap-4 md:max-h-[305px] overflow-y-auto bg-background-80 rounded-lg p-4">
|
||||
{sourcesGroupped?.map(({ name, official }) => (
|
||||
<Selector
|
||||
active={partialBoard?.source === name}
|
||||
key={`${name}`}
|
||||
tag={official ? 'official' : undefined}
|
||||
onClick={() => {
|
||||
if (partialBoard?.source !== name)
|
||||
setPartialBoard({ source: name });
|
||||
}}
|
||||
text={formatSource(name, official)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 w-full">
|
||||
<Localized id="firmware_tool-select_source-board_type">
|
||||
<Typography variant="section-title" />
|
||||
@@ -203,7 +221,7 @@ export function SelectSourceSetep({
|
||||
active={partialBoard?.board === board}
|
||||
key={`${board}`}
|
||||
onClick={() => {
|
||||
setPartialBoard({ board });
|
||||
setPartialBoard((curr) => ({ ...curr, board }));
|
||||
}}
|
||||
tag={
|
||||
board.startsWith('BOARD_SLIMEVR')
|
||||
@@ -219,30 +237,15 @@ export function SelectSourceSetep({
|
||||
}
|
||||
/>
|
||||
))}
|
||||
{partialBoard?.source && possibleBoards?.length === 0 && (
|
||||
<Typography id="firmware_tool-select_source-no_boards" />
|
||||
)}
|
||||
{!partialBoard?.source && (
|
||||
<Typography id="firmware_tool-select_source-not_selected" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 w-full">
|
||||
<Localized id="firmware_tool-select_source-firmware">
|
||||
<Typography variant="section-title" />
|
||||
</Localized>
|
||||
<div className="flex flex-col gap-4 md:max-h-[305px] overflow-y-auto bg-background-80 rounded-lg p-4">
|
||||
{sourcesGroupped?.map(({ name, official, disabled }) => (
|
||||
<Selector
|
||||
active={partialBoard?.source === name}
|
||||
disabled={disabled}
|
||||
key={`${name}`}
|
||||
tag={official ? 'official' : undefined}
|
||||
onClick={() => {
|
||||
setPartialBoard((curr) => ({
|
||||
...curr,
|
||||
source: name,
|
||||
}));
|
||||
}}
|
||||
text={formatSource(name, official)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-1 w-full">
|
||||
<Localized id="firmware_tool-select_source-version">
|
||||
<Typography variant="section-title" />
|
||||
@@ -268,6 +271,12 @@ export function SelectSourceSetep({
|
||||
text={name}
|
||||
/>
|
||||
))}
|
||||
{partialBoard?.source && possibleVersions?.length === 0 && (
|
||||
<Typography id="firmware_tool-select_source-no_versions" />
|
||||
)}
|
||||
{!partialBoard?.source && (
|
||||
<Typography id="firmware_tool-select_source-not_selected" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -28,18 +28,23 @@ export function ResetButtonIcon(options: UseResetOptions) {
|
||||
}
|
||||
|
||||
export function ResetButton({
|
||||
onClick,
|
||||
className,
|
||||
onReseted,
|
||||
children,
|
||||
onFailed,
|
||||
...options
|
||||
}: {
|
||||
onClick?: () => void;
|
||||
className?: string;
|
||||
children?: ReactNode;
|
||||
onReseted?: () => void;
|
||||
onFailed?: () => void;
|
||||
} & UseResetOptions) {
|
||||
const { triggerReset, status, timer, disabled, name, error } = useReset(
|
||||
options,
|
||||
onReseted
|
||||
onReseted,
|
||||
onFailed
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -60,7 +65,10 @@ export function ResetButton({
|
||||
>
|
||||
<Button
|
||||
icon={<ResetButtonIcon {...options} />}
|
||||
onClick={triggerReset}
|
||||
onClick={() => {
|
||||
if (onClick) onClick();
|
||||
triggerReset();
|
||||
}}
|
||||
className={classNames(
|
||||
'border-2 py-[5px]',
|
||||
status === 'finished'
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { ReactNode, useCallback, useMemo, useState } from 'react';
|
||||
import { AssignTrackerRequestT, BodyPart, RpcMessage } from 'solarxr-protocol';
|
||||
import { useOnboarding } from '@/hooks/onboarding';
|
||||
import { useWebsocketAPI } from '@/hooks/websocket-api';
|
||||
@@ -12,7 +12,7 @@ import { TipBox } from '@/components/commons/TipBox';
|
||||
import { Typography } from '@/components/commons/Typography';
|
||||
import { BodyAssignment } from '@/components/onboarding/BodyAssignment';
|
||||
import { MountingSelectionMenu } from './MountingSelectionMenu';
|
||||
import { useLocalization } from '@fluent/react';
|
||||
import { Localized } from '@fluent/react';
|
||||
import { useBreakpoint } from '@/hooks/breakpoint';
|
||||
import { Quaternion } from 'three';
|
||||
import { AssignMode, defaultConfig, useConfig } from '@/hooks/config';
|
||||
@@ -22,7 +22,6 @@ import * as Sentry from '@sentry/react';
|
||||
|
||||
export function ManualMountingPage() {
|
||||
const { isMobile } = useBreakpoint('mobile');
|
||||
const { l10n } = useLocalization();
|
||||
const { applyProgress, state } = useOnboarding();
|
||||
const { sendRPCPacket } = useWebsocketAPI();
|
||||
const { config } = useConfig();
|
||||
@@ -103,28 +102,26 @@ export function ManualMountingPage() {
|
||||
<div className="flex flex-col gap-5 h-full items-center w-full xs:justify-center relative overflow-y-auto">
|
||||
<div className="flex xs:flex-row mobile:flex-col h-full px-8 xs:w-full xs:justify-center mobile:px-4 items-center">
|
||||
<div className="flex flex-col w-full xs:max-w-sm gap-3">
|
||||
<Typography variant="main-title">
|
||||
{l10n.getString('onboarding-manual_mounting')}
|
||||
</Typography>
|
||||
<Typography>
|
||||
{l10n.getString('onboarding-manual_mounting-description')}
|
||||
</Typography>
|
||||
<TipBox>{l10n.getString('tips-find_tracker')}</TipBox>
|
||||
<Typography variant="main-title" id="onboarding-manual_mounting" />
|
||||
<Typography id="onboarding-manual_mounting-description" />
|
||||
<Typography id="tips-find_tracker" />
|
||||
<Localized id="tips-find_tracker">
|
||||
<TipBox />
|
||||
</Localized>
|
||||
|
||||
<div className="flex flex-row gap-3 mt-auto">
|
||||
<Button
|
||||
variant="secondary"
|
||||
to="/onboarding/mounting/choose"
|
||||
state={state}
|
||||
>
|
||||
{l10n.getString('onboarding-previous_step')}
|
||||
</Button>
|
||||
id="onboarding-previous_step"
|
||||
/>
|
||||
{!state.alonePage && (
|
||||
<Button
|
||||
variant="primary"
|
||||
to="/onboarding/body-proportions/scaled"
|
||||
>
|
||||
{l10n.getString('onboarding-manual_mounting-next')}
|
||||
</Button>
|
||||
id="onboarding-manual_mounting-next"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -142,3 +139,109 @@ export function ManualMountingPage() {
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function ManualMountingPageStayAligned({
|
||||
children,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
}) {
|
||||
const { isMobile } = useBreakpoint('mobile');
|
||||
const { sendRPCPacket } = useWebsocketAPI();
|
||||
const { config } = useConfig();
|
||||
|
||||
const [selectedRole, setSelectRole] = useState<BodyPart>(BodyPart.NONE);
|
||||
|
||||
const assignedTrackers = useAtomValue(assignedTrackersAtom);
|
||||
|
||||
const trackerPartGrouped = useMemo(
|
||||
() =>
|
||||
assignedTrackers.reduce<{ [key: number]: FlatDeviceTracker[] }>(
|
||||
(curr, td) => {
|
||||
const key = td.tracker.info?.bodyPart || BodyPart.NONE;
|
||||
return {
|
||||
...curr,
|
||||
[key]: [...(curr[key] || []), td],
|
||||
};
|
||||
},
|
||||
{}
|
||||
),
|
||||
[assignedTrackers]
|
||||
);
|
||||
|
||||
const onDirectionSelected = (mountingOrientationDegrees: Quaternion) => {
|
||||
(trackerPartGrouped[selectedRole] || []).forEach((td) => {
|
||||
const assignreq = new AssignTrackerRequestT();
|
||||
|
||||
assignreq.bodyPosition = td.tracker.info?.bodyPart || BodyPart.NONE;
|
||||
assignreq.mountingOrientation = MountingOrientationDegreesToQuatT(
|
||||
mountingOrientationDegrees
|
||||
);
|
||||
assignreq.trackerId = td.tracker.trackerId;
|
||||
assignreq.allowDriftCompensation = false;
|
||||
|
||||
sendRPCPacket(RpcMessage.AssignTrackerRequest, assignreq);
|
||||
Sentry.metrics.count('manual_mounting_set', 1, {
|
||||
attributes: {
|
||||
part: BodyPart[assignreq.bodyPosition],
|
||||
direction: assignreq.mountingOrientation,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
setSelectRole(BodyPart.NONE);
|
||||
};
|
||||
|
||||
const getCurrRotation = useCallback(
|
||||
(role: BodyPart) => {
|
||||
if (role === BodyPart.NONE) return undefined;
|
||||
|
||||
const trackers = trackerPartGrouped[role] || [];
|
||||
const [mountingOrientation, ...orientation] = trackers
|
||||
.map((td) => td.tracker.info?.mountingOrientation)
|
||||
.filter((orientation) => !!orientation)
|
||||
.map((orientation) => QuaternionFromQuatT(orientation));
|
||||
|
||||
const identicalOrientations =
|
||||
mountingOrientation !== undefined &&
|
||||
orientation.every((quat) =>
|
||||
similarQuaternions(quat, mountingOrientation)
|
||||
);
|
||||
return identicalOrientations ? mountingOrientation : undefined;
|
||||
},
|
||||
[trackerPartGrouped]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<MountingSelectionMenu
|
||||
bodyPart={selectedRole}
|
||||
currRotation={getCurrRotation(selectedRole)}
|
||||
isOpen={selectedRole !== BodyPart.NONE}
|
||||
onClose={() => setSelectRole(BodyPart.NONE)}
|
||||
onDirectionSelected={onDirectionSelected}
|
||||
/>
|
||||
<div className="flex flex-col gap-5 h-full items-center w-full xs:justify-center relative overflow-y-auto">
|
||||
<div className="flex xs:flex-row mobile:flex-col h-full px-8 xs:w-full xs:justify-center mobile:px-4 items-center">
|
||||
<div className="flex flex-col w-full xs:max-w-sm gap-3">
|
||||
<Typography variant="main-title" id="onboarding-manual_mounting" />
|
||||
<Typography id="onboarding-manual_mounting-description" />
|
||||
<Typography id="tips-find_tracker" />
|
||||
<Localized id="tips-find_tracker">
|
||||
<TipBox />
|
||||
</Localized>
|
||||
{children}
|
||||
</div>
|
||||
<div className="flex flex-row justify-center">
|
||||
<BodyAssignment
|
||||
width={isMobile ? 160 : undefined}
|
||||
mirror={config?.mirrorView ?? defaultConfig.mirrorView}
|
||||
onlyAssigned={true}
|
||||
assignMode={AssignMode.All}
|
||||
onRoleSelected={setSelectRole}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -238,7 +238,7 @@ export function MountingSelectionMenu({
|
||||
shouldCloseOnEsc
|
||||
onRequestClose={onClose}
|
||||
overlayClassName={classNames(
|
||||
'fixed top-0 right-0 left-0 bottom-0 flex flex-col items-center w-full h-full bg-background-90 bg-opacity-90 z-20'
|
||||
'fixed top-0 right-0 left-0 bottom-0 flex flex-col items-center w-full h-full bg-background-90 bg-opacity-90 z-50'
|
||||
)}
|
||||
className={classNames(
|
||||
'focus:ring-transparent focus:ring-offset-transparent focus:outline-transparent outline-none mt-20 z-10'
|
||||
|
||||
@@ -1,32 +1,33 @@
|
||||
import { useState } from 'react';
|
||||
import { Button } from '@/components/commons/Button';
|
||||
import { Typography } from '@/components/commons/Typography';
|
||||
import { ResetType } from 'solarxr-protocol';
|
||||
import { ResetButton } from '@/components/home/ResetButton';
|
||||
import { useLocalization } from '@fluent/react';
|
||||
import { useBreakpoint } from '@/hooks/breakpoint';
|
||||
import { VerticalStepComponentProps } from '@/components/commons/VerticalStepper';
|
||||
|
||||
import { BaseModal } from '@/components/commons/BaseModal';
|
||||
import { ManualMountingPageStayAligned } from '@/components/onboarding/pages/mounting/ManualMounting';
|
||||
export function VerifyMountingStep({
|
||||
nextStep,
|
||||
prevStep,
|
||||
}: VerticalStepComponentProps) {
|
||||
const { isMobile } = useBreakpoint('mobile');
|
||||
const { l10n } = useLocalization();
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const [disableMounting, setDisableMounting] = useState(false);
|
||||
|
||||
const goNextStep = () => {
|
||||
setDisableMounting(false);
|
||||
setOpen(false);
|
||||
nextStep();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col flex-grow justify-between py-2 gap-2">
|
||||
<div className="flex flex-col flex-grow">
|
||||
<div className="flex flex-grow flex-col gap-4 max-w-sm">
|
||||
<div className="flex flex-col gap-2">
|
||||
<Typography>
|
||||
{l10n.getString(
|
||||
'onboarding-automatic_mounting-mounting_reset-step-0'
|
||||
)}
|
||||
</Typography>
|
||||
<Typography>
|
||||
{l10n.getString(
|
||||
'onboarding-automatic_mounting-mounting_reset-step-1'
|
||||
)}
|
||||
</Typography>
|
||||
<Typography id="onboarding-automatic_mounting-mounting_reset-step-0" />
|
||||
<Typography id="onboarding-automatic_mounting-mounting_reset-step-1" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -50,13 +51,36 @@ export function VerifyMountingStep({
|
||||
</div>
|
||||
)}
|
||||
<div className="flex gap-3 justify-between">
|
||||
<Button variant={'secondary'} onClick={prevStep}>
|
||||
{l10n.getString('onboarding-automatic_mounting-prev_step')}
|
||||
</Button>
|
||||
<Button
|
||||
variant={'secondary'}
|
||||
onClick={prevStep}
|
||||
id="onboarding-automatic_mounting-prev_step"
|
||||
/>
|
||||
<Button
|
||||
disabled={disableMounting}
|
||||
variant={'secondary'}
|
||||
className="self-start mt-auto"
|
||||
onClick={() => setOpen(true)}
|
||||
id="onboarding-automatic_mounting-manual_mounting"
|
||||
/>
|
||||
<BaseModal isOpen={isOpen} onRequestClose={() => setOpen(false)}>
|
||||
<ManualMountingPageStayAligned>
|
||||
<div className="flex flex-row gap-3 mt-auto">
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={goNextStep}
|
||||
id="onboarding-stay_aligned-manual_mounting-done"
|
||||
/>
|
||||
</div>
|
||||
</ManualMountingPageStayAligned>
|
||||
</BaseModal>
|
||||
|
||||
<ResetButton
|
||||
onClick={() => setDisableMounting(true)}
|
||||
type={ResetType.Mounting}
|
||||
group="default"
|
||||
onReseted={nextStep}
|
||||
onReseted={goNextStep}
|
||||
onFailed={() => setDisableMounting(false)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
SettingsResponseT,
|
||||
SteamVRTrackersSettingT,
|
||||
TapDetectionSettingsT,
|
||||
HIDSettingsT,
|
||||
} from 'solarxr-protocol';
|
||||
import { useConfig } from '@/hooks/config';
|
||||
import { useWebsocketAPI } from '@/hooks/websocket-api';
|
||||
@@ -101,6 +102,9 @@ export type SettingsForm = {
|
||||
};
|
||||
resetsSettings: ResetSettingsForm;
|
||||
stayAligned: StayAlignedSettingsForm;
|
||||
hidSettings: {
|
||||
trackersOverHID: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
const defaultValues: SettingsForm = {
|
||||
@@ -156,6 +160,7 @@ const defaultValues: SettingsForm = {
|
||||
legTweaks: { correctionStrength: 0.3 },
|
||||
resetsSettings: defaultResetSettings,
|
||||
stayAligned: defaultStayAlignedSettings,
|
||||
hidSettings: { trackersOverHID: false },
|
||||
};
|
||||
|
||||
export function GeneralSettings() {
|
||||
@@ -277,6 +282,10 @@ export function GeneralSettings() {
|
||||
|
||||
settings.stayAligned = serializeStayAlignedSettings(values.stayAligned);
|
||||
|
||||
const hidSettings = new HIDSettingsT();
|
||||
hidSettings.trackersOverHid = values.hidSettings.trackersOverHID;
|
||||
settings.hidSettings = hidSettings;
|
||||
|
||||
if (values.resetsSettings) {
|
||||
settings.resetsSettings = loadResetSettings(values.resetsSettings);
|
||||
}
|
||||
@@ -392,6 +401,12 @@ export function GeneralSettings() {
|
||||
);
|
||||
}
|
||||
|
||||
if (settings.hidSettings) {
|
||||
formData.hidSettings = {
|
||||
trackersOverHID: settings.hidSettings.trackersOverHid,
|
||||
};
|
||||
}
|
||||
|
||||
reset({ ...getValues(), ...formData });
|
||||
});
|
||||
|
||||
@@ -689,6 +704,28 @@ export function GeneralSettings() {
|
||||
settingType="general"
|
||||
id="mechanics-magnetometer"
|
||||
/>
|
||||
<div className="flex flex-col pt-5 pb-3">
|
||||
<Typography variant="section-title">
|
||||
{l10n.getString(
|
||||
'settings-general-tracker_mechanics-trackers_over_usb'
|
||||
)}
|
||||
</Typography>
|
||||
<Localized
|
||||
id="settings-general-tracker_mechanics-trackers_over_usb-description"
|
||||
elems={{ b: <b /> }}
|
||||
>
|
||||
<Typography />
|
||||
</Localized>
|
||||
</div>
|
||||
<CheckBox
|
||||
variant="toggle"
|
||||
outlined
|
||||
control={control}
|
||||
name="hidSettings.trackersOverHID"
|
||||
label={l10n.getString(
|
||||
'settings-general-tracker_mechanics-trackers_over_usb-enabled-label'
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
</SettingsPagePaneLayout>
|
||||
<SettingsPagePaneLayout icon={<WrenchIcon />} id="fksettings">
|
||||
|
||||
@@ -54,11 +54,11 @@ export function TrackingChecklistSettings({
|
||||
// doing it this way prevents calling ignore step for every step.
|
||||
// that prevent sending a packet for steps that didnt change
|
||||
if (!value && !ignoredSteps.includes(stepId)) {
|
||||
ignoreStep(stepId, true);
|
||||
ignoreStep(stepId, true, false);
|
||||
}
|
||||
|
||||
if (value && ignoredSteps.includes(stepId)) {
|
||||
ignoreStep(stepId, false);
|
||||
ignoreStep(stepId, false, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2,11 +2,14 @@ import { useConfig } from '@/hooks/config';
|
||||
import { useLocaleConfig } from '@/i18n/config';
|
||||
import { BatteryIcon } from '@/components/commons/icon/BatteryIcon';
|
||||
import { Typography } from '@/components/commons/Typography';
|
||||
import { Tooltip } from '@/components/commons/Tooltip';
|
||||
|
||||
export function TrackerBattery({
|
||||
value,
|
||||
voltage,
|
||||
runtime,
|
||||
disabled,
|
||||
moreInfo = false,
|
||||
textColor = 'primary',
|
||||
}: {
|
||||
/**
|
||||
@@ -14,7 +17,9 @@ export function TrackerBattery({
|
||||
*/
|
||||
value: number;
|
||||
voltage?: number | null;
|
||||
runtime?: bigint | null;
|
||||
disabled?: boolean;
|
||||
moreInfo?: boolean;
|
||||
textColor?: string;
|
||||
}) {
|
||||
const { currentLocales } = useLocaleConfig();
|
||||
@@ -28,31 +33,49 @@ export function TrackerBattery({
|
||||
});
|
||||
|
||||
const charging = (voltage || 0) > 4.3;
|
||||
const showVoltage = voltage && config?.debug;
|
||||
const debug = config?.debug || config?.devSettings.moreInfo;
|
||||
const showVoltage = moreInfo && voltage && debug;
|
||||
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
<div className="flex flex-col justify-around">
|
||||
<BatteryIcon value={value} disabled={disabled} charging={charging} />
|
||||
<Tooltip
|
||||
disabled={charging || !runtime || debug}
|
||||
preferedDirection="left"
|
||||
content=<Typography>{percentFormatter.format(value)}</Typography>
|
||||
>
|
||||
<div className="flex gap-2">
|
||||
<div className="flex flex-col justify-around">
|
||||
<BatteryIcon value={value} disabled={disabled} charging={charging} />
|
||||
</div>
|
||||
{((!charging || showVoltage) && (
|
||||
<div className="w-15">
|
||||
{!charging && runtime != null && runtime > 0 && (
|
||||
<Typography color={textColor}>
|
||||
{(runtime / BigInt(3600000000)).toString() +
|
||||
'h ' +
|
||||
(
|
||||
(runtime % BigInt(3600000000)) /
|
||||
BigInt(60000000)
|
||||
).toString() +
|
||||
'min'}
|
||||
</Typography>
|
||||
)}
|
||||
{!charging && (!runtime || debug) && (
|
||||
<Typography color={textColor}>
|
||||
{percentFormatter.format(value)}
|
||||
</Typography>
|
||||
)}
|
||||
{showVoltage && (
|
||||
<Typography color={textColor}>
|
||||
{voltageFormatter.format(voltage)}V
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
)) || (
|
||||
<div className="flex flex-col justify-center w-15">
|
||||
<div className="w-5 h-1 bg-background-30 rounded-full" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{((!charging || showVoltage) && (
|
||||
<div className="w-10">
|
||||
{!charging && (
|
||||
<Typography color={textColor}>
|
||||
{percentFormatter.format(value)}
|
||||
</Typography>
|
||||
)}
|
||||
{showVoltage && (
|
||||
<Typography color={textColor}>
|
||||
{voltageFormatter.format(voltage)}V
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
)) || (
|
||||
<div className="flex flex-col justify-center w-10">
|
||||
<div className="w-5 h-1 bg-background-30 rounded-full" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,9 @@ function TrackerBig({
|
||||
<TrackerBattery
|
||||
voltage={device.hardwareStatus.batteryVoltage}
|
||||
value={device.hardwareStatus.batteryPctEstimate / 100}
|
||||
runtime={device.hardwareStatus.batteryRuntimeEstimate}
|
||||
disabled={tracker.status === TrackerStatusEnum.DISCONNECTED}
|
||||
moreInfo={true}
|
||||
/>
|
||||
)}
|
||||
<div className="flex gap-2">
|
||||
@@ -119,6 +121,7 @@ function TrackerSmol({
|
||||
<TrackerBattery
|
||||
voltage={device.hardwareStatus.batteryVoltage}
|
||||
value={device.hardwareStatus.batteryPctEstimate / 100}
|
||||
runtime={device.hardwareStatus.batteryRuntimeEstimate}
|
||||
disabled={tracker.status === TrackerStatusEnum.DISCONNECTED}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -199,7 +199,7 @@ export function TrackerSettingsPage() {
|
||||
shakeHighlight={false}
|
||||
/>
|
||||
)}
|
||||
{
|
||||
{tracker?.device?.hardwareInfo?.hardwareIdentifier != 'Unknown' && (
|
||||
<div className="flex flex-col bg-background-70 p-3 rounded-lg gap-2">
|
||||
<Typography
|
||||
variant="section-title"
|
||||
@@ -208,40 +208,53 @@ export function TrackerSettingsPage() {
|
||||
Firmware version
|
||||
</Typography>
|
||||
<div className="flex gap-2 flex-col">
|
||||
<div className="flex justify-between gap-2">
|
||||
<Typography id="tracker-settings-build-date" />
|
||||
<Typography
|
||||
whitespace="whitespace-pre-wrap"
|
||||
textAlign="text-end"
|
||||
>
|
||||
{tracker?.device?.hardwareInfo?.firmwareDate || '--'}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className="flex justify-between gap-2">
|
||||
<Typography id="tracker-settings-current-version" />
|
||||
<Typography
|
||||
whitespace="whitespace-pre-wrap"
|
||||
textAlign="text-end"
|
||||
>
|
||||
v{tracker?.device?.hardwareInfo?.firmwareVersion}
|
||||
{tracker?.device?.hardwareInfo?.firmwareVersion
|
||||
? `v${tracker?.device?.hardwareInfo?.firmwareVersion}`
|
||||
: '--'}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className="flex justify-between gap-2">
|
||||
<Typography id="tracker-settings-latest-version" />
|
||||
{!updateUnavailable && (
|
||||
<>
|
||||
{currentFirmwareRelease && (
|
||||
<Typography
|
||||
color={
|
||||
needUpdate === 'updated'
|
||||
? undefined
|
||||
: 'text-accent-background-10'
|
||||
}
|
||||
textAlign="text-end"
|
||||
whitespace="whitespace-pre-wrap"
|
||||
>
|
||||
{currentFirmwareRelease.name}
|
||||
</Typography>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{updateUnavailable && (
|
||||
<Typography id="tracker-settings-update-unavailable-v2">
|
||||
No releases found
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
{!!tracker?.device?.hardwareInfo?.officialBoardType && (
|
||||
<div className="flex justify-between gap-2">
|
||||
<Typography id="tracker-settings-latest-version" />
|
||||
{!updateUnavailable && (
|
||||
<>
|
||||
{currentFirmwareRelease && (
|
||||
<Typography
|
||||
color={
|
||||
needUpdate === 'updated'
|
||||
? undefined
|
||||
: 'text-accent-background-10'
|
||||
}
|
||||
textAlign="text-end"
|
||||
whitespace="whitespace-pre-wrap"
|
||||
>
|
||||
{currentFirmwareRelease.name}
|
||||
</Typography>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{updateUnavailable && (
|
||||
<Typography id="tracker-settings-update-unavailable-v2">
|
||||
No releases found
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{!updateUnavailable && (
|
||||
<Tooltip
|
||||
@@ -280,7 +293,7 @@ export function TrackerSettingsPage() {
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
|
||||
<div className="flex flex-col bg-background-70 p-3 rounded-lg gap-2 overflow-x-auto">
|
||||
<div className="flex justify-between">
|
||||
@@ -308,10 +321,11 @@ export function TrackerSettingsPage() {
|
||||
<div className="flex justify-between">
|
||||
<Typography>{l10n.getString('tracker-infos-url')}</Typography>
|
||||
<Typography>
|
||||
udp://
|
||||
{IPv4.fromNumber(
|
||||
tracker?.device?.hardwareInfo?.ipAddress?.addr || 0
|
||||
).toString()}
|
||||
{tracker?.device?.hardwareInfo?.ipAddress?.addr
|
||||
? `udp://${IPv4.fromNumber(
|
||||
tracker?.device?.hardwareInfo?.ipAddress?.addr || 0
|
||||
).toString()}`
|
||||
: '--'}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
@@ -367,6 +381,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">
|
||||
|
||||
@@ -229,7 +229,9 @@ function Row({
|
||||
<TrackerBattery
|
||||
value={device.hardwareStatus.batteryPctEstimate / 100}
|
||||
voltage={device.hardwareStatus.batteryVoltage}
|
||||
runtime={device.hardwareStatus.batteryRuntimeEstimate}
|
||||
disabled={tracker.status === TrackerStatusEnum.DISCONNECTED}
|
||||
moreInfo={config?.devSettings.moreInfo}
|
||||
textColor={fontColor}
|
||||
/>
|
||||
)}
|
||||
@@ -243,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>
|
||||
@@ -327,20 +333,20 @@ export function TrackersTable({
|
||||
|
||||
const gridTemplateColumns = useMemo(() => {
|
||||
const cols = [
|
||||
'minmax(150px, 1.5fr)', // Name
|
||||
'minmax(100px, 1fr)', // Type
|
||||
'minmax(60px, 1fr)', // Battery
|
||||
'6rem', // Ping (w-24)
|
||||
'minmax(60px, 1fr)', // TPS
|
||||
config?.devSettings?.preciseRotation ? '11rem' : '8rem', // Rotation
|
||||
'minmax(60px, 1fr)', // Temp
|
||||
'minmax(15rem, 1.5fr)', // Name
|
||||
'9rem', // Type
|
||||
'9rem', // Battery
|
||||
'9rem', // Ping (w-24)
|
||||
'5rem', // TPS
|
||||
config?.devSettings?.preciseRotation ? '11rem' : '9rem', // Rotation
|
||||
'9rem', // Temp
|
||||
];
|
||||
|
||||
if (moreInfo) {
|
||||
cols.push('9rem'); // Linear Acc
|
||||
cols.push('9rem'); // Position
|
||||
cols.push('9rem'); // Stay Aligned
|
||||
cols.push('minmax(150px, 1fr)'); // URL
|
||||
cols.push('11rem'); // URL
|
||||
}
|
||||
|
||||
return cols.join(' ');
|
||||
|
||||
@@ -109,7 +109,10 @@ const stepContentLookup: Record<
|
||||
context: TrackingChecklistContext
|
||||
) => JSX.Element
|
||||
> = {
|
||||
[TrackingChecklistStepId.TRACKERS_REST_CALIBRATION]: (step, { toggle }) => {
|
||||
[TrackingChecklistStepId.TRACKERS_REST_CALIBRATION]: (
|
||||
step,
|
||||
{ toggleSession }
|
||||
) => {
|
||||
return (
|
||||
<div className="space-y-2.5">
|
||||
<Typography id="tracking_checklist-TRACKERS_REST_CALIBRATION-desc" />
|
||||
@@ -118,7 +121,7 @@ const stepContentLookup: Record<
|
||||
<Button
|
||||
id="tracking_checklist-ignore"
|
||||
variant="secondary"
|
||||
onClick={() => toggle(step.id)}
|
||||
onClick={() => toggleSession(step.id)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -166,7 +169,7 @@ const stepContentLookup: Record<
|
||||
</div>
|
||||
);
|
||||
},
|
||||
[TrackingChecklistStepId.STEAMVR_DISCONNECTED]: (step, { toggle }) => {
|
||||
[TrackingChecklistStepId.STEAMVR_DISCONNECTED]: (step, { toggleSession }) => {
|
||||
return (
|
||||
<>
|
||||
<div className="space-y-2.5">
|
||||
@@ -181,7 +184,7 @@ const stepContentLookup: Record<
|
||||
<Button
|
||||
id="tracking_checklist-ignore"
|
||||
variant="secondary"
|
||||
onClick={() => toggle(step.id)}
|
||||
onClick={() => toggleSession(step.id)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -195,7 +198,10 @@ const stepContentLookup: Record<
|
||||
[TrackingChecklistStepId.UNASSIGNED_HMD]: () => {
|
||||
return <Typography id="tracking_checklist-UNASSIGNED_HMD-desc" />;
|
||||
},
|
||||
[TrackingChecklistStepId.NETWORK_PROFILE_PUBLIC]: (step, { toggle }) => {
|
||||
[TrackingChecklistStepId.NETWORK_PROFILE_PUBLIC]: (
|
||||
step,
|
||||
{ toggleSession }
|
||||
) => {
|
||||
const data = step.extraData as TrackingChecklistPublicNetworksT | null;
|
||||
return (
|
||||
<>
|
||||
@@ -226,7 +232,7 @@ const stepContentLookup: Record<
|
||||
<Button
|
||||
id="tracking_checklist-ignore"
|
||||
variant="secondary"
|
||||
onClick={() => toggle(step.id)}
|
||||
onClick={() => toggleSession(step.id)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -234,7 +240,7 @@ const stepContentLookup: Record<
|
||||
</>
|
||||
);
|
||||
},
|
||||
[TrackingChecklistStepId.VRCHAT_SETTINGS]: (step, { toggle }) => {
|
||||
[TrackingChecklistStepId.VRCHAT_SETTINGS]: (step, { toggleSession }) => {
|
||||
return (
|
||||
<>
|
||||
<div className="space-y-2.5">
|
||||
@@ -249,7 +255,7 @@ const stepContentLookup: Record<
|
||||
<Button
|
||||
id="tracking_checklist-ignore"
|
||||
variant="secondary"
|
||||
onClick={() => toggle(step.id)}
|
||||
onClick={() => toggleSession(step.id)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -257,7 +263,7 @@ const stepContentLookup: Record<
|
||||
</>
|
||||
);
|
||||
},
|
||||
[TrackingChecklistStepId.MOUNTING_CALIBRATION]: (step, { toggle }) => {
|
||||
[TrackingChecklistStepId.MOUNTING_CALIBRATION]: (step, { toggleSession }) => {
|
||||
return (
|
||||
<div className="space-y-2.5">
|
||||
<Typography id="onboarding-automatic_mounting-mounting_reset-step-0" />
|
||||
@@ -275,14 +281,17 @@ const stepContentLookup: Record<
|
||||
<Button
|
||||
id="tracking_checklist-ignore"
|
||||
variant="secondary"
|
||||
onClick={() => toggle(step.id)}
|
||||
onClick={() => toggleSession(step.id)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
[TrackingChecklistStepId.FEET_MOUNTING_CALIBRATION]: (step, { toggle }) => {
|
||||
[TrackingChecklistStepId.FEET_MOUNTING_CALIBRATION]: (
|
||||
step,
|
||||
{ toggleSession }
|
||||
) => {
|
||||
return (
|
||||
<div className="space-y-2.5">
|
||||
<Typography id="onboarding-automatic_mounting-mounting_reset-feet-step-0" />
|
||||
@@ -309,14 +318,17 @@ const stepContentLookup: Record<
|
||||
<Button
|
||||
id="tracking_checklist-ignore"
|
||||
variant="secondary"
|
||||
onClick={() => toggle(step.id)}
|
||||
onClick={() => toggleSession(step.id)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
[TrackingChecklistStepId.STAY_ALIGNED_CONFIGURED]: (step, { toggle }) => {
|
||||
[TrackingChecklistStepId.STAY_ALIGNED_CONFIGURED]: (
|
||||
step,
|
||||
{ toggleSession }
|
||||
) => {
|
||||
return (
|
||||
<>
|
||||
<div className="space-y-2.5">
|
||||
@@ -332,7 +344,7 @@ const stepContentLookup: Record<
|
||||
<Button
|
||||
id="tracking_checklist-ignore"
|
||||
variant="secondary"
|
||||
onClick={() => toggle(step.id)}
|
||||
onClick={() => toggleSession(step.id)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -129,9 +129,6 @@ export function checkForUpdate(
|
||||
|
||||
if (
|
||||
!device.hardwareInfo?.officialBoardType ||
|
||||
![BoardType.SLIMEVR, BoardType.SLIMEVR_V1_2].includes(
|
||||
device.hardwareInfo.officialBoardType
|
||||
) ||
|
||||
!semver.valid(currentFirmwareRelease.version) ||
|
||||
!semver.valid(device.hardwareInfo.firmwareVersion?.toString() ?? 'none')
|
||||
) {
|
||||
@@ -143,6 +140,14 @@ export function checkForUpdate(
|
||||
currentFirmwareRelease.version
|
||||
);
|
||||
|
||||
if (
|
||||
![BoardType.SLIMEVR, BoardType.SLIMEVR_V1_2].includes(
|
||||
device.hardwareInfo.officialBoardType
|
||||
)
|
||||
) {
|
||||
return canUpdate ? 'unavailable' : 'updated';
|
||||
}
|
||||
|
||||
if (
|
||||
canUpdate &&
|
||||
device.hardwareStatus?.batteryPctEstimate != null &&
|
||||
|
||||
@@ -27,13 +27,16 @@ export const BODY_PARTS_GROUPS: Record<MountingResetGroup, BodyPart[]> = {
|
||||
fingers: FINGER_BODY_PARTS,
|
||||
};
|
||||
|
||||
export function useReset(options: UseResetOptions, onReseted?: () => void) {
|
||||
export function useReset(
|
||||
options: UseResetOptions,
|
||||
onReseted?: () => void,
|
||||
onFailed?: () => void
|
||||
) {
|
||||
if (options.type === ResetType.Mounting && !options.group) options.group = 'default';
|
||||
|
||||
const serverGuards = useAtomValue(serverGuardsAtom);
|
||||
const { currentLocales } = useLocaleConfig();
|
||||
const { sendRPCPacket, useRPCPacket } = useWebsocketAPI();
|
||||
|
||||
const finishedTimeoutRef = useRef<NodeJS.Timeout>();
|
||||
const [status, setStatus] = useState<ResetBtnStatus>('idle');
|
||||
const [progress, setProgress] = useState(0);
|
||||
@@ -62,6 +65,7 @@ export function useReset(options: UseResetOptions, onReseted?: () => void) {
|
||||
|
||||
const onResetCanceled = () => {
|
||||
if (status !== 'finished') setStatus('idle');
|
||||
if (onFailed) onFailed();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -92,8 +92,16 @@ export type Steps = {
|
||||
visibleSteps: TrackingChecklistStep[];
|
||||
ignoredSteps: TrackingChecklistStepId[];
|
||||
};
|
||||
|
||||
const filterActive =
|
||||
(ignoredSteps: TrackingChecklistStepId[]) => (step: TrackingChecklistStepT) =>
|
||||
!ignoredSteps.includes(step.id) && step.enabled;
|
||||
|
||||
export function provideTrackingChecklist() {
|
||||
const { sendRPCPacket, useRPCPacket } = useWebsocketAPI();
|
||||
const [sessionIgnoredSteps, setSessionIgnoredSteps] = useState<
|
||||
TrackingChecklistStepId[]
|
||||
>([]);
|
||||
const [steps, setSteps] = useState<Steps>({
|
||||
steps: [],
|
||||
visibleSteps: [],
|
||||
@@ -104,7 +112,7 @@ export function provideTrackingChecklist() {
|
||||
RpcMessage.TrackingChecklistResponse,
|
||||
(data: TrackingChecklistResponseT) => {
|
||||
const activeSteps = data.steps.filter(
|
||||
(step) => !data.ignoredSteps.includes(step.id) && step.enabled
|
||||
filterActive([...data.ignoredSteps, ...sessionIgnoredSteps])
|
||||
);
|
||||
setSteps({
|
||||
steps: data.steps,
|
||||
@@ -161,26 +169,48 @@ export function provideTrackingChecklist() {
|
||||
[steps]
|
||||
);
|
||||
|
||||
const ignoreStep = (step: TrackingChecklistStepId, ignore: boolean) => {
|
||||
const res = new IgnoreTrackingChecklistStepRequestT();
|
||||
res.stepId = step;
|
||||
res.ignore = ignore;
|
||||
sendRPCPacket(RpcMessage.IgnoreTrackingChecklistStepRequest, res);
|
||||
Sentry.metrics.count(ignore ? 'mute_checklist_step' : 'unmute_checklist_step', 1, {
|
||||
attributes: { step: TrackingChecklistStepId[step] },
|
||||
const ignoreStep = (
|
||||
step: TrackingChecklistStepId,
|
||||
ignore: boolean,
|
||||
session = true
|
||||
) => {
|
||||
setSessionIgnoredSteps((curr) => {
|
||||
if (ignore && !curr.includes(step)) return [...curr, step];
|
||||
if (!ignore && curr.includes(step)) {
|
||||
curr.splice(curr.indexOf(step), 1);
|
||||
return curr;
|
||||
}
|
||||
return curr;
|
||||
});
|
||||
Sentry.metrics.count(ignore ? 'mute_checklist_step' : 'unmute_checklist_step', 1, {
|
||||
attributes: { step: TrackingChecklistStepId[step], session },
|
||||
});
|
||||
if (session) {
|
||||
// Force refresh of the flightlist when ignoring a step as the filtering
|
||||
// is done only in one place to simplify the data flow
|
||||
sendRPCPacket(
|
||||
RpcMessage.TrackingChecklistRequest,
|
||||
new TrackingChecklistRequestT()
|
||||
);
|
||||
} else {
|
||||
const res = new IgnoreTrackingChecklistStepRequestT();
|
||||
res.stepId = step;
|
||||
res.ignore = ignore;
|
||||
sendRPCPacket(RpcMessage.IgnoreTrackingChecklistStepRequest, res);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
...steps,
|
||||
sessionIgnoredSteps,
|
||||
firstRequired,
|
||||
highlightedTrackers,
|
||||
progress,
|
||||
completion,
|
||||
warnings,
|
||||
ignoreStep,
|
||||
toggle: (step: TrackingChecklistStepId) =>
|
||||
ignoreStep(step, !steps.ignoredSteps.includes(step)),
|
||||
toggleSession: (step: TrackingChecklistStepId) =>
|
||||
ignoreStep(step, !sessionIgnoredSteps.includes(step)),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
63
server/android/proguard-rules.pro
vendored
63
server/android/proguard-rules.pro
vendored
@@ -1,62 +1,29 @@
|
||||
-dontwarn java.awt.Color
|
||||
-dontwarn java.beans.BeanInfo
|
||||
-dontwarn java.beans.IntrospectionException
|
||||
-dontwarn java.beans.Introspector
|
||||
-dontwarn java.beans.PropertyDescriptor
|
||||
-dontwarn java.lang.management.ManagementFactory
|
||||
-dontwarn java.lang.management.RuntimeMXBean
|
||||
-dontwarn org.apache.logging.log4j.Level
|
||||
-dontwarn org.apache.logging.log4j.LogManager
|
||||
-dontwarn org.apache.logging.log4j.Logger
|
||||
-dontwarn org.apache.logging.log4j.message.MessageFactory
|
||||
-dontwarn org.apache.logging.log4j.spi.ExtendedLogger
|
||||
-dontwarn org.apache.logging.log4j.spi.ExtendedLoggerWrapper
|
||||
-dontwarn org.apache.log4j.Level
|
||||
-dontwarn org.apache.log4j.Logger
|
||||
-dontwarn org.apache.log4j.Priority
|
||||
-dontwarn org.conscrypt.BufferAllocator
|
||||
-dontwarn org.conscrypt.Conscrypt
|
||||
-dontwarn org.conscrypt.HandshakeListener
|
||||
-dontwarn org.eclipse.jetty.npn.NextProtoNego$ClientProvider
|
||||
-dontwarn org.eclipse.jetty.npn.NextProtoNego$Provider
|
||||
-dontwarn org.eclipse.jetty.npn.NextProtoNego$ServerProvider
|
||||
-dontwarn org.eclipse.jetty.npn.NextProtoNego
|
||||
-dontwarn org.jetbrains.annotations.Async$Execute
|
||||
-dontwarn org.jetbrains.annotations.Async$Schedule
|
||||
-dontwarn reactor.blockhound.integration.BlockHoundIntegration
|
||||
|
||||
-keep class io.ktor.** { *; }
|
||||
-keep class io.netty.** { *; }
|
||||
-keep class kotlin.reflect.jvm.internal.** { *; }
|
||||
-keep class kotlinx.coroutines.** { *; }
|
||||
-dontwarn kotlinx.atomicfu.**
|
||||
-dontwarn java.beans.**
|
||||
|
||||
-dontwarn io.netty.**
|
||||
-dontwarn com.typesafe.**
|
||||
-dontwarn org.slf4j.**
|
||||
-keep class io.netty.** { *; }
|
||||
|
||||
# Proguard configuration for Jackson 2.x
|
||||
# Jackson 2.x
|
||||
# https://github.com/FasterXML/jackson-docs/wiki/JacksonOnAndroid
|
||||
#-keep class java.beans.** { *; }
|
||||
#-dontwarn java.beans.**
|
||||
#
|
||||
#-keep class com.fasterxml.jackson.** { *; }
|
||||
#-dontwarn com.fasterxml.jackson.databind.**
|
||||
#
|
||||
#-keep class com.github.jonpeterson.jackson.** { *; }
|
||||
#
|
||||
#-keepclassmembers class * {
|
||||
# @com.fasterxml.jackson.annotation.* *;
|
||||
#}
|
||||
-keepattributes *Annotation*,Signature,EnclosingMethod
|
||||
|
||||
# Proguard configuration for SnakeYAML 2.X
|
||||
#-keep class org.yaml.snakeyaml.** { *; }
|
||||
#-dontwarn org.yaml.snakeyaml.**
|
||||
-keep class com.fasterxml.jackson.**
|
||||
-keepclassmembers class * {
|
||||
@com.fasterxml.jackson.annotation.* *;
|
||||
}
|
||||
|
||||
# Jackson 2.x Model Versioning Module
|
||||
-keep class com.github.jonpeterson.jackson.**
|
||||
|
||||
# SnakeYAML 2.X
|
||||
-keep class org.yaml.snakeyaml.**
|
||||
|
||||
# Don't mess with SlimeVR config, the class structure is essential for serialization
|
||||
-keep class dev.slimevr.config.** { *; }
|
||||
|
||||
# Obfuscation is fine but it makes crash logs unreadable, we don't really need it for our app
|
||||
-dontobfuscate
|
||||
|
||||
# Temporary measure to keep config functional, beware Jackson issues if removing!!
|
||||
-dontoptimize
|
||||
|
||||
@@ -8,10 +8,13 @@ import android.content.IntentFilter
|
||||
import android.hardware.usb.UsbDevice
|
||||
import android.hardware.usb.UsbManager
|
||||
import androidx.core.content.ContextCompat
|
||||
import dev.slimevr.VRServer
|
||||
import dev.slimevr.config.config
|
||||
import dev.slimevr.tracking.trackers.Device
|
||||
import dev.slimevr.tracking.trackers.Tracker
|
||||
import dev.slimevr.tracking.trackers.TrackerStatus
|
||||
import dev.slimevr.tracking.trackers.hid.HIDCommon
|
||||
import dev.slimevr.tracking.trackers.hid.HIDCommon.Companion.HID_TRACKER_PID
|
||||
import dev.slimevr.tracking.trackers.hid.HIDCommon.Companion.HID_TRACKER_RECEIVER_PID
|
||||
import dev.slimevr.tracking.trackers.hid.HIDCommon.Companion.HID_TRACKER_RECEIVER_VID
|
||||
import dev.slimevr.tracking.trackers.hid.HIDCommon.Companion.PACKET_SIZE
|
||||
@@ -91,7 +94,7 @@ class AndroidHIDManager(
|
||||
}
|
||||
|
||||
fun checkConfigureDevice(usbDevice: UsbDevice, requestPermission: Boolean = false) {
|
||||
if (usbDevice.vendorId == HID_TRACKER_RECEIVER_VID && usbDevice.productId == HID_TRACKER_RECEIVER_PID) {
|
||||
if (usbDevice.vendorId == HID_TRACKER_RECEIVER_VID && (usbDevice.productId == HID_TRACKER_RECEIVER_PID || usbDevice.productId == HID_TRACKER_PID)) {
|
||||
if (usbManager.hasPermission(usbDevice)) {
|
||||
LogManager.info("[TrackerServer] Already have permission for ${usbDevice.deviceName}")
|
||||
proceedWithDeviceConfiguration(usbDevice)
|
||||
@@ -199,8 +202,9 @@ class AndroidHIDManager(
|
||||
}
|
||||
|
||||
private fun deviceEnumerate(requestPermission: Boolean = false) {
|
||||
val trackersOverHID: Boolean = VRServer.instance.configManager.vrConfig.hidConfig.trackersOverHID
|
||||
val hidDeviceList: MutableList<UsbDevice> = usbManager.deviceList.values.filter {
|
||||
it.vendorId == HID_TRACKER_RECEIVER_VID && it.productId == HID_TRACKER_RECEIVER_PID
|
||||
it.vendorId == HID_TRACKER_RECEIVER_VID && (it.productId == HID_TRACKER_RECEIVER_PID || (trackersOverHID && it.productId == HID_TRACKER_PID))
|
||||
}.toMutableList()
|
||||
synchronized(devicesByHID) {
|
||||
// Work on devicesByHid and add/remove as necessary
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,6 +339,15 @@ public class CurrentVRConfigConverter implements VersionedModelConverter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (version < 15) {
|
||||
ObjectNode checklistNode = (ObjectNode) modelData.get("trackingChecklist");
|
||||
if (checklistNode != null) {
|
||||
ArrayNode ignoredStepsArray = (ArrayNode) checklistNode.get("ignoredStepsIds");
|
||||
if (ignoredStepsArray != null)
|
||||
ignoredStepsArray.removeAll();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogManager.severe("Error during config migration: " + e);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package dev.slimevr.config
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
|
||||
class HIDConfig {
|
||||
var trackersOverHID = false
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ import dev.slimevr.tracking.trackers.Tracker
|
||||
import dev.slimevr.tracking.trackers.TrackerRole
|
||||
|
||||
@JsonVersionedModel(
|
||||
currentVersion = "14",
|
||||
defaultDeserializeToVersion = "14",
|
||||
currentVersion = "15",
|
||||
defaultDeserializeToVersion = "15",
|
||||
toCurrentConverterClass = CurrentVRConfigConverter::class,
|
||||
)
|
||||
class VRConfig {
|
||||
@@ -42,6 +42,8 @@ class VRConfig {
|
||||
|
||||
val stayAlignedConfig = StayAlignedConfig()
|
||||
|
||||
val hidConfig = HIDConfig()
|
||||
|
||||
@JsonDeserialize(using = TrackerConfigMapDeserializer::class)
|
||||
@JsonSerialize(keyUsing = StdKeySerializers.StringKeySerializer::class)
|
||||
private val trackers: MutableMap<String, TrackerConfig> = HashMap()
|
||||
|
||||
@@ -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,403 +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 hardwareIdentifierOffset = fbb.createString(device.getHardwareIdentifier());
|
||||
|
||||
HardwareInfo.startHardwareInfo(fbb);
|
||||
HardwareInfo.addFirmwareVersion(fbb, nameOffset);
|
||||
HardwareInfo.addManufacturer(fbb, manufacturerOffset);
|
||||
HardwareInfo.addHardwareIdentifier(fbb, hardwareIdentifierOffset);
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
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,506 @@
|
||||
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!!)
|
||||
}
|
||||
if (tracker.batteryRemainingRuntime != null) {
|
||||
HardwareStatus.addBatteryRuntimeEstimate(fbb, tracker.batteryRemainingRuntime!!)
|
||||
}
|
||||
|
||||
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,418 +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()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,32 +0,0 @@
|
||||
package dev.slimevr.protocol.rpc.settings
|
||||
|
||||
import com.google.flatbuffers.FlatBufferBuilder
|
||||
import dev.slimevr.config.StayAlignedConfig
|
||||
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,
|
||||
)
|
||||
}
|
||||
@@ -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) {
|
||||
@@ -369,6 +354,12 @@ class RPCSettingsHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) {
|
||||
config.flatRelaxedPose.footAngleInDeg = requestConfig.flatFootAngle()
|
||||
}
|
||||
|
||||
if (req.hidSettings() != null) {
|
||||
val config = api.server.configManager.vrConfig.hidConfig
|
||||
val requestConfig = req.hidSettings()
|
||||
config.trackersOverHID = requestConfig.trackersOverHid()
|
||||
}
|
||||
|
||||
api.server.configManager.saveConfig()
|
||||
}
|
||||
|
||||
@@ -385,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,
|
||||
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)
|
||||
|
||||
@@ -36,7 +36,7 @@ public class ProvisioningHandler implements SerialListener {
|
||||
this.provisioningTickTimer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!isRunning)
|
||||
if (!isRunning || provisioningStatus == ProvisioningStatus.DONE)
|
||||
return;
|
||||
provisioningTick();
|
||||
}
|
||||
|
||||
@@ -30,14 +30,18 @@ abstract class SerialHandler {
|
||||
Pair(0x1A86, 0x7523),
|
||||
// CH341
|
||||
Pair(0x1A86, 0x5523),
|
||||
// CH343
|
||||
Pair(0x1A86, 0x55D3),
|
||||
// CH9102x
|
||||
Pair(0x1A86, 0x55D4),
|
||||
// / Silabs
|
||||
// CP210x
|
||||
Pair(0x10C4, 0xEA60),
|
||||
// / Espressif
|
||||
// ESP32-C3
|
||||
// ESP32-S3 / ESP32-C3 / ESP32-C5 / ESP32-C6 / ESP32-C61 / ESP32-H2 / ESP32-P4
|
||||
Pair(0x303A, 0x1001),
|
||||
// ESP32-S2
|
||||
Pair(0x303A, 0x0002),
|
||||
// / FTDI
|
||||
// FT232BM/L/Q, FT245BM/L/Q
|
||||
// FT232RL/Q, FT245RL/Q
|
||||
|
||||
@@ -13,6 +13,7 @@ open class Device(val magSupport: Boolean = false) {
|
||||
open val id: Int = nextLocalDeviceId.incrementAndGet()
|
||||
open var name: String? = null
|
||||
open var firmwareVersion: String? = null
|
||||
open var firmwareDate: String? = null
|
||||
open var manufacturer: String? = null
|
||||
open val trackers: MutableMap<Int, Tracker> = ConcurrentHashMap()
|
||||
|
||||
|
||||
@@ -112,9 +112,14 @@ class Tracker @JvmOverloads constructor(
|
||||
val trackerFlexHandler: TrackerFlexHandler = TrackerFlexHandler(this)
|
||||
var batteryVoltage: Float? = null
|
||||
var batteryLevel: Float? = null
|
||||
var batteryRemainingRuntime: Long? = null
|
||||
var ping: Int? = null
|
||||
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
|
||||
|
||||
@@ -24,8 +24,6 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
Math.PI.toFloat(),
|
||||
0f,
|
||||
).toQuaternion()
|
||||
private val QuarterPitch = Quaternion.rotationAroundXAxis(FastMath.HALF_PI)
|
||||
|
||||
private var driftAmount = 0f
|
||||
private var averagedDriftQuat = Quaternion.IDENTITY
|
||||
private var rotationSinceReset = Quaternion.IDENTITY
|
||||
@@ -54,6 +52,16 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
|
||||
// Reference adjustment quats
|
||||
|
||||
/**
|
||||
* Gyro fix is set by full reset. This sets the current y rotation to 0, correcting
|
||||
* for initial yaw rotation and the rotation incurred by mounting orientation. This
|
||||
* is a local offset in rotation and does not affect the axes of rotation.
|
||||
*
|
||||
* This rotation is only used to compute [attachmentFix], otherwise [yawFix] would
|
||||
* correct for the same rotation.
|
||||
*/
|
||||
private var gyroFix = Quaternion.IDENTITY
|
||||
|
||||
/**
|
||||
* Attachment fix is set by full reset. This sets the current x and z rotations to
|
||||
* 0, correcting for initial pitch and roll rotation. This is a global offset in
|
||||
@@ -180,9 +188,12 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
/**
|
||||
* Get the reference adjusted accel.
|
||||
*/
|
||||
// All IMU axis corrections are inverse to undo `adjustToReference` after local yaw offsets are added
|
||||
// Order is VERY important here! Please be extremely careful! >~>
|
||||
fun getReferenceAdjustedAccel(rawRot: Quaternion, accel: Vector3): Vector3 = (adjustToReference(rawRot) * (attachmentFix * mountingOrientation * mountRotFix * tposeDownFix).inv()).sandwich(accel)
|
||||
// TODO: Make this actually adjusted to the corrected IMU heading. The current
|
||||
// implementation for heading correction doesn't appear to be correct and may simply
|
||||
// make acceleration worse, so I'm just leaving this until we work that out. The
|
||||
// output of this will be world space, but with an unknown offset to heading (yaw).
|
||||
// - Butterscotch
|
||||
fun getReferenceAdjustedAccel(rawRot: Quaternion, accel: Vector3): Vector3 = rawRot.sandwich(accel)
|
||||
|
||||
/**
|
||||
* Converts raw or filtered rotation into reference- and
|
||||
@@ -191,17 +202,22 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
*/
|
||||
private fun adjustToReference(rotation: Quaternion): Quaternion {
|
||||
var rot = rotation
|
||||
// Correct for global pitch/roll offset
|
||||
rot *= attachmentFix
|
||||
// Correct for global yaw offset without affecting local yaw so we can change this
|
||||
// later without invalidating local yaw offset corrections
|
||||
// Align heading axis with bone space
|
||||
if (!tracker.isHmd || tracker.trackerPosition != TrackerPosition.HEAD) {
|
||||
rot = mountingOrientation.inv() * rot * mountingOrientation
|
||||
rot *= mountingOrientation
|
||||
}
|
||||
rot = mountRotFix.inv() * rot * mountRotFix
|
||||
// T-pose global correction
|
||||
// Heading correction assuming manual orientation is correct
|
||||
rot = gyroFix * rot
|
||||
// Align attitude axes with bone space
|
||||
rot *= attachmentFix
|
||||
// Secondary heading axis alignment with bone space for automatic mounting
|
||||
// Note: Applying an inverse amount of heading correction corresponding to the
|
||||
// axis alignment quaternion will leave the correction to another variable
|
||||
rot = mountRotFix.inv() * (rot * mountRotFix)
|
||||
// More attitude axes alignment specifically for the t-pose configuration, this
|
||||
// probably shouldn't be a separate variable from attachmentFix?
|
||||
rot *= tposeDownFix
|
||||
// Align local yaw with reference
|
||||
// More heading correction
|
||||
rot = yawFix * rot
|
||||
rot = constraintFix * rot
|
||||
return rot
|
||||
@@ -211,6 +227,8 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
* Converts raw or filtered rotation into zero-reference-adjusted by
|
||||
* applying quaternions produced after full reset and yaw reset only
|
||||
*/
|
||||
// This is essentially just adjustToReference but aligning to quaternion identity
|
||||
// rather than to the bone.
|
||||
private fun adjustToIdentity(rotation: Quaternion): Quaternion {
|
||||
var rot = rotation
|
||||
rot = gyroFixNoMounting * rot
|
||||
@@ -265,23 +283,15 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
lastResetQuaternion = oldRot
|
||||
|
||||
// Adjust raw rotation to mountingOrientation
|
||||
val rotation = tracker.getRawRotation()
|
||||
val mountingAdjustedRotation = tracker.getRawRotation() * mountingOrientation
|
||||
|
||||
// Gyrofix
|
||||
val gyroFix = if (tracker.allowMounting || (tracker.trackerPosition == TrackerPosition.HEAD && !tracker.isHmd)) {
|
||||
if (tracker.isComputed) {
|
||||
fixGyroscope(rotation)
|
||||
if (tracker.allowMounting || (tracker.trackerPosition == TrackerPosition.HEAD && !tracker.isHmd)) {
|
||||
gyroFix = if (tracker.isComputed) {
|
||||
fixGyroscope(tracker.getRawRotation())
|
||||
} else {
|
||||
if (tracker.trackerPosition.isFoot()) {
|
||||
// Feet are rotated by 90 deg pitch, this means we're relying on IMU rotation
|
||||
// to be set correctly here.
|
||||
fixGyroscope(rotation * tposeDownFix * QuarterPitch)
|
||||
} else {
|
||||
fixGyroscope(rotation * tposeDownFix)
|
||||
}
|
||||
fixGyroscope(mountingAdjustedRotation * tposeDownFix)
|
||||
}
|
||||
} else {
|
||||
Quaternion.IDENTITY
|
||||
}
|
||||
|
||||
// Mounting for computed trackers
|
||||
@@ -296,7 +306,7 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
if (resetHmdPitch) {
|
||||
// Reset the HMD's pitch if it's assigned to head and resetHmdPitch is true
|
||||
// Get rotation without yaw (make sure to use the raw rotation directly!)
|
||||
val rotBuf = getYawQuaternion(rotation).inv() * rotation
|
||||
val rotBuf = getYawQuaternion(tracker.getRawRotation()).inv() * tracker.getRawRotation()
|
||||
// Isolate pitch
|
||||
Quaternion(rotBuf.w, -rotBuf.x, 0f, 0f).unit()
|
||||
} else {
|
||||
@@ -304,7 +314,7 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
Quaternion.IDENTITY
|
||||
}
|
||||
} else {
|
||||
(gyroFix * rotation).inv()
|
||||
fixAttachment(mountingAdjustedRotation)
|
||||
}
|
||||
|
||||
// Rotate attachmentFix by 180 degrees as a workaround for t-pose (down)
|
||||
@@ -316,7 +326,7 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
|
||||
// Don't adjust yaw if head and computed
|
||||
if (tracker.trackerPosition != TrackerPosition.HEAD || !tracker.isComputed) {
|
||||
yawFix = gyroFix * reference.project(Vector3.POS_Y).unit()
|
||||
yawFix = fixYaw(mountingAdjustedRotation, reference)
|
||||
tracker.yawResetSmoothing.reset()
|
||||
}
|
||||
|
||||
@@ -360,7 +370,7 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
lastResetQuaternion = oldRot
|
||||
|
||||
val yawFixOld = yawFix
|
||||
yawFix = fixYaw(tracker.getRawRotation(), reference)
|
||||
yawFix = fixYaw(tracker.getRawRotation() * mountingOrientation, reference)
|
||||
tracker.yawResetSmoothing.reset()
|
||||
|
||||
makeIdentityAdjustmentQuatsYaw()
|
||||
@@ -399,9 +409,9 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
constraintFix = Quaternion.IDENTITY
|
||||
|
||||
// Get the current calibrated rotation
|
||||
var rotBuf = adjustToDrift(tracker.getRawRotation())
|
||||
var rotBuf = adjustToDrift(tracker.getRawRotation() * mountingOrientation)
|
||||
rotBuf = gyroFix * rotBuf
|
||||
rotBuf *= attachmentFix
|
||||
rotBuf = mountingOrientation.inv() * rotBuf * mountingOrientation
|
||||
rotBuf = yawFix * rotBuf
|
||||
|
||||
// Adjust buffer to reference
|
||||
@@ -457,22 +467,14 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
mountRotFix = Quaternion.IDENTITY
|
||||
}
|
||||
|
||||
// EulerOrder.YXZ is actually better for gyroscope fix, as it can get yaw at any roll.
|
||||
// Consequentially, instead of the roll being limited, the pitch is limited to
|
||||
// 90 degrees from the yaw plane. This means trackers may be mounted upside down
|
||||
// or with incorrectly configured IMU rotation, but we will need to compensate for
|
||||
// the pitch.
|
||||
private fun fixGyroscope(sensorRotation: Quaternion): Quaternion = getYawQuaternion(sensorRotation, EulerOrder.YXZ).inv()
|
||||
private fun fixGyroscope(sensorRotation: Quaternion): Quaternion = getYawQuaternion(sensorRotation).inv()
|
||||
|
||||
private fun fixAttachment(sensorRotation: Quaternion): Quaternion = (gyroFix * sensorRotation).inv()
|
||||
|
||||
private fun fixYaw(sensorRotation: Quaternion, reference: Quaternion): Quaternion {
|
||||
var rot = sensorRotation * attachmentFix
|
||||
// We need to fix the global yaw offset for the euler yaw calculation
|
||||
if (!tracker.isHmd || tracker.trackerPosition != TrackerPosition.HEAD) {
|
||||
rot = mountingOrientation.inv() * rot * mountingOrientation
|
||||
}
|
||||
rot = mountRotFix.inv() * rot * mountRotFix
|
||||
// TODO: Get diff from ref to rot, use euler angle (YZX) yaw as output.
|
||||
// This prevents pitch and roll from affecting the alignment.
|
||||
var rot = gyroFix * sensorRotation
|
||||
rot *= attachmentFix
|
||||
rot = mountRotFix.inv() * (rot * mountRotFix)
|
||||
rot = getYawQuaternion(rot)
|
||||
return rot.inv() * reference.project(Vector3.POS_Y).unit()
|
||||
}
|
||||
@@ -483,7 +485,7 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
// In both cases, the isolated yaw value changes
|
||||
// with the tracker's roll when pointing forward.
|
||||
// calling twinNearest() makes sure this rotation has the wanted polarity (+-).
|
||||
private fun getYawQuaternion(rot: Quaternion, order: EulerOrder = EulerOrder.YZX): Quaternion = EulerAngles(order, 0f, rot.toEulerAngles(order).y, 0f).toQuaternion().twinNearest(rot)
|
||||
private fun getYawQuaternion(rot: Quaternion): Quaternion = EulerAngles(EulerOrder.YZX, 0f, rot.toEulerAngles(EulerOrder.YZX).y, 0f).toQuaternion().twinNearest(rot)
|
||||
|
||||
private fun makeIdentityAdjustmentQuatsFull() {
|
||||
val sensorRotation = tracker.getRawRotation()
|
||||
|
||||
@@ -27,6 +27,7 @@ class HIDCommon {
|
||||
companion object {
|
||||
const val HID_TRACKER_RECEIVER_VID = 0x1209
|
||||
const val HID_TRACKER_RECEIVER_PID = 0x7690
|
||||
const val HID_TRACKER_PID = 0x7692
|
||||
|
||||
const val PACKET_SIZE = 16
|
||||
|
||||
@@ -138,11 +139,13 @@ class HIDCommon {
|
||||
}
|
||||
|
||||
// Packet data
|
||||
var runtime: Long? = null
|
||||
var batt: Int? = null
|
||||
var batt_v: Int? = null
|
||||
var temp: Int? = null
|
||||
var brd_id: Int? = null
|
||||
var mcu_id: Int? = null
|
||||
var button: Int? = null
|
||||
// var imu_id: Int? = null
|
||||
// var mag_id: Int? = null
|
||||
var fw_date: Int? = null
|
||||
@@ -152,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) {
|
||||
@@ -206,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()
|
||||
}
|
||||
|
||||
@@ -220,11 +231,42 @@ class HIDCommon {
|
||||
}
|
||||
}
|
||||
|
||||
5 -> { // runtime
|
||||
// ulong as little endian
|
||||
runtime = (dataReceived[i + 9].toUByte().toLong() shl 56) or (dataReceived[i + 8].toUByte().toLong() shl 48) or (dataReceived[i + 7].toUByte().toLong() shl 40) or (dataReceived[i + 6].toUByte().toLong() shl 32) or (dataReceived[i + 5].toUByte().toLong() shl 24) or (dataReceived[i + 4].toUByte().toLong() shl 16) or (dataReceived[i + 3].toUByte().toLong() shl 8) or dataReceived[i + 2].toUByte().toLong()
|
||||
}
|
||||
|
||||
6 -> { // data
|
||||
button = dataReceived[i + 2].toUByte().toInt()
|
||||
rssi = dataReceived[i + 15].toUByte().toInt()
|
||||
}
|
||||
|
||||
7 -> { // reduced precision quat and accel with data
|
||||
button = dataReceived[i + 2].toUByte().toInt()
|
||||
// quaternion is quantized as exponential map
|
||||
// X = 10 bits, Y/Z = 11 bits
|
||||
val buffer = ByteBuffer.wrap(dataReceived, i + 5, 4)
|
||||
buffer.order(java.nio.ByteOrder.LITTLE_ENDIAN)
|
||||
val q_buf = buffer.getInt().toUInt()
|
||||
q[0] = (q_buf and 1023u).toInt()
|
||||
q[1] = (q_buf shr 10 and 2047u).toInt()
|
||||
q[2] = (q_buf shr 21 and 2047u).toInt()
|
||||
for (j in 0..2) { // accel received as fixed 7, in m/s^2
|
||||
// Q7 as short little endian
|
||||
a[j] = dataReceived[i + 9 + j * 2 + 1].toInt() shl 8 or dataReceived[i + 9 + j * 2].toUByte().toInt()
|
||||
}
|
||||
rssi = dataReceived[i + 15].toUByte().toInt()
|
||||
}
|
||||
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
|
||||
// Assign data
|
||||
if (runtime != null && runtime >= 0) {
|
||||
tracker.batteryRemainingRuntime = runtime
|
||||
}
|
||||
// -1: Not known (e.g. not yet calculated after wake up, reusing known value is okay), 0: N/A (e.g. charging)
|
||||
if (batt != null) {
|
||||
tracker.batteryLevel = if (batt == 128) 1f else (batt and 127).toFloat()
|
||||
}
|
||||
@@ -248,12 +290,23 @@ class HIDCommon {
|
||||
device.mcuType = mcuType!!
|
||||
}
|
||||
}
|
||||
if (fw_date != null && fw_major != null && fw_minor != null && fw_patch != null) {
|
||||
if (button != null) {
|
||||
if (tracker.button == null) {
|
||||
tracker.button = 0
|
||||
}
|
||||
if (button != tracker.button) {
|
||||
button = button and tracker.button!!.inv()
|
||||
// Nothing to do now..
|
||||
}
|
||||
}
|
||||
if (fw_date != null) {
|
||||
val firmwareYear = 2020 + (fw_date shr 9 and 127)
|
||||
val firmwareMonth = fw_date shr 5 and 15
|
||||
val firmwareDay = fw_date and 31
|
||||
val firmwareDate = String.format("%04d-%02d-%02d", firmwareYear, firmwareMonth, firmwareDay)
|
||||
device.firmwareVersion = "$fw_major.$fw_minor.$fw_patch (Build $firmwareDate)"
|
||||
device.firmwareDate = String.format("%04d-%02d-%02d", firmwareYear, firmwareMonth, firmwareDay)
|
||||
}
|
||||
if (fw_major != null && fw_minor != null && fw_patch != null) {
|
||||
device.firmwareVersion = "$fw_major.$fw_minor.$fw_patch"
|
||||
}
|
||||
if (svr_status != null) {
|
||||
val status = TrackerStatus.getById(svr_status)
|
||||
@@ -264,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) {
|
||||
@@ -274,7 +332,7 @@ class HIDCommon {
|
||||
rot = AXES_OFFSET.times(scaleRot).times(rot) // no division
|
||||
tracker.setRotation(rot)
|
||||
}
|
||||
if (packetType == 2) {
|
||||
if (packetType == 2 || packetType == 7) {
|
||||
val v = floatArrayOf(q[0].toFloat(), q[1].toFloat(), q[2].toFloat()) // used q array for quantized data
|
||||
v[0] /= (1 shl 10).toFloat()
|
||||
v[1] /= (1 shl 11).toFloat()
|
||||
@@ -293,7 +351,7 @@ class HIDCommon {
|
||||
rot = AXES_OFFSET.times(rot) // no division
|
||||
tracker.setRotation(rot)
|
||||
}
|
||||
if (packetType == 1 || packetType == 2) {
|
||||
if (packetType == 1 || packetType == 2 || packetType == 7) {
|
||||
// Acceleration is in local device frame
|
||||
// On flat surface / face up:
|
||||
// Right side of the device is +X
|
||||
@@ -317,7 +375,7 @@ class HIDCommon {
|
||||
val magnetometer = Vector3(m[0].toFloat(), m[1].toFloat(), m[2].toFloat()).times(scaleMag) // no division
|
||||
tracker.setMagVector(magnetometer)
|
||||
}
|
||||
if (packetType == 1 || packetType == 2 || packetType == 4) {
|
||||
if (packetType == 1 || packetType == 2 || packetType == 4 || packetType == 7) {
|
||||
tracker.dataTick() // only data tick if there is rotation data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,9 @@ enum class BoardType(val id: UInt) {
|
||||
GESTURES(21u),
|
||||
SLIMEVR_V1_2(22u),
|
||||
ESP32S3_SUPERMINI(23u),
|
||||
GENERIC_NRF(24u),
|
||||
SLIMEVR_BUTTERFLY_DEV(25u),
|
||||
SLIMEVR_BUTTERFLY(26u),
|
||||
DEV_RESERVED(250u),
|
||||
;
|
||||
|
||||
@@ -88,6 +91,9 @@ enum class BoardType(val id: UInt) {
|
||||
GLOVE_IMU_SLIMEVR_DEV -> "SlimeVR Dev IMU Glove"
|
||||
SLIMEVR_V1_2 -> "SlimeVR v1.2"
|
||||
ESP32S3_SUPERMINI -> "ESP32-S3 SuperMini"
|
||||
GENERIC_NRF -> "Generic nRF"
|
||||
SLIMEVR_BUTTERFLY_DEV -> "SlimeVR Dev Butterfly"
|
||||
SLIMEVR_BUTTERFLY -> "SlimeVR Butterfly"
|
||||
DEV_RESERVED -> "Prototype"
|
||||
}
|
||||
|
||||
@@ -109,6 +115,8 @@ enum class MCUType(val id: UInt) {
|
||||
ESP32_C3(6u),
|
||||
MOCOPI(7u),
|
||||
HARITORA(8u),
|
||||
NRF52(9u),
|
||||
NRF54L(10u),
|
||||
DEV_RESERVED(250u),
|
||||
;
|
||||
|
||||
|
||||
@@ -397,7 +397,9 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
Util.close(socket)
|
||||
if (::socket.isInitialized) {
|
||||
Util.close(socket)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,9 +417,13 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
|
||||
if (tracker == null) return
|
||||
tracker.setRotation(rot)
|
||||
if (packet is UDPPacket23RotationAndAcceleration) {
|
||||
// If sensorOffset was applied to accel correctly, the axes will already
|
||||
// be correct for SlimeVR
|
||||
tracker.setAcceleration(SENSOR_OFFSET_CORRECTION.sandwich(packet.acceleration))
|
||||
// sensorOffset is applied correctly since protocol 22
|
||||
// See: https://github.com/SlimeVR/SlimeVR-Tracker-ESP/pull/480
|
||||
if (connection.protocolVersion >= 22) {
|
||||
tracker.setAcceleration(packet.acceleration)
|
||||
} else {
|
||||
tracker.setAcceleration(SENSOR_OFFSET_CORRECTION.sandwich(packet.acceleration))
|
||||
}
|
||||
}
|
||||
tracker.dataTick()
|
||||
}
|
||||
@@ -449,9 +455,13 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker
|
||||
is UDPPacket4Acceleration -> {
|
||||
tracker = connection?.getTracker(packet.sensorId)
|
||||
if (tracker == null) return
|
||||
// If sensorOffset was applied to accel correctly, the axes will already
|
||||
// be correct for SlimeVR
|
||||
tracker.setAcceleration(SENSOR_OFFSET_CORRECTION.sandwich(packet.acceleration))
|
||||
// sensorOffset is applied correctly since protocol 22
|
||||
// See: https://github.com/SlimeVR/SlimeVR-Tracker-ESP/pull/480
|
||||
if (connection.protocolVersion >= 22) {
|
||||
tracker.setAcceleration(packet.acceleration)
|
||||
} else {
|
||||
tracker.setAcceleration(SENSOR_OFFSET_CORRECTION.sandwich(packet.acceleration))
|
||||
}
|
||||
}
|
||||
|
||||
is UDPPacket10PingPong -> {
|
||||
|
||||
@@ -11,6 +11,7 @@ import dev.slimevr.tracking.trackers.Tracker
|
||||
import dev.slimevr.tracking.trackers.TrackerStatus
|
||||
import dev.slimevr.tracking.trackers.TrackerUtils
|
||||
import dev.slimevr.tracking.trackers.udp.TrackerDataType
|
||||
import io.github.axisangles.ktmath.Quaternion
|
||||
import solarxr_protocol.datatypes.DeviceIdT
|
||||
import solarxr_protocol.datatypes.TrackerIdT
|
||||
import solarxr_protocol.rpc.*
|
||||
@@ -199,7 +200,8 @@ class TrackingChecklistManager(private val vrServer: VRServer) : VRCConfigListen
|
||||
}
|
||||
// We ask for a full reset if you need to do mounting calibration but cant because you haven't done full reset in a while
|
||||
// or if you have trackers that need reset after re-assigning
|
||||
val needFullReset = (!resetMountingCompleted && !vrServer.serverGuards.canDoMounting) || trackerRequireReset.isNotEmpty()
|
||||
val usingSavedCalibration = vrServer.configManager.vrConfig.resetsConfig.saveMountingReset && imuTrackers.all { it.resetsHandler.mountRotFix != Quaternion.IDENTITY }
|
||||
val needFullReset = (vrServer.configManager.vrConfig.resetsConfig.lastMountingMethod == MountingMethods.AUTOMATIC && !usingSavedCalibration && !resetMountingCompleted && !vrServer.serverGuards.canDoMounting) || trackerRequireReset.isNotEmpty()
|
||||
updateValidity(TrackingChecklistStepId.FULL_RESET, !needFullReset) {
|
||||
it.enabled = imuTrackers.isNotEmpty()
|
||||
if (trackerRequireReset.isNotEmpty()) {
|
||||
@@ -216,7 +218,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;
|
||||
|
||||
@@ -3,6 +3,7 @@ package dev.slimevr.websocketapi;
|
||||
import dev.slimevr.protocol.ConnectionContext;
|
||||
import dev.slimevr.protocol.GenericConnection;
|
||||
import org.java_websocket.WebSocket;
|
||||
import org.java_websocket.exceptions.WebsocketNotConnectedException;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.UUID;
|
||||
@@ -27,8 +28,13 @@ public class WebsocketConnection implements GenericConnection {
|
||||
|
||||
@Override
|
||||
public void send(ByteBuffer bytes) {
|
||||
if (this.conn.isOpen())
|
||||
this.conn.send(bytes.slice());
|
||||
if (this.conn.isOpen()) {
|
||||
try {
|
||||
this.conn.send(bytes.slice());
|
||||
} catch (WebsocketNotConnectedException ignored) {
|
||||
// Race condition if it closes between our check and sending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -93,13 +93,13 @@ class SkeletonResetTests {
|
||||
TrackerPosition.HIP,
|
||||
TrackerPosition.LEFT_LOWER_LEG,
|
||||
TrackerPosition.RIGHT_LOWER_LEG,
|
||||
-> mountRot
|
||||
-> mountRot * Quaternion.SLIMEVR.FRONT
|
||||
|
||||
TrackerPosition.LEFT_UPPER_LEG,
|
||||
TrackerPosition.RIGHT_UPPER_LEG,
|
||||
-> mountRot * Quaternion.SLIMEVR.FRONT
|
||||
-> mountRot
|
||||
|
||||
else -> mountRot * Quaternion.SLIMEVR.FRONT
|
||||
else -> mountRot
|
||||
}
|
||||
val actualMounting = tracker.resetsHandler.mountRotFix
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import dev.slimevr.desktop.platform.ProtobufMessages.*
|
||||
import dev.slimevr.tracking.trackers.Tracker
|
||||
import dev.slimevr.tracking.trackers.TrackerStatus
|
||||
import dev.slimevr.tracking.trackers.TrackerStatus.Companion.getById
|
||||
import dev.slimevr.tracking.trackers.TrackerUtils
|
||||
import dev.slimevr.util.ann.VRServerThread
|
||||
import io.eiren.util.ann.Synchronize
|
||||
import io.eiren.util.ann.ThreadSafe
|
||||
@@ -218,6 +219,11 @@ abstract class ProtobufBridge(@JvmField protected val bridgeName: String) : ISte
|
||||
|
||||
"mounting_reset" -> instance.resetTrackersMounting(resetSourceName)
|
||||
|
||||
"feet_mounting_reset" -> instance.resetTrackersMounting(
|
||||
resetSourceName,
|
||||
TrackerUtils.feetsBodyParts,
|
||||
)
|
||||
|
||||
"pause_tracking" ->
|
||||
instance
|
||||
.togglePauseTracking(resetSourceName)
|
||||
|
||||
@@ -3558,6 +3558,20 @@ public final class ProtobufMessages {
|
||||
* @return The trackerRole.
|
||||
*/
|
||||
int getTrackerRole();
|
||||
|
||||
/**
|
||||
* <code>string manufacturer = 5;</code>
|
||||
*
|
||||
* @return The manufacturer.
|
||||
*/
|
||||
java.lang.String getManufacturer();
|
||||
|
||||
/**
|
||||
* <code>string manufacturer = 5;</code>
|
||||
*
|
||||
* @return The bytes for manufacturer.
|
||||
*/
|
||||
com.google.protobuf.ByteString getManufacturerBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3588,6 +3602,7 @@ public final class ProtobufMessages {
|
||||
private TrackerAdded() {
|
||||
trackerSerial_ = "";
|
||||
trackerName_ = "";
|
||||
manufacturer_ = "";
|
||||
}
|
||||
|
||||
public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() {
|
||||
@@ -3713,6 +3728,48 @@ public final class ProtobufMessages {
|
||||
return trackerRole_;
|
||||
}
|
||||
|
||||
public static final int MANUFACTURER_FIELD_NUMBER = 5;
|
||||
@SuppressWarnings("serial")
|
||||
private volatile java.lang.Object manufacturer_ = "";
|
||||
|
||||
/**
|
||||
* <code>string manufacturer = 5;</code>
|
||||
*
|
||||
* @return The manufacturer.
|
||||
*/
|
||||
@java.lang.Override
|
||||
public java.lang.String getManufacturer() {
|
||||
java.lang.Object ref = manufacturer_;
|
||||
if (ref instanceof java.lang.String) {
|
||||
return (java.lang.String) ref;
|
||||
} else {
|
||||
com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref;
|
||||
java.lang.String s = bs.toStringUtf8();
|
||||
manufacturer_ = s;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>string manufacturer = 5;</code>
|
||||
*
|
||||
* @return The bytes for manufacturer.
|
||||
*/
|
||||
@java.lang.Override
|
||||
public com.google.protobuf.ByteString getManufacturerBytes() {
|
||||
java.lang.Object ref = manufacturer_;
|
||||
if (ref instanceof java.lang.String) {
|
||||
com.google.protobuf.ByteString b = com.google.protobuf.ByteString
|
||||
.copyFromUtf8(
|
||||
(java.lang.String) ref
|
||||
);
|
||||
manufacturer_ = b;
|
||||
return b;
|
||||
} else {
|
||||
return (com.google.protobuf.ByteString) ref;
|
||||
}
|
||||
}
|
||||
|
||||
private byte memoizedIsInitialized = -1;
|
||||
|
||||
@java.lang.Override
|
||||
@@ -3742,6 +3799,9 @@ public final class ProtobufMessages {
|
||||
if (trackerRole_ != 0) {
|
||||
output.writeInt32(4, trackerRole_);
|
||||
}
|
||||
if (!com.google.protobuf.GeneratedMessage.isStringEmpty(manufacturer_)) {
|
||||
com.google.protobuf.GeneratedMessage.writeString(output, 5, manufacturer_);
|
||||
}
|
||||
getUnknownFields().writeTo(output);
|
||||
}
|
||||
|
||||
@@ -3766,6 +3826,9 @@ public final class ProtobufMessages {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeInt32Size(4, trackerRole_);
|
||||
}
|
||||
if (!com.google.protobuf.GeneratedMessage.isStringEmpty(manufacturer_)) {
|
||||
size += com.google.protobuf.GeneratedMessage.computeStringSize(5, manufacturer_);
|
||||
}
|
||||
size += getUnknownFields().getSerializedSize();
|
||||
memoizedSize = size;
|
||||
return size;
|
||||
@@ -3801,6 +3864,11 @@ public final class ProtobufMessages {
|
||||
!= other.getTrackerRole()
|
||||
)
|
||||
return false;
|
||||
if (
|
||||
!getManufacturer()
|
||||
.equals(other.getManufacturer())
|
||||
)
|
||||
return false;
|
||||
if (!getUnknownFields().equals(other.getUnknownFields()))
|
||||
return false;
|
||||
return true;
|
||||
@@ -3821,6 +3889,8 @@ public final class ProtobufMessages {
|
||||
hash = (53 * hash) + getTrackerName().hashCode();
|
||||
hash = (37 * hash) + TRACKER_ROLE_FIELD_NUMBER;
|
||||
hash = (53 * hash) + getTrackerRole();
|
||||
hash = (37 * hash) + MANUFACTURER_FIELD_NUMBER;
|
||||
hash = (53 * hash) + getManufacturer().hashCode();
|
||||
hash = (29 * hash) + getUnknownFields().hashCode();
|
||||
memoizedHashCode = hash;
|
||||
return hash;
|
||||
@@ -3993,6 +4063,7 @@ public final class ProtobufMessages {
|
||||
trackerSerial_ = "";
|
||||
trackerName_ = "";
|
||||
trackerRole_ = 0;
|
||||
manufacturer_ = "";
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -4044,6 +4115,9 @@ public final class ProtobufMessages {
|
||||
if (((from_bitField0_ & 0x00000008) != 0)) {
|
||||
result.trackerRole_ = trackerRole_;
|
||||
}
|
||||
if (((from_bitField0_ & 0x00000010) != 0)) {
|
||||
result.manufacturer_ = manufacturer_;
|
||||
}
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
@@ -4083,6 +4157,11 @@ public final class ProtobufMessages {
|
||||
if (other.getTrackerRole() != 0) {
|
||||
setTrackerRole(other.getTrackerRole());
|
||||
}
|
||||
if (!other.getManufacturer().isEmpty()) {
|
||||
manufacturer_ = other.manufacturer_;
|
||||
bitField0_ |= 0x00000010;
|
||||
onChanged();
|
||||
}
|
||||
this.mergeUnknownFields(other.getUnknownFields());
|
||||
onChanged();
|
||||
return this;
|
||||
@@ -4130,6 +4209,11 @@ public final class ProtobufMessages {
|
||||
bitField0_ |= 0x00000008;
|
||||
break;
|
||||
} // case 32
|
||||
case 42: {
|
||||
manufacturer_ = input.readStringRequireUtf8();
|
||||
bitField0_ |= 0x00000010;
|
||||
break;
|
||||
} // case 42
|
||||
default: {
|
||||
if (!super.parseUnknownField(input, extensionRegistry, tag)) {
|
||||
done = true; // was an endgroup tag
|
||||
@@ -4398,6 +4482,93 @@ public final class ProtobufMessages {
|
||||
return this;
|
||||
}
|
||||
|
||||
private java.lang.Object manufacturer_ = "";
|
||||
|
||||
/**
|
||||
* <code>string manufacturer = 5;</code>
|
||||
*
|
||||
* @return The manufacturer.
|
||||
*/
|
||||
public java.lang.String getManufacturer() {
|
||||
java.lang.Object ref = manufacturer_;
|
||||
if (!(ref instanceof java.lang.String)) {
|
||||
com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref;
|
||||
java.lang.String s = bs.toStringUtf8();
|
||||
manufacturer_ = s;
|
||||
return s;
|
||||
} else {
|
||||
return (java.lang.String) ref;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>string manufacturer = 5;</code>
|
||||
*
|
||||
* @return The bytes for manufacturer.
|
||||
*/
|
||||
public com.google.protobuf.ByteString getManufacturerBytes() {
|
||||
java.lang.Object ref = manufacturer_;
|
||||
if (ref instanceof String) {
|
||||
com.google.protobuf.ByteString b = com.google.protobuf.ByteString
|
||||
.copyFromUtf8(
|
||||
(java.lang.String) ref
|
||||
);
|
||||
manufacturer_ = b;
|
||||
return b;
|
||||
} else {
|
||||
return (com.google.protobuf.ByteString) ref;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>string manufacturer = 5;</code>
|
||||
*
|
||||
* @param value The manufacturer to set.
|
||||
* @return This builder for chaining.
|
||||
*/
|
||||
public Builder setManufacturer(
|
||||
java.lang.String value
|
||||
) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
manufacturer_ = value;
|
||||
bitField0_ |= 0x00000010;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>string manufacturer = 5;</code>
|
||||
*
|
||||
* @return This builder for chaining.
|
||||
*/
|
||||
public Builder clearManufacturer() {
|
||||
manufacturer_ = getDefaultInstance().getManufacturer();
|
||||
bitField0_ = (bitField0_ & ~0x00000010);
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>string manufacturer = 5;</code>
|
||||
*
|
||||
* @param value The bytes for manufacturer to set.
|
||||
* @return This builder for chaining.
|
||||
*/
|
||||
public Builder setManufacturerBytes(
|
||||
com.google.protobuf.ByteString value
|
||||
) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
checkByteStringIsUtf8(value);
|
||||
manufacturer_ = value;
|
||||
bitField0_ |= 0x00000010;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(builder_scope:messages.TrackerAdded)
|
||||
}
|
||||
|
||||
@@ -8872,53 +9043,55 @@ public final class ProtobufMessages {
|
||||
+
|
||||
"ctionArgumentsEntry\0326\n\024ActionArgumentsEn"
|
||||
+
|
||||
"try\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"f\n\014T"
|
||||
"try\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"|\n\014T"
|
||||
+
|
||||
"rackerAdded\022\022\n\ntracker_id\030\001 \001(\005\022\026\n\016track"
|
||||
+
|
||||
"er_serial\030\002 \001(\t\022\024\n\014tracker_name\030\003 \001(\t\022\024\n"
|
||||
+
|
||||
"\014tracker_role\030\004 \001(\005\"\374\002\n\rTrackerStatus\022\022\n"
|
||||
"\014tracker_role\030\004 \001(\005\022\024\n\014manufacturer\030\005 \001("
|
||||
+
|
||||
"\ntracker_id\030\001 \001(\005\022.\n\006status\030\002 \001(\0162\036.mess"
|
||||
"\t\"\374\002\n\rTrackerStatus\022\022\n\ntracker_id\030\001 \001(\005\022"
|
||||
+
|
||||
"ages.TrackerStatus.Status\0221\n\005extra\030\003 \003(\013"
|
||||
".\n\006status\030\002 \001(\0162\036.messages.TrackerStatus"
|
||||
+
|
||||
"2\".messages.TrackerStatus.ExtraEntry\022;\n\n"
|
||||
".Status\0221\n\005extra\030\003 \003(\0132\".messages.Tracke"
|
||||
+
|
||||
"confidence\030\004 \001(\0162\".messages.TrackerStatu"
|
||||
"rStatus.ExtraEntry\022;\n\nconfidence\030\004 \001(\0162\""
|
||||
+
|
||||
"s.ConfidenceH\000\210\001\001\032,\n\nExtraEntry\022\013\n\003key\030\001"
|
||||
".messages.TrackerStatus.ConfidenceH\000\210\001\001\032"
|
||||
+
|
||||
" \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"E\n\006Status\022\020\n\014DIS"
|
||||
",\n\nExtraEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001("
|
||||
+
|
||||
"CONNECTED\020\000\022\006\n\002OK\020\001\022\010\n\004BUSY\020\002\022\t\n\005ERROR\020\003"
|
||||
"\t:\0028\001\"E\n\006Status\022\020\n\014DISCONNECTED\020\000\022\006\n\002OK\020"
|
||||
+
|
||||
"\022\014\n\010OCCLUDED\020\004\"3\n\nConfidence\022\006\n\002NO\020\000\022\007\n\003"
|
||||
"\001\022\010\n\004BUSY\020\002\022\t\n\005ERROR\020\003\022\014\n\010OCCLUDED\020\004\"3\n\n"
|
||||
+
|
||||
"LOW\020\001\022\n\n\006MEDIUM\020\005\022\010\n\004HIGH\020\nB\r\n\013_confiden"
|
||||
"Confidence\022\006\n\002NO\020\000\022\007\n\003LOW\020\001\022\n\n\006MEDIUM\020\005\022"
|
||||
+
|
||||
"ce\"I\n\007Battery\022\022\n\ntracker_id\030\001 \001(\005\022\025\n\rbat"
|
||||
"\010\n\004HIGH\020\nB\r\n\013_confidence\"I\n\007Battery\022\022\n\nt"
|
||||
+
|
||||
"tery_level\030\002 \001(\002\022\023\n\013is_charging\030\003 \001(\010\"\241\002"
|
||||
"racker_id\030\001 \001(\005\022\025\n\rbattery_level\030\002 \001(\002\022\023"
|
||||
+
|
||||
"\n\017ProtobufMessage\022&\n\010position\030\001 \001(\0132\022.me"
|
||||
"\n\013is_charging\030\003 \001(\010\"\241\002\n\017ProtobufMessage\022"
|
||||
+
|
||||
"ssages.PositionH\000\022+\n\013user_action\030\002 \001(\0132\024"
|
||||
"&\n\010position\030\001 \001(\0132\022.messages.PositionH\000\022"
|
||||
+
|
||||
".messages.UserActionH\000\022/\n\rtracker_added\030"
|
||||
"+\n\013user_action\030\002 \001(\0132\024.messages.UserActi"
|
||||
+
|
||||
"\003 \001(\0132\026.messages.TrackerAddedH\000\0221\n\016track"
|
||||
"onH\000\022/\n\rtracker_added\030\003 \001(\0132\026.messages.T"
|
||||
+
|
||||
"er_status\030\004 \001(\0132\027.messages.TrackerStatus"
|
||||
"rackerAddedH\000\0221\n\016tracker_status\030\004 \001(\0132\027."
|
||||
+
|
||||
"H\000\022$\n\007battery\030\005 \001(\0132\021.messages.BatteryH\000"
|
||||
"messages.TrackerStatusH\000\022$\n\007battery\030\005 \001("
|
||||
+
|
||||
"\022$\n\007version\030\006 \001(\0132\021.messages.VersionH\000B\t"
|
||||
"\0132\021.messages.BatteryH\000\022$\n\007version\030\006 \001(\0132"
|
||||
+
|
||||
"\n\007messageB2\n\034dev.slimevr.desktop.platfor"
|
||||
"\021.messages.VersionH\000B\t\n\007messageB2\n\034dev.s"
|
||||
+
|
||||
"mB\020ProtobufMessagesH\003b\006proto3"
|
||||
"limevr.desktop.platformB\020ProtobufMessage"
|
||||
+
|
||||
"sH\003b\006proto3"
|
||||
};
|
||||
descriptor = com.google.protobuf.Descriptors.FileDescriptor
|
||||
.internalBuildGeneratedFileFrom(
|
||||
@@ -8957,7 +9130,8 @@ public final class ProtobufMessages {
|
||||
internal_static_messages_TrackerAdded_descriptor = getDescriptor().getMessageTypes().get(4);
|
||||
internal_static_messages_TrackerAdded_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable(
|
||||
internal_static_messages_TrackerAdded_descriptor,
|
||||
new java.lang.String[] { "TrackerId", "TrackerSerial", "TrackerName", "TrackerRole", }
|
||||
new java.lang.String[] { "TrackerId", "TrackerSerial", "TrackerName", "TrackerRole",
|
||||
"Manufacturer", }
|
||||
);
|
||||
internal_static_messages_TrackerStatus_descriptor = getDescriptor()
|
||||
.getMessageTypes()
|
||||
|
||||
@@ -147,23 +147,13 @@ abstract class SteamVRBridge(
|
||||
val device = instance.deviceManager
|
||||
.createDevice(
|
||||
trackerAdded.trackerName,
|
||||
trackerAdded.trackerSerial,
|
||||
"OpenVR", // TODO : We need the manufacturer
|
||||
null,
|
||||
trackerAdded.manufacturer.ifEmpty { "OpenVR" },
|
||||
)
|
||||
|
||||
// Display name, needsReset and isHmd
|
||||
val displayName: String
|
||||
val isHmd = if (trackerAdded.trackerId == 0) {
|
||||
displayName = if (trackerAdded.trackerName == "HMD") {
|
||||
"SteamVR Driver HMD"
|
||||
} else {
|
||||
"Feeder App HMD"
|
||||
}
|
||||
true
|
||||
} else {
|
||||
displayName = trackerAdded.trackerName
|
||||
false
|
||||
}
|
||||
val displayName: String = trackerAdded.trackerName
|
||||
val isHmd = trackerAdded.trackerId == 0
|
||||
|
||||
// trackerPosition
|
||||
val role = getById(trackerAdded.trackerRole)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package dev.slimevr.desktop.tracking.trackers.hid
|
||||
|
||||
import dev.slimevr.VRServer
|
||||
import dev.slimevr.config.config
|
||||
import dev.slimevr.tracking.trackers.Device
|
||||
import dev.slimevr.tracking.trackers.Tracker
|
||||
import dev.slimevr.tracking.trackers.TrackerStatus
|
||||
import dev.slimevr.tracking.trackers.hid.HIDCommon
|
||||
import dev.slimevr.tracking.trackers.hid.HIDCommon.Companion.HID_TRACKER_PID
|
||||
import dev.slimevr.tracking.trackers.hid.HIDCommon.Companion.HID_TRACKER_RECEIVER_PID
|
||||
import dev.slimevr.tracking.trackers.hid.HIDCommon.Companion.HID_TRACKER_RECEIVER_VID
|
||||
import dev.slimevr.tracking.trackers.hid.HIDCommon.Companion.PACKET_SIZE
|
||||
@@ -55,12 +58,15 @@ class DesktopHIDManager(name: String, private val trackersConsumer: Consumer<Tra
|
||||
}
|
||||
|
||||
private fun checkConfigureDevice(hidDevice: HidDevice) {
|
||||
if (hidDevice.vendorId == HID_TRACKER_RECEIVER_VID && hidDevice.productId == HID_TRACKER_RECEIVER_PID) { // TODO: Use correct ids
|
||||
if (hidDevice.vendorId == HID_TRACKER_RECEIVER_VID && (hidDevice.productId == HID_TRACKER_RECEIVER_PID || hidDevice.productId == HID_TRACKER_PID)) { // TODO: Use list of valid ids
|
||||
val serial = hidDevice.serialNumber ?: "Unknown HID Device"
|
||||
if (hidDevice.isClosed) {
|
||||
check(hidDevice.open()) { "Unable to open device" }
|
||||
if (!hidDevice.open()) {
|
||||
LogManager.warning("[TrackerServer] Unable to open device: $serial")
|
||||
return
|
||||
}
|
||||
}
|
||||
// TODO: Configure the device here
|
||||
val serial = hidDevice.serialNumber ?: "Unknown HID Device"
|
||||
// val product = hidDevice.product
|
||||
// val manufacturer = hidDevice.manufacturer
|
||||
this.devicesBySerial[serial]?.let {
|
||||
@@ -205,12 +211,29 @@ class DesktopHIDManager(name: String, private val trackersConsumer: Consumer<Tra
|
||||
}
|
||||
|
||||
private fun deviceEnumerate() {
|
||||
var root: HidDeviceInfoStructure? = null
|
||||
var rootReceivers: HidDeviceInfoStructure? = null
|
||||
var rootTrackers: HidDeviceInfoStructure? = null
|
||||
val trackersOverHID: Boolean = VRServer.instance.configManager.vrConfig.hidConfig.trackersOverHID
|
||||
try {
|
||||
root = HidApi.enumerateDevices(HID_TRACKER_RECEIVER_VID, HID_TRACKER_RECEIVER_PID) // TODO: change to proper vendorId and productId, need to enum all appropriate productId
|
||||
rootReceivers = HidApi.enumerateDevices(HID_TRACKER_RECEIVER_VID, HID_TRACKER_RECEIVER_PID) // TODO: Use list of ids
|
||||
rootTrackers = if (trackersOverHID) {
|
||||
HidApi.enumerateDevices(HID_TRACKER_RECEIVER_VID, HID_TRACKER_PID)
|
||||
} else {
|
||||
null
|
||||
} // TODO: Use list of ids
|
||||
} catch (e: Throwable) {
|
||||
LogManager.severe("[TrackerServer] Couldn't enumerate HID devices", e)
|
||||
}
|
||||
var root: HidDeviceInfoStructure? = rootReceivers
|
||||
if (root == null) {
|
||||
root = rootTrackers
|
||||
} else {
|
||||
var last: HidDeviceInfoStructure = root
|
||||
while (last.hasNext()) {
|
||||
last = last.next()
|
||||
}
|
||||
last.next = rootTrackers
|
||||
}
|
||||
val hidDeviceList: MutableList<HidDevice> = mutableListOf()
|
||||
if (root != null) {
|
||||
var hidDeviceInfoStructure: HidDeviceInfoStructure? = root
|
||||
|
||||
Submodule solarxr-protocol updated: b0147eeffa...fa2895b19a
Reference in New Issue
Block a user