Compare commits

...

7 Commits

Author SHA1 Message Date
Sam Freund
10f38268e6 Evaluate IMAGE_VERSION when passing into image-runner (#2521)
If we don't evaluate IMAGE_VERSION when passing it into the image-runner
action, cache gets the variable name instead of the actual value. This
PR also cleans up the really long URLs in the matrix entries since it's
always the same.
2026-06-23 22:08:07 -07:00
Sam Freund
d3503226b3 cache rubik image (#2520)
Pursuant to caching being added in
https://github.com/photonvision/photonvision/photon-image-runner, we can
now cache images for our action. We're only caching the rubik image as
we need to wait for all of them to finish anyways, and caching the other
ones as well won't gain time.

Also, downgrade to windows 2022 cause MSVC ICE bugs. Also also, bump
pnpm to 11 in actions cause windows using the version as part of the
path for caches (why? WHY??)

---------

Co-authored-by: Matt Morley <matthew.morley.ca@gmail.com>
2026-06-23 20:08:55 -07:00
Sam Freund
0e2563111c refactor OD model UID to path (#2486) 2026-06-23 02:15:57 -04:00
Alan Everett
0a07263f74 Clean up TargetModel.java (#2460)
## Description

`TargetModel.java` had some leftover code from #139 still present. This
cleans up everything unused and also consolidates some redundant code to
make the class more readable.

## Meta

Merge checklist:
- [x] Pull Request title is [short, imperative
summary](https://cbea.ms/git-commit/) of proposed changes
- [x] The description documents the _what_ and _why_, including events
that led to this PR
- [ ] If this PR changes behavior or adds a feature, user documentation
is updated
- [ ] If this PR touches photon-serde, all messages have been
regenerated and hashes have not changed unexpectedly
- [x] If this PR touches configuration, this is backwards compatible
with all settings going back to the previous seasons's last release
(seasons end after champs ends)
- [ ] If this PR touches pipeline settings or anything related to data
exchange, the frontend typing is updated
- [ ] If this PR addresses a bug, a regression test for it is added
- [ ] If this PR adds a dependency, the license has been checked for
compatibility and steps taken to follow it

---------

Co-authored-by: Matt Morley <matthew.morley.ca@gmail.com>
2026-06-23 01:55:39 -04:00
Alan Everett
e41be8e858 Store calibration board measurements in native units (#2480) 2026-06-22 22:31:44 -07:00
Gold856
bd9f899514 Shorten registration of CorsPlugin (#2518) 2026-06-22 22:28:40 -07:00
Alan Everett
c04c8d76ed Use Avaje Jsonb for Javalin JSON (#2512) 2026-06-09 17:20:21 -07:00
21 changed files with 209 additions and 215 deletions

View File

@@ -100,7 +100,7 @@ jobs:
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v5 uses: pnpm/action-setup@v5
with: with:
version: 10 version: 11
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v6
with: with:
@@ -125,7 +125,7 @@ jobs:
distribution: temurin distribution: temurin
- uses: pnpm/action-setup@v5 - uses: pnpm/action-setup@v5
with: with:
version: 10 version: 11
- uses: actions/setup-node@v6 - uses: actions/setup-node@v6
with: with:
cache: pnpm cache: pnpm
@@ -164,7 +164,7 @@ jobs:
distribution: temurin distribution: temurin
- uses: pnpm/action-setup@v5 - uses: pnpm/action-setup@v5
with: with:
version: 10 version: 11
- uses: actions/setup-node@v6 - uses: actions/setup-node@v6
with: with:
cache: pnpm cache: pnpm
@@ -363,7 +363,7 @@ jobs:
distribution: temurin distribution: temurin
- uses: pnpm/action-setup@v5 - uses: pnpm/action-setup@v5
with: with:
version: 10 version: 11
- uses: actions/setup-node@v6 - uses: actions/setup-node@v6
with: with:
node-version: 24 node-version: 24
@@ -415,7 +415,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- os: windows-latest - os: windows-2022
artifact-name: Win artifact-name: Win
arch-override: winx86-64 arch-override: winx86-64
@@ -434,7 +434,7 @@ jobs:
- os: ubuntu-24.04 - os: ubuntu-24.04
artifact-name: photonvision-*-linuxx86-64.jar artifact-name: photonvision-*-linuxx86-64.jar
extraOpts: -Djdk.lang.Process.launchMechanism=vfork extraOpts: -Djdk.lang.Process.launchMechanism=vfork
- os: windows-latest - os: windows-2022
artifact-name: photonvision-*-winx86-64.jar artifact-name: photonvision-*-winx86-64.jar
- os: macos-latest - os: macos-latest
artifact-name: photonvision-*-macarm64.jar artifact-name: photonvision-*-macarm64.jar
@@ -474,11 +474,11 @@ jobs:
fi fi
echo "=== Second run ===" echo "=== Second run ==="
java -jar ${{ matrix.extraOpts }} *.jar --smoketest java -jar ${{ matrix.extraOpts }} *.jar --smoketest
if: ${{ (matrix.os) != 'windows-latest' }} if: ${{ (matrix.os) != 'windows-2022' }}
- run: | - run: |
ls *.jar | %{ Write-Host "Running $($_.Name)"; Start-Process "java" -ArgumentList "-jar `"$($_.FullName)`" --smoketest" -NoNewWindow -Wait; break } ls *.jar | %{ Write-Host "Running $($_.Name)"; Start-Process "java" -ArgumentList "-jar `"$($_.FullName)`" --smoketest" -NoNewWindow -Wait; break }
ls *.jar | %{ Write-Host "Running $($_.Name)"; Start-Process "java" -ArgumentList "-jar `"$($_.FullName)`" --smoketest" -NoNewWindow -Wait; break } ls *.jar | %{ Write-Host "Running $($_.Name)"; Start-Process "java" -ArgumentList "-jar `"$($_.FullName)`" --smoketest" -NoNewWindow -Wait; break }
if: ${{ (matrix.os) == 'windows-latest' }} if: ${{ (matrix.os) == 'windows-2022' }}
build-image: build-image:
needs: [build-package-linux] needs: [build-package-linux]
@@ -490,67 +490,68 @@ jobs:
- os: ubuntu-24.04-arm - os: ubuntu-24.04-arm
image_suffix: RaspberryPi image_suffix: RaspberryPi
plat_override: LINUX_RASPBIAN64 plat_override: LINUX_RASPBIAN64
image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_raspi.img.xz image_name: photonvision_raspi.img.xz
minimum_free_mb: 100 minimum_free_mb: 100
- os: ubuntu-24.04-arm - os: ubuntu-24.04-arm
image_suffix: limelight2 image_suffix: limelight2
plat_override: LINUX_RASPBIAN64 plat_override: LINUX_RASPBIAN64
image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_limelight.img.xz image_name: photonvision_limelight.img.xz
minimum_free_mb: 100 minimum_free_mb: 100
- os: ubuntu-24.04-arm - os: ubuntu-24.04-arm
image_suffix: limelight3 image_suffix: limelight3
plat_override: LINUX_RASPBIAN64 plat_override: LINUX_RASPBIAN64
image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_limelight3.img.xz image_name: photonvision_limelight3.img.xz
minimum_free_mb: 100 minimum_free_mb: 100
- os: ubuntu-24.04-arm - os: ubuntu-24.04-arm
image_suffix: limelight3G image_suffix: limelight3G
plat_override: LINUX_RASPBIAN64 plat_override: LINUX_RASPBIAN64
image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_limelight3g.img.xz image_name: photonvision_limelight3g.img.xz
minimum_free_mb: 100 minimum_free_mb: 100
- os: ubuntu-24.04-arm - os: ubuntu-24.04-arm
image_suffix: limelight4 image_suffix: limelight4
plat_override: LINUX_RASPBIAN64 plat_override: LINUX_RASPBIAN64
image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_limelight4.img.xz image_name: photonvision_limelight4.img.xz
minimum_free_mb: 100 minimum_free_mb: 100
- os: ubuntu-24.04-arm - os: ubuntu-24.04-arm
image_suffix: luma_p1 image_suffix: luma_p1
plat_override: LINUX_RASPBIAN64 plat_override: LINUX_RASPBIAN64
image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_luma_p1.img.xz image_name: photonvision_luma_p1.img.xz
minimum_free_mb: 100 minimum_free_mb: 100
- os: ubuntu-24.04-arm - os: ubuntu-24.04-arm
image_suffix: orangepi5 image_suffix: orangepi5
plat_override: LINUX_RK3588_64 plat_override: LINUX_RK3588_64
image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_opi5.img.xz image_name: photonvision_opi5.img.xz
minimum_free_mb: 1024 minimum_free_mb: 1024
- os: ubuntu-24.04-arm - os: ubuntu-24.04-arm
image_suffix: orangepi5b image_suffix: orangepi5b
plat_override: LINUX_RK3588_64 plat_override: LINUX_RK3588_64
image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_opi5b.img.xz image_name: photonvision_opi5b.img.xz
minimum_free_mb: 1024 minimum_free_mb: 1024
- os: ubuntu-24.04-arm - os: ubuntu-24.04-arm
image_suffix: orangepi5plus image_suffix: orangepi5plus
plat_override: LINUX_RK3588_64 plat_override: LINUX_RK3588_64
image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_opi5plus.img.xz image_name: photonvision_opi5plus.img.xz
minimum_free_mb: 1024 minimum_free_mb: 1024
- os: ubuntu-24.04-arm - os: ubuntu-24.04-arm
image_suffix: orangepi5pro image_suffix: orangepi5pro
plat_override: LINUX_RK3588_64 plat_override: LINUX_RK3588_64
image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_opi5pro.img.xz image_name: photonvision_opi5pro.img.xz
minimum_free_mb: 1024 minimum_free_mb: 1024
- os: ubuntu-24.04-arm - os: ubuntu-24.04-arm
image_suffix: orangepi5max image_suffix: orangepi5max
plat_override: LINUX_RK3588_64 plat_override: LINUX_RK3588_64
image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_opi5max.img.xz image_name: photonvision_opi5max.img.xz
minimum_free_mb: 1024 minimum_free_mb: 1024
- os: ubuntu-24.04-arm - os: ubuntu-24.04-arm
image_suffix: rock5c image_suffix: rock5c
plat_override: LINUX_RK3588_64 plat_override: LINUX_RK3588_64
image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_rock5c.img.xz image_name: photonvision_rock5c.img.xz
minimum_free_mb: 1024 minimum_free_mb: 1024
- os: ubuntu-24.04-arm - os: ubuntu-24.04-arm
image_suffix: rubikpi3 image_suffix: rubikpi3
cache: 'yes'
plat_override: LINUX_QCS6490 plat_override: LINUX_QCS6490
image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_rubikpi3.tar.xz image_name: photonvision_rubikpi3.tar.xz
minimum_free_mb: 1024 minimum_free_mb: 1024
root_location: 'offset=569376768' root_location: 'offset=569376768'
shrink_image: 'no' shrink_image: 'no'
@@ -566,11 +567,12 @@ jobs:
- uses: actions/download-artifact@v8 - uses: actions/download-artifact@v8
with: with:
pattern: photonvision-*-linuxarm64.jar pattern: photonvision-*-linuxarm64.jar
- uses: photonvision/photon-image-runner@HEAD - uses: photonvision/photon-image-runner@v2.0.0
name: Generate image name: Generate image
id: generate_image id: generate_image
with: with:
image_url: ${{ matrix.image_url }} image_url: "https://github.com/PhotonVision/photon-image-modifier/releases/download/${{ env.IMAGE_VERSION }}/${{ matrix.image_name }}"
use_cache: ${{ matrix.cache || 'no' }}
minimum_free_mb: ${{ matrix.minimum_free_mb }} minimum_free_mb: ${{ matrix.minimum_free_mb }}
root_location: ${{ matrix.root_location || 'partition=2' }} root_location: ${{ matrix.root_location || 'partition=2' }}
shrink_image: ${{ matrix.shrink_image || 'yes' }} shrink_image: ${{ matrix.shrink_image || 'yes' }}

View File

@@ -34,7 +34,7 @@ ext {
wpilibVersion = "2027.0.0-alpha-6" wpilibVersion = "2027.0.0-alpha-6"
openCVversion = "2027-4.13.0-3" openCVversion = "2027-4.13.0-3"
ejmlVersion = "0.43.1"; ejmlVersion = "0.43.1";
avajeJsonbVersion = "3.14-RC4"; avajeJsonbVersion = "3.14";
msgpackVersion = "0.9.0"; msgpackVersion = "0.9.0";
quickbufVersion = "1.3.3"; quickbufVersion = "1.3.3";
jacocoVersion = "0.8.14"; jacocoVersion = "0.8.14";

View File

@@ -21,6 +21,7 @@
"type-check": "vue-tsc --noEmit" "type-check": "vue-tsc --noEmit"
}, },
"dependencies": { "dependencies": {
"@adam-rocska/units-and-measurement": "^1.2.0",
"@fontsource/prompt": "^5.2.6", "@fontsource/prompt": "^5.2.6",
"@mdi/font": "^7.4.47", "@mdi/font": "^7.4.47",
"@msgpack/msgpack": "^3.1.2", "@msgpack/msgpack": "^3.1.2",

View File

@@ -8,6 +8,9 @@ importers:
.: .:
dependencies: dependencies:
'@adam-rocska/units-and-measurement':
specifier: ^1.2.0
version: 1.2.0
'@fontsource/prompt': '@fontsource/prompt':
specifier: ^5.2.6 specifier: ^5.2.6
version: 5.2.6 version: 5.2.6
@@ -96,6 +99,10 @@ importers:
packages: packages:
'@adam-rocska/units-and-measurement@1.2.0':
resolution: {integrity: sha512-mBnZ8/STbztVec+Mz9DH932z0gny52SebtSJ/y3n+IVtuF7KqbtQ3t1u1lpFSkLFU1msaNGzFgqsW7Emj0lrXA==}
engines: {node: '>=20.0.0'}
'@babel/helper-string-parser@7.27.1': '@babel/helper-string-parser@7.27.1':
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
@@ -250,42 +257,36 @@ packages:
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [glibc]
'@parcel/watcher-linux-arm-musl@2.5.1': '@parcel/watcher-linux-arm-musl@2.5.1':
resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [musl]
'@parcel/watcher-linux-arm64-glibc@2.5.1': '@parcel/watcher-linux-arm64-glibc@2.5.1':
resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@parcel/watcher-linux-arm64-musl@2.5.1': '@parcel/watcher-linux-arm64-musl@2.5.1':
resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@parcel/watcher-linux-x64-glibc@2.5.1': '@parcel/watcher-linux-x64-glibc@2.5.1':
resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@parcel/watcher-linux-x64-musl@2.5.1': '@parcel/watcher-linux-x64-musl@2.5.1':
resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@parcel/watcher-win32-arm64@2.5.1': '@parcel/watcher-win32-arm64@2.5.1':
resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==}
@@ -353,42 +354,36 @@ packages:
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@rolldown/binding-linux-arm64-musl@1.0.0-rc.17': '@rolldown/binding-linux-arm64-musl@1.0.0-rc.17':
resolution: {integrity: sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==} resolution: {integrity: sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17': '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17':
resolution: {integrity: sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==} resolution: {integrity: sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [glibc]
'@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17': '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17':
resolution: {integrity: sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==} resolution: {integrity: sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc]
'@rolldown/binding-linux-x64-gnu@1.0.0-rc.17': '@rolldown/binding-linux-x64-gnu@1.0.0-rc.17':
resolution: {integrity: sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==} resolution: {integrity: sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@rolldown/binding-linux-x64-musl@1.0.0-rc.17': '@rolldown/binding-linux-x64-musl@1.0.0-rc.17':
resolution: {integrity: sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==} resolution: {integrity: sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@rolldown/binding-openharmony-arm64@1.0.0-rc.17': '@rolldown/binding-openharmony-arm64@1.0.0-rc.17':
resolution: {integrity: sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==} resolution: {integrity: sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==}
@@ -1084,28 +1079,24 @@ packages:
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
lightningcss-linux-arm64-musl@1.32.0: lightningcss-linux-arm64-musl@1.32.0:
resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
lightningcss-linux-x64-gnu@1.32.0: lightningcss-linux-x64-gnu@1.32.0:
resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
lightningcss-linux-x64-musl@1.32.0: lightningcss-linux-x64-musl@1.32.0:
resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
lightningcss-win32-arm64-msvc@1.32.0: lightningcss-win32-arm64-msvc@1.32.0:
resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
@@ -1550,6 +1541,8 @@ packages:
snapshots: snapshots:
'@adam-rocska/units-and-measurement@1.2.0': {}
'@babel/helper-string-parser@7.27.1': {} '@babel/helper-string-parser@7.27.1': {}
'@babel/helper-validator-identifier@7.27.1': {} '@babel/helper-validator-identifier@7.27.1': {}

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref, watchEffect } from "vue"; import { computed, ref, watch, watchEffect } from "vue";
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore"; import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
import { CalibrationBoardTypes, CalibrationTagFamilies, type VideoFormat } from "@/types/SettingTypes"; import { CalibrationBoardTypes, CalibrationTagFamilies, type VideoFormat } from "@/types/SettingTypes";
import MonoLogo from "@/assets/images/logoMono.png"; import MonoLogo from "@/assets/images/logoMono.png";
@@ -15,12 +15,12 @@ import CameraCalibrationInfoCard from "@/components/cameras/CameraCalibrationInf
import { useSettingsStore } from "@/stores/settings/GeneralSettingsStore"; import { useSettingsStore } from "@/stores/settings/GeneralSettingsStore";
import { useTheme } from "vuetify"; import { useTheme } from "vuetify";
import TooltippedLabel from "@/components/common/pv-tooltipped-label.vue"; import TooltippedLabel from "@/components/common/pv-tooltipped-label.vue";
import { length } from "@adam-rocska/units-and-measurement/length";
const PromptRegular = import("@/assets/fonts/PromptRegular"); const PromptRegular = import("@/assets/fonts/PromptRegular");
const jspdf = import("jspdf"); const jspdf = import("jspdf");
const theme = useTheme(); const theme = useTheme();
const MM_PER_INCH = 25.4;
const settingsValid = ref(true); const settingsValid = ref(true);
@@ -110,8 +110,8 @@ watchEffect(() => {
uniqueVideoResolutionIndex.value = currentIndex; uniqueVideoResolutionIndex.value = currentIndex;
}); });
const dimensionUnit = ref<"in" | "mm">("in"); const dimensionUnit = ref<"in" | "mm">("in");
const squareSizeIn = ref(1); const squareSize = ref(30);
const markerSizeIn = ref(0.75); const markerSize = ref(22);
const patternWidth = ref(8); const patternWidth = ref(8);
const patternHeight = ref(8); const patternHeight = ref(8);
const boardType = ref<CalibrationBoardTypes>(CalibrationBoardTypes.Charuco); const boardType = ref<CalibrationBoardTypes>(CalibrationBoardTypes.Charuco);
@@ -119,24 +119,9 @@ const useOldPattern = ref(false);
const tagFamily = ref<CalibrationTagFamilies>(CalibrationTagFamilies.Dict_4X4_1000); const tagFamily = ref<CalibrationTagFamilies>(CalibrationTagFamilies.Dict_4X4_1000);
const requestedVideoFormatIndex = ref(0); const requestedVideoFormatIndex = ref(0);
const convertInchesToDisplay = (valueInInches: number) => watch(dimensionUnit, (value, oldValue) => {
dimensionUnit.value === "mm" ? valueInInches * MM_PER_INCH : valueInInches; squareSize.value = length[oldValue](squareSize.value)[value].value;
markerSize.value = length[oldValue](markerSize.value)[value].value;
const convertDisplayToInches = (displayValue: number) =>
dimensionUnit.value === "mm" ? displayValue / MM_PER_INCH : displayValue;
const squareSize = computed({
get: () => convertInchesToDisplay(squareSizeIn.value),
set(value) {
squareSizeIn.value = convertDisplayToInches(value);
}
});
const markerSize = computed({
get: () => convertInchesToDisplay(markerSizeIn.value),
set(value) {
markerSizeIn.value = convertDisplayToInches(value);
}
}); });
const dimensionStep = computed(() => (dimensionUnit.value === "mm" ? 0.1 : 0.01)); const dimensionStep = computed(() => (dimensionUnit.value === "mm" ? 0.1 : 0.01));
@@ -161,25 +146,31 @@ const downloadCalibBoard = async () => {
switch (boardType.value) { switch (boardType.value) {
case CalibrationBoardTypes.Chessboard: case CalibrationBoardTypes.Chessboard:
const chessboardStartX = (paperWidth - patternWidth.value * squareSizeIn.value) / 2; const squareSizeIn = length[dimensionUnit.value](squareSize.value).in.value;
const chessboardStartX = (paperWidth - patternWidth.value * squareSizeIn) / 2;
const chessboardStartY = (paperHeight - patternWidth.value * squareSizeIn.value) / 2; const chessboardStartY = (paperHeight - patternHeight.value * squareSizeIn) / 2;
for (let squareY = 0; squareY < patternHeight.value; squareY++) { for (let squareY = 0; squareY < patternHeight.value; squareY++) {
for (let squareX = 0; squareX < patternWidth.value; squareX++) { for (let squareX = 0; squareX < patternWidth.value; squareX++) {
const xPos = chessboardStartX + squareX * squareSizeIn.value; const xPos = chessboardStartX + squareX * squareSizeIn;
const yPos = chessboardStartY + squareY * squareSizeIn.value; const yPos = chessboardStartY + squareY * squareSizeIn;
// Only draw the odd squares to create the chessboard pattern // Only draw the odd squares to create the chessboard pattern
if (squareY % 2 !== squareX % 2) { if (squareY % 2 !== squareX % 2) {
doc.rect(xPos, yPos, squareSizeIn.value, squareSizeIn.value, "F"); doc.rect(xPos, yPos, squareSizeIn, squareSizeIn, "F");
} }
} }
} }
doc.text(`${patternWidth.value} x ${patternHeight.value} | ${squareSizeIn.value}in`, paperWidth - 1, 1.0, { doc.text(
maxWidth: (paperWidth - 2.0) / 2, `${patternWidth.value} x ${patternHeight.value} | ${squareSize.value}${dimensionUnit.value}`,
align: "right" paperWidth - 1,
}); 1.0,
{
maxWidth: (paperWidth - 2.0) / 2,
align: "right"
}
);
break; break;
case CalibrationBoardTypes.Charuco: case CalibrationBoardTypes.Charuco:
@@ -220,8 +211,8 @@ const isCalibrating = computed(
const startCalibration = () => { const startCalibration = () => {
useCameraSettingsStore().startPnPCalibration({ useCameraSettingsStore().startPnPCalibration({
squareSizeIn: squareSizeIn.value, squareSizeMeters: length[dimensionUnit.value](squareSize.value).m.value,
markerSizeIn: markerSizeIn.value, markerSizeMeters: length[dimensionUnit.value](markerSize.value).m.value,
patternHeight: patternHeight.value, patternHeight: patternHeight.value,
patternWidth: patternWidth.value, patternWidth: patternWidth.value,
boardType: boardType.value, boardType: boardType.value,

View File

@@ -368,8 +368,8 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
*/ */
startPnPCalibration( startPnPCalibration(
calibrationInitData: { calibrationInitData: {
squareSizeIn: number; squareSizeMeters: number;
markerSizeIn: number; markerSizeMeters: number;
patternWidth: number; patternWidth: number;
patternHeight: number; patternHeight: number;
boardType: CalibrationBoardTypes; boardType: CalibrationBoardTypes;

View File

@@ -87,8 +87,8 @@ export interface WebsocketCalibrationData {
minCount: number; minCount: number;
videoModeIndex: number; videoModeIndex: number;
patternHeight: number; patternHeight: number;
squareSizeIn: number; squareSizeMm: number;
markerSizeIn: number; markerSizeMm: number;
} }
export interface IncomingWebsocketData { export interface IncomingWebsocketData {

View File

@@ -280,10 +280,10 @@ public class NeuralNetworkModelManager {
* *
* <p>If this method returns `Optional.of(..)` then the model should be safe to load. * <p>If this method returns `Optional.of(..)` then the model should be safe to load.
* *
* @param modelUID the unique identifier of the model to retrieve * @param modelPath the unique identifier of the model to retrieve
* @return an Optional containing the model if found, or an empty Optional if not found * @return an Optional containing the model if found, or an empty Optional if not found
*/ */
public Optional<Model> getModel(String modelUID) { public Optional<Model> getModel(Path modelPath) {
if (models == null) { if (models == null) {
return Optional.empty(); return Optional.empty();
} }
@@ -292,7 +292,7 @@ public class NeuralNetworkModelManager {
for (Family backend : supportedBackends) { for (Family backend : supportedBackends) {
if (models.containsKey(backend)) { if (models.containsKey(backend)) {
Optional<Model> model = Optional<Model> model =
models.get(backend).stream().filter(m -> m.getUID().equals(modelUID)).findFirst(); models.get(backend).stream().filter(m -> m.getPath().equals(modelPath)).findFirst();
if (model.isPresent()) { if (model.isPresent()) {
return model; return model;
} }
@@ -406,7 +406,8 @@ public class NeuralNetworkModelManager {
// After loading all of the models, sort them by name to ensure a consistent // After loading all of the models, sort them by name to ensure a consistent
// ordering // ordering
models.forEach( models.forEach(
(backend, backendModels) -> backendModels.sort((a, b) -> a.getUID().compareTo(b.getUID()))); (backend, backendModels) ->
backendModels.sort((a, b) -> a.getPath().compareTo(b.getPath())));
// Log // Log
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
@@ -414,7 +415,7 @@ public class NeuralNetworkModelManager {
models.forEach( models.forEach(
(backend, backendModels) -> { (backend, backendModels) -> {
sb.append(backend).append(" ["); sb.append(backend).append(" [");
backendModels.forEach(model -> sb.append(model.getUID()).append(", ")); backendModels.forEach(model -> sb.append(model.getPath()).append(", "));
sb.append("] "); sb.append("] ");
}); });
} }

View File

@@ -17,13 +17,19 @@
package org.photonvision.vision.objects; package org.photonvision.vision.objects;
import java.nio.file.Path;
import org.photonvision.common.configuration.NeuralNetworkModelManager.Family; import org.photonvision.common.configuration.NeuralNetworkModelManager.Family;
import org.photonvision.common.configuration.NeuralNetworkModelsSettings.ModelProperties; import org.photonvision.common.configuration.NeuralNetworkModelsSettings.ModelProperties;
public interface Model { public interface Model {
public ObjectDetector load(); public ObjectDetector load();
public String getUID(); /**
* Gets the path to the model file. This is being used as a UID.
*
* @return the path to the model file (for use as a UID)
*/
public Path getPath();
public String getNickname(); public String getNickname();

View File

@@ -17,6 +17,7 @@
package org.photonvision.vision.objects; package org.photonvision.vision.objects;
import java.nio.file.Path;
import java.util.List; import java.util.List;
import org.opencv.core.Mat; import org.opencv.core.Mat;
import org.photonvision.common.configuration.NeuralNetworkModelManager.Family; import org.photonvision.common.configuration.NeuralNetworkModelManager.Family;
@@ -43,8 +44,8 @@ public class NullModel implements Model, ObjectDetector {
} }
@Override @Override
public String getUID() { public Path getPath() {
return "NullModel"; return Path.of("null");
} }
@Override @Override

View File

@@ -18,6 +18,7 @@
package org.photonvision.vision.objects; package org.photonvision.vision.objects;
import java.io.File; import java.io.File;
import java.nio.file.Path;
import org.opencv.core.Size; import org.opencv.core.Size;
import org.photonvision.common.configuration.NeuralNetworkModelManager.Family; import org.photonvision.common.configuration.NeuralNetworkModelManager.Family;
import org.photonvision.common.configuration.NeuralNetworkModelManager.Version; import org.photonvision.common.configuration.NeuralNetworkModelManager.Version;
@@ -61,8 +62,8 @@ public class RknnModel implements Model {
} }
/** Return the unique identifier for the model. In this case, it's the model's path. */ /** Return the unique identifier for the model. In this case, it's the model's path. */
public String getUID() { public Path getPath() {
return properties.modelPath().toString(); return properties.modelPath();
} }
public String getNickname() { public String getNickname() {

View File

@@ -18,6 +18,7 @@
package org.photonvision.vision.objects; package org.photonvision.vision.objects;
import java.io.File; import java.io.File;
import java.nio.file.Path;
import org.opencv.core.Size; import org.opencv.core.Size;
import org.photonvision.common.configuration.NeuralNetworkModelManager.Family; import org.photonvision.common.configuration.NeuralNetworkModelManager.Family;
import org.photonvision.common.configuration.NeuralNetworkModelManager.Version; import org.photonvision.common.configuration.NeuralNetworkModelManager.Version;
@@ -59,8 +60,8 @@ public class RubikModel implements Model {
} }
/** Return the unique identifier for the model. In this case, it's the model's path. */ /** Return the unique identifier for the model. In this case, it's the model's path. */
public String getUID() { public Path getPath() {
return properties.modelPath().toString(); return properties.modelPath();
} }
public String getNickname() { public String getNickname() {

View File

@@ -57,8 +57,7 @@ public class ObjectDetectionPipeline
protected void setPipeParamsImpl() { protected void setPipeParamsImpl() {
Optional<Model> selectedModel = Optional<Model> selectedModel =
settings.model != null settings.model != null
? NeuralNetworkModelManager.getInstance() ? NeuralNetworkModelManager.getInstance().getModel(settings.model.modelPath())
.getModel(settings.model.modelPath().toString())
: Optional.empty(); : Optional.empty();
// If the desired model couldn't be found, log an error and try to use the default model // If the desired model couldn't be found, log an error and try to use the default model

View File

@@ -24,11 +24,11 @@ import org.opencv.objdetect.Objdetect;
public class UICalibrationData { public class UICalibrationData {
public int videoModeIndex; public int videoModeIndex;
public int count; public int count;
public double squareSizeIn; public double squareSizeMeters;
public int patternWidth; public int patternWidth;
public int patternHeight; public int patternHeight;
public BoardType boardType; public BoardType boardType;
public double markerSizeIn; public double markerSizeMeters;
public boolean useOldPattern; public boolean useOldPattern;
public TagFamily tagFamily; public TagFamily tagFamily;
@@ -37,8 +37,8 @@ public class UICalibrationData {
public UICalibrationData( public UICalibrationData(
int count, int count,
int videoModeIndex, int videoModeIndex,
double squareSizeIn, double squareSizeMeters,
double markerSizeIn, double markerSizeMeters,
int patternWidth, int patternWidth,
int patternHeight, int patternHeight,
BoardType boardType, BoardType boardType,
@@ -46,8 +46,8 @@ public class UICalibrationData {
TagFamily tagFamily) { TagFamily tagFamily) {
this.count = count; this.count = count;
this.videoModeIndex = videoModeIndex; this.videoModeIndex = videoModeIndex;
this.squareSizeIn = squareSizeIn; this.squareSizeMeters = squareSizeMeters;
this.markerSizeIn = markerSizeIn; this.markerSizeMeters = markerSizeMeters;
this.patternWidth = patternWidth; this.patternWidth = patternWidth;
this.patternHeight = patternHeight; this.patternHeight = patternHeight;
this.boardType = boardType; this.boardType = boardType;
@@ -98,10 +98,10 @@ public class UICalibrationData {
+ videoModeIndex + videoModeIndex
+ ", count=" + ", count="
+ count + count
+ ", squareSizeIn=" + ", squareSizeMeters="
+ squareSizeIn + squareSizeMeters
+ ", markerSizeIn=" + ", markerSizeMeters="
+ markerSizeIn + markerSizeMeters
+ ", patternWidth=" + ", patternWidth="
+ patternWidth + patternWidth
+ ", patternHeight=" + ", patternHeight="

View File

@@ -55,7 +55,6 @@ import org.photonvision.vision.pipeline.UICalibrationData;
import org.photonvision.vision.pipeline.result.CVPipelineResult; import org.photonvision.vision.pipeline.result.CVPipelineResult;
import org.photonvision.vision.target.TargetModel; import org.photonvision.vision.target.TargetModel;
import org.photonvision.vision.target.TrackedTarget; import org.photonvision.vision.target.TrackedTarget;
import org.wpilib.math.util.Units;
import org.wpilib.vision.camera.CameraServerJNI; import org.wpilib.vision.camera.CameraServerJNI;
import org.wpilib.vision.camera.VideoException; import org.wpilib.vision.camera.VideoException;
@@ -391,8 +390,8 @@ public class VisionModule {
+ data.videoModeIndex + data.videoModeIndex
+ " and settings " + " and settings "
+ data); + data);
settings.gridSize = Units.inchesToMeters(data.squareSizeIn); settings.gridSize = data.squareSizeMeters;
settings.markerSize = Units.inchesToMeters(data.markerSizeIn); settings.markerSize = data.markerSizeMeters;
settings.boardHeight = data.patternHeight; settings.boardHeight = data.patternHeight;
settings.boardWidth = data.patternWidth; settings.boardWidth = data.patternWidth;
settings.boardType = data.boardType; settings.boardType = data.boardType;

View File

@@ -69,97 +69,28 @@ public enum TargetModel implements Releasable {
new Point3(Units.inchesToMeters(-19.625), Units.inchesToMeters(-8.5), 0), new Point3(Units.inchesToMeters(-19.625), Units.inchesToMeters(-8.5), 0),
new Point3(Units.inchesToMeters(19.625), Units.inchesToMeters(-8.5), 0)), new Point3(Units.inchesToMeters(19.625), Units.inchesToMeters(-8.5), 0)),
Units.inchesToMeters(12)), Units.inchesToMeters(12)),
kCircularPowerCell7in( kCircularPowerCell7in(circleTargetCorners(Units.inchesToMeters(7)), 0),
List.of( k2022CircularCargoBall(circleTargetCorners(Units.inchesToMeters(9.5)), 0),
new Point3( k2025Algae(circleTargetCorners(Units.inchesToMeters(16.25)), 0),
-Units.inchesToMeters(7) / 2,
-Units.inchesToMeters(7) / 2,
-Units.inchesToMeters(7) / 2),
new Point3(
-Units.inchesToMeters(7) / 2,
Units.inchesToMeters(7) / 2,
-Units.inchesToMeters(7) / 2),
new Point3(
Units.inchesToMeters(7) / 2,
Units.inchesToMeters(7) / 2,
-Units.inchesToMeters(7) / 2),
new Point3(
Units.inchesToMeters(7) / 2,
-Units.inchesToMeters(7) / 2,
-Units.inchesToMeters(7) / 2)),
0),
k2022CircularCargoBall(
List.of(
new Point3(
-Units.inchesToMeters(9.5) / 2,
-Units.inchesToMeters(9.5) / 2,
-Units.inchesToMeters(9.5) / 2),
new Point3(
-Units.inchesToMeters(9.5) / 2,
Units.inchesToMeters(9.5) / 2,
-Units.inchesToMeters(9.5) / 2),
new Point3(
Units.inchesToMeters(9.5) / 2,
Units.inchesToMeters(9.5) / 2,
-Units.inchesToMeters(9.5) / 2),
new Point3(
Units.inchesToMeters(9.5) / 2,
-Units.inchesToMeters(9.5) / 2,
-Units.inchesToMeters(9.5) / 2)),
0),
k2025Algae(
List.of(
new Point3(
-Units.inchesToMeters(16.25) / 2,
-Units.inchesToMeters(16.25) / 2,
-Units.inchesToMeters(16.25) / 2),
new Point3(
-Units.inchesToMeters(16.25) / 2,
Units.inchesToMeters(16.25) / 2,
-Units.inchesToMeters(16.25) / 2),
new Point3(
Units.inchesToMeters(16.25) / 2,
Units.inchesToMeters(16.25) / 2,
-Units.inchesToMeters(16.25) / 2),
new Point3(
Units.inchesToMeters(16.25) / 2,
-Units.inchesToMeters(16.25) / 2,
-Units.inchesToMeters(16.25) / 2)),
0),
// 2023 AprilTag, with 6 inch marker width (inner black square). // 2023 AprilTag, with 6 inch marker width (inner black square).
// MIGRATION: 2023 // MIGRATION: 2023
@Json.Alias({"k6in_16h5"}) @Json.Alias({"k6in_16h5"})
kAprilTag6in_16h5( kAprilTag6in_16h5(
// Corners of the tag's inner black square (excluding white border) // Corners of the tag's inner black square (excluding white border)
List.of( squareTargetCorners(Units.inchesToMeters(6)), Units.inchesToMeters(6)),
new Point3(Units.inchesToMeters(3), Units.inchesToMeters(3), 0),
new Point3(-Units.inchesToMeters(3), Units.inchesToMeters(3), 0),
new Point3(-Units.inchesToMeters(3), -Units.inchesToMeters(3), 0),
new Point3(Units.inchesToMeters(3), -Units.inchesToMeters(3), 0)),
Units.inchesToMeters(3 * 2)),
// 2024 AprilTag, with 6.5 inch marker width (inner black square). // 2024 AprilTag, with 6.5 inch marker width (inner black square).
// MIGRATION: 2023 // MIGRATION: 2023
@Json.Alias({"k6p5in_36h11", "k200mmAprilTag", "kAruco6p5in_36h11"}) @Json.Alias({"k6p5in_36h11", "k200mmAprilTag", "kAruco6p5in_36h11"})
kAprilTag6p5in_36h11( kAprilTag6p5in_36h11(
// Corners of the tag's inner black square (excluding white border) // Corners of the tag's inner black square (excluding white border)
List.of( squareTargetCorners(Units.inchesToMeters(6.5)), Units.inchesToMeters(6.5));
new Point3(-Units.inchesToMeters(6.5 / 2.0), Units.inchesToMeters(6.5 / 2.0), 0),
new Point3(Units.inchesToMeters(6.5 / 2.0), Units.inchesToMeters(6.5 / 2.0), 0),
new Point3(Units.inchesToMeters(6.5 / 2.0), -Units.inchesToMeters(6.5 / 2.0), 0),
new Point3(-Units.inchesToMeters(6.5 / 2.0), -Units.inchesToMeters(6.5 / 2.0), 0)),
Units.inchesToMeters(6.5));
@Json.Ignore private final MatOfPoint3f realWorldTargetCoordinates; @Json.Ignore private final MatOfPoint3f realWorldTargetCoordinates;
@Json.Ignore private final MatOfPoint3f visualizationBoxBottom = new MatOfPoint3f(); @Json.Ignore private final MatOfPoint3f visualizationBoxBottom = new MatOfPoint3f();
@Json.Ignore private final MatOfPoint3f visualizationBoxTop = new MatOfPoint3f(); @Json.Ignore private final MatOfPoint3f visualizationBoxTop = new MatOfPoint3f();
private List<Point3> realWorldCoordinatesArray;
private double boxHeight;
TargetModel(MatOfPoint3f realWorldTargetCoordinates, double boxHeight) { TargetModel(MatOfPoint3f realWorldTargetCoordinates, double boxHeight) {
this.realWorldTargetCoordinates = realWorldTargetCoordinates; this.realWorldTargetCoordinates = realWorldTargetCoordinates;
this.realWorldCoordinatesArray = realWorldTargetCoordinates.toList();
this.boxHeight = boxHeight;
var bottomList = realWorldTargetCoordinates.toList(); var bottomList = realWorldTargetCoordinates.toList();
var topList = new ArrayList<Point3>(); var topList = new ArrayList<Point3>();
@@ -175,22 +106,6 @@ public enum TargetModel implements Releasable {
this(listToMat(realWorldCoordinatesArray), boxHeight); this(listToMat(realWorldCoordinatesArray), boxHeight);
} }
public List<Point3> getRealWorldCoordinatesArray() {
return this.realWorldCoordinatesArray;
}
public double getBoxHeight() {
return boxHeight;
}
public void setRealWorldCoordinatesArray(List<Point3> realWorldCoordinatesArray) {
this.realWorldCoordinatesArray = realWorldCoordinatesArray;
}
public void setBoxHeight(double boxHeight) {
this.boxHeight = boxHeight;
}
private static MatOfPoint3f listToMat(List<Point3> points) { private static MatOfPoint3f listToMat(List<Point3> points) {
var mat = new MatOfPoint3f(); var mat = new MatOfPoint3f();
mat.fromList(points); mat.fromList(points);
@@ -209,15 +124,23 @@ public enum TargetModel implements Releasable {
return visualizationBoxTop; return visualizationBoxTop;
} }
// public static TargetModel getCircleTarget(double Units.inchesToMeters(7)) { private static List<Point3> circleTargetCorners(double diameter) {
// var corners = double radius = diameter / 2;
// List.of( return List.of(
// new Point3(-Units.inchesToMeters(7) / 2, -radius / 2, -radius / 2), new Point3(-radius, -radius, -radius),
// new Point3(-Units.inchesToMeters(7) / 2, radius / 2, -radius / 2), new Point3(-radius, radius, -radius),
// new Point3(Units.inchesToMeters(7) / 2, radius / 2, -radius / 2), new Point3(radius, radius, -radius),
// new Point3(Units.inchesToMeters(7) / 2, -radius / 2, -radius / 2)); new Point3(radius, -radius, -radius));
// return new TargetModel(corners, 0); }
// }
private static List<Point3> squareTargetCorners(double edgeLength) {
double radius = edgeLength / 2;
return List.of(
new Point3(-radius, -radius, 0),
new Point3(-radius, radius, 0),
new Point3(radius, radius, 0),
new Point3(radius, -radius, 0));
}
@Json.Value @Json.Value
@Override @Override

View File

@@ -62,7 +62,7 @@ public class TargetCalculationsTest {
null); null);
@BeforeAll @BeforeAll
public static void setup() { public static void init() {
LoadJNI.loadLibraries(); LoadJNI.loadLibraries();
} }

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) Photon Vision.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.photonvision.vision.target;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.List;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.opencv.core.Point3;
import org.photonvision.common.LoadJNI;
import org.wpilib.math.util.Units;
public class TargetModelTest {
@BeforeAll
public static void init() {
LoadJNI.loadLibraries();
}
@Test
void testCircleTargetGeneration() {
assertApproxEquals(
List.of(
new Point3(
-Units.inchesToMeters(7) / 2,
-Units.inchesToMeters(7) / 2,
-Units.inchesToMeters(7) / 2),
new Point3(
-Units.inchesToMeters(7) / 2,
Units.inchesToMeters(7) / 2,
-Units.inchesToMeters(7) / 2),
new Point3(
Units.inchesToMeters(7) / 2,
Units.inchesToMeters(7) / 2,
-Units.inchesToMeters(7) / 2),
new Point3(
Units.inchesToMeters(7) / 2,
-Units.inchesToMeters(7) / 2,
-Units.inchesToMeters(7) / 2)),
TargetModel.kCircularPowerCell7in.getRealWorldTargetCoordinates().toList(),
1E-6);
}
@Test
void testSquareTargetGeneration() {
assertApproxEquals(
List.of(
new Point3(-Units.inchesToMeters(6.5 / 2.0), -Units.inchesToMeters(6.5 / 2.0), 0),
new Point3(-Units.inchesToMeters(6.5 / 2.0), Units.inchesToMeters(6.5 / 2.0), 0),
new Point3(Units.inchesToMeters(6.5 / 2.0), Units.inchesToMeters(6.5 / 2.0), 0),
new Point3(Units.inchesToMeters(6.5 / 2.0), -Units.inchesToMeters(6.5 / 2.0), 0)),
TargetModel.kAprilTag6p5in_36h11.getRealWorldTargetCoordinates().toList(),
1E-6);
}
static void assertApproxEquals(List<Point3> expected, List<Point3> actual, double delta) {
assertEquals(expected.size(), actual.size());
for (int i = 0; i < actual.size(); i++) {
assertEquals(expected.get(i).x, actual.get(i).x, delta, "Bad x for point %d".formatted(i));
assertEquals(expected.get(i).y, actual.get(i).y, delta, "Bad y for point %d".formatted(i));
assertEquals(expected.get(i).z, actual.get(i).z, delta, "Bad z for point %d".formatted(i));
}
}
}

View File

@@ -20,7 +20,7 @@ package org.photonvision.vision.target;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.List; import java.util.List;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.opencv.core.MatOfPoint; import org.opencv.core.MatOfPoint;
import org.opencv.core.Point; import org.opencv.core.Point;
@@ -30,8 +30,8 @@ import org.photonvision.vision.opencv.Contour;
import org.photonvision.vision.opencv.DualOffsetValues; import org.photonvision.vision.opencv.DualOffsetValues;
public class TrackedTargetTest { public class TrackedTargetTest {
@BeforeEach @BeforeAll
public void Init() { public static void init() {
LoadJNI.loadLibraries(); LoadJNI.loadLibraries();
} }

View File

@@ -17,8 +17,10 @@
package org.photonvision.server; package org.photonvision.server;
import io.avaje.jsonb.javalin.JavalinJsonb;
import io.javalin.Javalin; import io.javalin.Javalin;
import io.javalin.plugin.bundled.CorsPlugin; import io.javalin.plugin.bundled.CorsPlugin;
import io.javalin.plugin.bundled.CorsPluginConfig.CorsRule;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.List; import java.util.List;
import java.util.StringJoiner; import java.util.StringJoiner;
@@ -60,14 +62,7 @@ public class Server {
javalinConfig -> { javalinConfig -> {
javalinConfig.showJavalinBanner = false; javalinConfig.showJavalinBanner = false;
javalinConfig.staticFiles.add("web"); javalinConfig.staticFiles.add("web");
javalinConfig.registerPlugin( javalinConfig.registerPlugin(new CorsPlugin(cors -> cors.addRule(CorsRule::anyHost)));
new CorsPlugin(
cors -> {
cors.addRule(
it -> {
it.anyHost();
});
}));
javalinConfig.requestLogger.http( javalinConfig.requestLogger.http(
(ctx, ms) -> { (ctx, ms) -> {
StringJoiner joiner = StringJoiner joiner =
@@ -103,6 +98,7 @@ public class Server {
return "Got WebSockets binary message from host: " + host; return "Got WebSockets binary message from host: " + host;
})); }));
}); });
javalinConfig.jsonMapper(new JavalinJsonb());
}); });
/* Web Socket Events for Data Exchange */ /* Web Socket Events for Data Exchange */

View File

@@ -37,6 +37,7 @@ dependencies {
implementation group: "io.avaje", name: "avaje-jsonb", version: avajeJsonbVersion implementation group: "io.avaje", name: "avaje-jsonb", version: avajeJsonbVersion
annotationProcessor group: "io.avaje", name: "avaje-jsonb-generator", version: avajeJsonbVersion annotationProcessor group: "io.avaje", name: "avaje-jsonb-generator", version: avajeJsonbVersion
implementation group: "io.avaje", name: "avaje-jsonb-jackson", version: avajeJsonbVersion implementation group: "io.avaje", name: "avaje-jsonb-jackson", version: avajeJsonbVersion
implementation group: "io.avaje", name: "avaje-jsonb-javalin-mapper", version: avajeJsonbVersion
implementation group: "org.ejml", name: "ejml-simple", version: ejmlVersion implementation group: "org.ejml", name: "ejml-simple", version: ejmlVersion
implementation group: "us.hebi.quickbuf", name: "quickbuf-runtime", version: quickbufVersion; implementation group: "us.hebi.quickbuf", name: "quickbuf-runtime", version: quickbufVersion;