mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-21 01:01:41 +00:00
Compare commits
50 Commits
v2022.1.1-
...
v2022.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72717cecf0 | ||
|
|
971ff3ac40 | ||
|
|
b80e436f02 | ||
|
|
be1a053cbe | ||
|
|
f4555dc545 | ||
|
|
54fdd1db51 | ||
|
|
1805785cc6 | ||
|
|
e62f6419b5 | ||
|
|
fa7824c616 | ||
|
|
9090aa6bcc | ||
|
|
5655ca6890 | ||
|
|
50fdfd8bce | ||
|
|
3120a6439b | ||
|
|
ab3e8c8db7 | ||
|
|
5144819ce2 | ||
|
|
d779fe23f0 | ||
|
|
b2a3f34433 | ||
|
|
b09a6d6a2d | ||
|
|
9893cf1f7e | ||
|
|
fc91daf397 | ||
|
|
a3e205cb6f | ||
|
|
553bed32b5 | ||
|
|
6c91feaf3f | ||
|
|
4ddb9aa08f | ||
|
|
4aadebdbb2 | ||
|
|
da8d70f887 | ||
|
|
80a0d8de1c | ||
|
|
3ad476bc28 | ||
|
|
e6d8e05b91 | ||
|
|
46fa17dfd8 | ||
|
|
43c35286f3 | ||
|
|
3d317f7035 | ||
|
|
a161bd5be9 | ||
|
|
0f730fc28d | ||
|
|
12e06b09c3 | ||
|
|
641101f574 | ||
|
|
6a1201432c | ||
|
|
e77a06bfa6 | ||
|
|
8b0b18bd07 | ||
|
|
1766f3bf0f | ||
|
|
4578fa756c | ||
|
|
d6e1e28fc2 | ||
|
|
49048c3998 | ||
|
|
8b079d9b20 | ||
|
|
1522adaa0e | ||
|
|
3cd57b8b43 | ||
|
|
c944967476 | ||
|
|
dbd631da61 | ||
|
|
f731ae37d2 | ||
|
|
0a8da1a0bd |
95
.github/workflows/main.yml
vendored
95
.github/workflows/main.yml
vendored
@@ -148,46 +148,11 @@ jobs:
|
||||
./gradlew spotlessCheck
|
||||
|
||||
|
||||
photon-release:
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
needs: [photon-build-package]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: jar
|
||||
|
||||
- run: |
|
||||
NEW_JAR=$(realpath $(find . -name photonvision\*.jar))
|
||||
sudo apt install unzip zip
|
||||
curl -sk https://api.github.com/repos/photonvision/photon-pi-gen/releases/latest | grep "browser_download_url.*zip" | cut -d : -f 2,3 | tr -d '"' | wget -qi -
|
||||
FILE_NAME=$(ls | grep image_*.zip)
|
||||
unzip $FILE_NAME
|
||||
IMAGE_FILE=$(ls | grep *.img)
|
||||
TMP=$(mktemp -d)
|
||||
LOOP=$(sudo losetup --show -fP "${IMAGE_FILE}")
|
||||
sudo mount ${LOOP}p2 $TMP
|
||||
pushd .
|
||||
cd $TMP/opt/photonvision
|
||||
ls
|
||||
sudo cp $NEW_JAR photonvision.jar
|
||||
popd
|
||||
sudo umount ${TMP}
|
||||
sudo rmdir ${TMP}
|
||||
rm $FILE_NAME
|
||||
NEW_IMAGE=$(basename "${NEW_JAR/jar/img}")
|
||||
mv $IMAGE_FILE $NEW_IMAGE
|
||||
zip -r $(basename "${NEW_JAR/jar/zip}") $NEW_IMAGE
|
||||
rm $NEW_IMAGE
|
||||
|
||||
- uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: '**/*'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Building photonlib
|
||||
photonlib-build-host:
|
||||
env:
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.14
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -316,34 +281,18 @@ jobs:
|
||||
name: built-docs
|
||||
path: photon-server/src/main/resources/web/docs
|
||||
|
||||
# Build fat jar.
|
||||
# Build fat jar for both pi and everything
|
||||
- run: |
|
||||
chmod +x gradlew
|
||||
./gradlew photon-server:shadowJar --max-workers 1
|
||||
./gradlew photon-server:shadowJar --max-workers 1 -Ppionly
|
||||
|
||||
# The image will only pull the Pi JAR in
|
||||
- name: Generate image
|
||||
if: github.event_name != 'pull_request'
|
||||
run: |
|
||||
NEW_JAR=$(realpath $(find . -name photonvision\*.jar))
|
||||
sudo apt install unzip zip
|
||||
curl -sk https://api.github.com/repos/photonvision/photon-pi-gen/releases/latest | grep "browser_download_url.*zip" | cut -d : -f 2,3 | tr -d '"' | wget -qi -
|
||||
FILE_NAME=$(ls | grep image_*.zip)
|
||||
unzip $FILE_NAME
|
||||
IMAGE_FILE=$(ls | grep *.img)
|
||||
TMP=$(mktemp -d)
|
||||
LOOP=$(sudo losetup --show -fP "${IMAGE_FILE}")
|
||||
sudo mount ${LOOP}p2 $TMP
|
||||
pushd .
|
||||
cd $TMP/opt/photonvision
|
||||
ls
|
||||
sudo cp $NEW_JAR photonvision.jar
|
||||
popd
|
||||
sudo umount ${TMP}
|
||||
sudo rmdir ${TMP}
|
||||
rm $FILE_NAME
|
||||
NEW_IMAGE=$(basename "${NEW_JAR/jar/img}")
|
||||
mv $IMAGE_FILE $NEW_IMAGE
|
||||
zip -r $(basename "${NEW_JAR/jar/zip}") $NEW_IMAGE
|
||||
rm $NEW_IMAGE
|
||||
chmod +x scripts/generatePiImage.sh
|
||||
./scripts/generatePiImage.sh
|
||||
|
||||
# Upload final fat jar as artifact.
|
||||
- uses: actions/upload-artifact@master
|
||||
@@ -353,14 +302,36 @@ jobs:
|
||||
- uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: image
|
||||
path: image*.zip
|
||||
path: photonvision*.zip
|
||||
|
||||
- uses: eine/tip@master
|
||||
- uses: pyTooling/Actions/releaser@r0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: 'Dev'
|
||||
rm: true
|
||||
files: |
|
||||
photon-server/build/libs/*.jar
|
||||
image*.zip
|
||||
photonvision*.zip
|
||||
if: github.event_name == 'push'
|
||||
|
||||
photon-release:
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
needs: [photon-build-package]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# This *should* pull in fat and pi-only jars
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: jar
|
||||
|
||||
# And the image we made previously
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: image
|
||||
|
||||
# All we've downloaded (ideally) is the fat jar, pi jar, and image. So just upload it all
|
||||
- uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: '**/*'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -143,3 +143,4 @@ build/*
|
||||
build
|
||||
photon-lib/src/main/java/org/photonvision/PhotonVersion.java
|
||||
/photonlib-java-examples/bin/
|
||||
photon-lib/src/generate/native/include/PhotonVersion.h
|
||||
|
||||
@@ -25,7 +25,3 @@ includeOtherLibs {
|
||||
^units/
|
||||
^wpi/
|
||||
}
|
||||
|
||||
licenseUpdateExclude {
|
||||
\.java$
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
17
README.md
17
README.md
@@ -10,8 +10,17 @@ If you are interested in contributing code or documentation to the project, plea
|
||||
|
||||
## Authors
|
||||
|
||||
A list of contributors is available in our documentation on ReadTheDocs.
|
||||
<a href="https://github.com/PhotonVision/photonvision/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=PhotonVision/photonvision" />
|
||||
</a>
|
||||
|
||||
## Gradle Arguments
|
||||
|
||||
Note that these are case sensitive!
|
||||
|
||||
* `-Ppionly`: only builds for `linuxraspbian`, which reduces JAR size. The JAR name will have "-raspi" appended.
|
||||
- `-PtgtIp`: deploys (builds and copies the JAR) to the coprocessor at the specified IP
|
||||
- `-Pprofile`: enables JVM profiling
|
||||
|
||||
## Acknowledgments
|
||||
PhotonVision was forked from [Chameleon Vision](https://github.com/Chameleon-Vision/chameleon-vision/). Thank you to everyone who worked on the original project.
|
||||
@@ -29,3 +38,9 @@ PhotonVision was forked from [Chameleon Vision](https://github.com/Chameleon-Vis
|
||||
|
||||
## License
|
||||
PhotonVision is licensed under the [GNU General Public License](https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
## Meeting Notes
|
||||
Our meeting notes can be found in the wiki section of this repository.
|
||||
|
||||
* [2020 Meeting Notes](https://github.com/PhotonVision/photonvision/wiki/2020-Meeting-Notes)
|
||||
* [2021 Meeting Notes](https://github.com/PhotonVision/photonvision/wiki/2021-Meeting-Notes)
|
||||
|
||||
23
build.gradle
23
build.gradle
@@ -1,10 +1,10 @@
|
||||
plugins {
|
||||
id "com.diffplug.gradle.spotless" version "3.28.0"
|
||||
id "com.github.johnrengelman.shadow" version "5.2.0"
|
||||
id "com.github.node-gradle.node" version "2.2.4" apply false
|
||||
id "com.diffplug.spotless" version "6.1.2"
|
||||
id "com.github.johnrengelman.shadow" version "7.1.2"
|
||||
id "com.github.node-gradle.node" version "3.1.1" apply false
|
||||
id "edu.wpi.first.GradleJni" version "1.0.0"
|
||||
id "edu.wpi.first.GradleVsCode" version "1.0.0"
|
||||
id "edu.wpi.first.NativeUtils" version "2022.4.4" apply false
|
||||
id "edu.wpi.first.GradleVsCode" version "1.1.0"
|
||||
id "edu.wpi.first.NativeUtils" version "2022.8.1" apply false
|
||||
id "edu.wpi.first.wpilib.repositories.WPILibRepositoriesPlugin" version "2020.2"
|
||||
id "org.hidetake.ssh" version "2.10.1"
|
||||
}
|
||||
@@ -22,24 +22,31 @@ allprojects {
|
||||
apply from: "versioningHelper.gradle"
|
||||
|
||||
ext {
|
||||
wpilibVersion = "2022.1.1-beta-3-1-g4ba80a3"
|
||||
wpilibVersion = "2022.1.1"
|
||||
opencvVersion = "4.5.2-1"
|
||||
joglVersion = "2.4.0-rc-20200307"
|
||||
pubVersion = versionString
|
||||
isDev = pubVersion.startsWith("dev")
|
||||
|
||||
|
||||
jniPlatforms = project.hasProperty('pionly') ? ['linuxraspbian']
|
||||
: ['linuxaarch64bionic', 'linuxraspbian', 'linuxx86-64', 'osxx86-64', 'windowsx86-64']
|
||||
|
||||
println("Building for archs " + jniPlatforms)
|
||||
}
|
||||
|
||||
spotless {
|
||||
java {
|
||||
toggleOffOn()
|
||||
googleJavaFormat()
|
||||
paddedCell()
|
||||
indentWithTabs(2)
|
||||
indentWithSpaces(4)
|
||||
removeUnusedImports()
|
||||
trimTrailingWhitespace()
|
||||
endWithNewline()
|
||||
}
|
||||
java {
|
||||
target "**/*.java"
|
||||
licenseHeaderFile "$rootDir/LicenseHeader.txt"
|
||||
targetExclude("photon-core/src/main/java/org/photonvision/PhotonVersion.java")
|
||||
targetExclude("photon-lib/src/main/java/org/photonvision/PhotonVersion.java")
|
||||
}
|
||||
|
||||
8
gradle.properties
Normal file
8
gradle.properties
Normal file
@@ -0,0 +1,8 @@
|
||||
# The --add-exports flags work around a bug with spotless and JDK 17
|
||||
# https://github.com/diffplug/spotless/issues/834
|
||||
org.gradle.jvmargs= \
|
||||
--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
|
||||
--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \
|
||||
--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \
|
||||
--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \
|
||||
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
|
||||
96
photon-client/package-lock.json
generated
96
photon-client/package-lock.json
generated
@@ -12792,11 +12792,10 @@
|
||||
}
|
||||
},
|
||||
"vue-loader-v16": {
|
||||
"version": "npm:vue-loader@16.8.2",
|
||||
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.8.2.tgz",
|
||||
"integrity": "sha512-Nkq+z9mAsMEK+qkXgK+9Ia7D8w9uu9j4ut0IMT5coMfux3rCgIp1QBB1CYwY0M34A1nRMSONEaWXxAAw6xSl/Q==",
|
||||
"version": "npm:vue-loader@16.8.3",
|
||||
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.8.3.tgz",
|
||||
"integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"chalk": "^4.1.0",
|
||||
"hash-sum": "^2.0.0",
|
||||
@@ -12808,7 +12807,6 @@
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
@@ -12818,7 +12816,6 @@
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
@@ -12829,7 +12826,6 @@
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
@@ -12838,39 +12834,34 @@
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"emojis-list": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
|
||||
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"json5": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
|
||||
"integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
},
|
||||
"loader-utils": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.1.tgz",
|
||||
"integrity": "sha512-g4miPa9uUrZz4iElkaVJgDFwKJGh8aQGM7pUL4ejXl6cu7kSb30seQOVGNMP6sW8j7DW77X68hJZ+GM7UGhXeQ==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
|
||||
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
"emojis-list": "^3.0.0",
|
||||
@@ -12882,7 +12873,6 @@
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
@@ -12937,7 +12927,7 @@
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "http://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
|
||||
"integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo="
|
||||
}
|
||||
}
|
||||
@@ -13841,7 +13831,7 @@
|
||||
},
|
||||
"browserify-aes": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
|
||||
"integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
|
||||
"requires": {
|
||||
"buffer-xor": "^1.0.3",
|
||||
@@ -13875,7 +13865,7 @@
|
||||
},
|
||||
"browserify-rsa": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
|
||||
"integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
|
||||
"requires": {
|
||||
"bn.js": "^4.1.0",
|
||||
@@ -13906,7 +13896,7 @@
|
||||
},
|
||||
"buffer": {
|
||||
"version": "4.9.1",
|
||||
"resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
|
||||
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
|
||||
"requires": {
|
||||
"base64-js": "^1.0.2",
|
||||
@@ -14042,7 +14032,7 @@
|
||||
},
|
||||
"chalk": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
|
||||
"requires": {
|
||||
"ansi-styles": "^2.2.1",
|
||||
@@ -14289,7 +14279,7 @@
|
||||
},
|
||||
"create-hash": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
|
||||
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
|
||||
"requires": {
|
||||
"cipher-base": "^1.0.1",
|
||||
@@ -14301,7 +14291,7 @@
|
||||
},
|
||||
"create-hmac": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
|
||||
"resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
|
||||
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
|
||||
"requires": {
|
||||
"cipher-base": "^1.0.3",
|
||||
@@ -14508,7 +14498,7 @@
|
||||
},
|
||||
"diffie-hellman": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
|
||||
"integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
|
||||
"requires": {
|
||||
"bn.js": "^4.1.0",
|
||||
@@ -14584,7 +14574,7 @@
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "http://registry.npmjs.org/debug/-/debug-2.3.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz",
|
||||
"integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=",
|
||||
"requires": {
|
||||
"ms": "0.7.2"
|
||||
@@ -14623,7 +14613,7 @@
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "http://registry.npmjs.org/debug/-/debug-2.3.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz",
|
||||
"integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=",
|
||||
"requires": {
|
||||
"ms": "0.7.2"
|
||||
@@ -16074,7 +16064,7 @@
|
||||
},
|
||||
"http-errors": {
|
||||
"version": "1.6.3",
|
||||
"resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
|
||||
"integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
|
||||
"requires": {
|
||||
"depd": "~1.1.2",
|
||||
@@ -16560,7 +16550,7 @@
|
||||
"dependencies": {
|
||||
"lodash": {
|
||||
"version": "3.10.1",
|
||||
"resolved": "http://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
|
||||
"integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y="
|
||||
}
|
||||
}
|
||||
@@ -16596,7 +16586,7 @@
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||
}
|
||||
}
|
||||
@@ -16805,7 +16795,7 @@
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "1.0.34",
|
||||
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
|
||||
"integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
@@ -16964,7 +16954,7 @@
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||
},
|
||||
"path-exists": {
|
||||
@@ -17091,7 +17081,7 @@
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
|
||||
},
|
||||
"mixin-deep": {
|
||||
@@ -17115,7 +17105,7 @@
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
@@ -17142,7 +17132,7 @@
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "http://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
|
||||
"integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=",
|
||||
"requires": {
|
||||
"graceful-readlink": ">= 1.0.0"
|
||||
@@ -17429,7 +17419,7 @@
|
||||
},
|
||||
"onetime": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
|
||||
"integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k="
|
||||
},
|
||||
"optimist": {
|
||||
@@ -17534,7 +17524,7 @@
|
||||
},
|
||||
"parse-asn1": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz",
|
||||
"integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==",
|
||||
"requires": {
|
||||
"asn1.js": "^4.0.0",
|
||||
@@ -17757,7 +17747,7 @@
|
||||
},
|
||||
"public-encrypt": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "http://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz",
|
||||
"resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz",
|
||||
"integrity": "sha512-4kJ5Esocg8X3h8YgJsKAuoesBgB7mqH3eowiDzMUPKiRDDE7E/BqqZD1hnTByIaAFiwAw246YEltSq7tdrOH0Q==",
|
||||
"requires": {
|
||||
"bn.js": "^4.1.0",
|
||||
@@ -17873,7 +17863,7 @@
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
@@ -18186,7 +18176,7 @@
|
||||
},
|
||||
"sha.js": {
|
||||
"version": "2.4.11",
|
||||
"resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
|
||||
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
|
||||
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.1",
|
||||
@@ -18364,7 +18354,7 @@
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "http://registry.npmjs.org/debug/-/debug-2.3.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz",
|
||||
"integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=",
|
||||
"requires": {
|
||||
"ms": "0.7.2"
|
||||
@@ -18393,7 +18383,7 @@
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "http://registry.npmjs.org/debug/-/debug-2.3.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz",
|
||||
"integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=",
|
||||
"requires": {
|
||||
"ms": "0.7.2"
|
||||
@@ -18431,7 +18421,7 @@
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "http://registry.npmjs.org/debug/-/debug-2.3.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz",
|
||||
"integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=",
|
||||
"requires": {
|
||||
"ms": "0.7.2"
|
||||
@@ -18457,7 +18447,7 @@
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
|
||||
"integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
|
||||
"requires": {
|
||||
"ms": "0.7.1"
|
||||
@@ -18652,7 +18642,7 @@
|
||||
},
|
||||
"table": {
|
||||
"version": "3.8.3",
|
||||
"resolved": "http://registry.npmjs.org/table/-/table-3.8.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz",
|
||||
"integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=",
|
||||
"requires": {
|
||||
"ajv": "^4.7.0",
|
||||
@@ -18709,7 +18699,7 @@
|
||||
},
|
||||
"through": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
|
||||
},
|
||||
"time-stamp": {
|
||||
@@ -18885,7 +18875,7 @@
|
||||
},
|
||||
"yargs": {
|
||||
"version": "3.10.0",
|
||||
"resolved": "http://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
|
||||
"integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
|
||||
"requires": {
|
||||
"camelcase": "^1.0.2",
|
||||
@@ -19527,7 +19517,7 @@
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
|
||||
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
|
||||
"requires": {
|
||||
"string-width": "^1.0.1",
|
||||
@@ -20438,9 +20428,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"y18n": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
|
||||
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
||||
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
|
||||
@@ -87,31 +87,56 @@
|
||||
</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Advanced Mode</v-list-item-title>
|
||||
<v-list-item-title>Compact Mode</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
|
||||
<div style="position: absolute; bottom: 0; left: 0;">
|
||||
<v-list-item>
|
||||
<v-list-item-icon>
|
||||
<v-icon v-if="$store.state.settings.networkSettings.runNTServer">mdi-server</v-icon>
|
||||
<img v-else-if="$store.state.ntConnectionInfo.connected" src="@/assets/robot.svg" alt="">
|
||||
<img v-else class="pulse" style="border-radius: 100%" src="@/assets/robot-off.svg" alt="">
|
||||
<v-icon v-if="$store.state.settings.networkSettings.runNTServer">
|
||||
mdi-server
|
||||
</v-icon>
|
||||
<img
|
||||
v-else-if="$store.state.ntConnectionInfo.connected"
|
||||
src="@/assets/robot.svg"
|
||||
alt=""
|
||||
>
|
||||
<img
|
||||
v-else
|
||||
class="pulse"
|
||||
style="border-radius: 100%"
|
||||
src="@/assets/robot-off.svg"
|
||||
alt=""
|
||||
>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title class="text-wrap" v-if="$store.state.settings.networkSettings.runNTServer">
|
||||
NetworkTables server running for {{$store.state.ntConnectionInfo.clients ? $store.state.ntConnectionInfo.clients : 'zero'}} clients!
|
||||
<v-list-item-title
|
||||
v-if="$store.state.settings.networkSettings.runNTServer"
|
||||
class="text-wrap"
|
||||
>
|
||||
NetworkTables server running for {{ $store.state.ntConnectionInfo.clients ? $store.state.ntConnectionInfo.clients : 'zero' }} clients!
|
||||
</v-list-item-title>
|
||||
<v-list-item-title class="text-wrap" v-else-if="$store.state.ntConnectionInfo.connected && $store.state.backendConnected">
|
||||
Robot connected! {{$store.state.ntConnectionInfo.address}}
|
||||
<v-list-item-title
|
||||
v-else-if="$store.state.ntConnectionInfo.connected && $store.state.backendConnected"
|
||||
class="text-wrap"
|
||||
>
|
||||
Robot connected! {{ $store.state.ntConnectionInfo.address }}
|
||||
</v-list-item-title>
|
||||
<v-list-item-title class="text-wrap" v-else>
|
||||
<v-list-item-title
|
||||
v-else
|
||||
class="text-wrap"
|
||||
>
|
||||
Not connected to robot!
|
||||
</v-list-item-title>
|
||||
<a
|
||||
href="/#/settings"
|
||||
style="color:#FFD843"
|
||||
>{{"Team: " + $store.state.settings.networkSettings.teamNumber}}</a>
|
||||
<router-link
|
||||
v-if="!$store.state.settings.networkSettings.runNTServer"
|
||||
to="settings"
|
||||
class="accent--text"
|
||||
@click="switchToSettingsTab"
|
||||
>
|
||||
Team number is {{ $store.state.settings.networkSettings.teamNumber }}
|
||||
</router-link>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
|
||||
@@ -121,9 +146,9 @@
|
||||
mdi-wifi
|
||||
</v-icon>
|
||||
<v-icon
|
||||
v-else
|
||||
class="pulse"
|
||||
style="border-radius: 100%;"
|
||||
v-else
|
||||
class="pulse"
|
||||
style="border-radius: 100%;"
|
||||
>
|
||||
mdi-wifi-off
|
||||
</v-icon>
|
||||
@@ -135,7 +160,6 @@
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</div>
|
||||
|
||||
</v-list>
|
||||
</v-navigation-drawer>
|
||||
<v-main>
|
||||
@@ -159,23 +183,27 @@
|
||||
<logs />
|
||||
</v-dialog>
|
||||
<v-dialog
|
||||
v-model="needsTeamNumberSet"
|
||||
width="500"
|
||||
dark
|
||||
persistent
|
||||
v-model="needsTeamNumberSet"
|
||||
width="500"
|
||||
dark
|
||||
persistent
|
||||
>
|
||||
<v-card
|
||||
dark
|
||||
color="primary"
|
||||
flat
|
||||
dark
|
||||
color="primary"
|
||||
flat
|
||||
>
|
||||
<v-card-title>No team number set!</v-card-title>
|
||||
<v-card-text>
|
||||
PhotonVision cannot connect to your robot! Please
|
||||
<a
|
||||
href="/#/settings"
|
||||
style="color:#FFD843"
|
||||
>head to the settings page</a> and set your team number.
|
||||
<router-link
|
||||
to="settings"
|
||||
class="accent--text"
|
||||
@click="switchToSettingsTab"
|
||||
>
|
||||
vist the settings tab
|
||||
</router-link>
|
||||
and set your team number.
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
@@ -219,12 +247,6 @@ export default {
|
||||
localStorage.setItem("compactMode", value);
|
||||
},
|
||||
},
|
||||
// ...mapState({
|
||||
// ntServerMode: state => state.settings.networkSettings.runNTServer,
|
||||
// ntClients: state => state.ntConnectionInfo.clients,
|
||||
// ntConnected: state => state.ntConnectionInfo.connected,
|
||||
// backendConnected: state => state.backendConnected
|
||||
// })
|
||||
},
|
||||
created() {
|
||||
document.addEventListener("keydown", e => {
|
||||
@@ -319,8 +341,7 @@ export default {
|
||||
}
|
||||
}
|
||||
this.previouslySelectedIndices = null;
|
||||
}
|
||||
,
|
||||
},
|
||||
switchToSettingsTab() {
|
||||
this.axios.post('http://' + this.$address + '/api/sendMetrics', {})
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
export default {
|
||||
name: "CvImage",
|
||||
// eslint-disable-next-line vue/require-prop-types
|
||||
props: ['address', 'scale', 'maxHeight', 'maxHeightMd', 'maxHeightXl', 'colorPicking', 'id', 'disconnected'],
|
||||
props: ['address', 'scale', 'maxHeight', 'maxHeightMd', 'maxHeightLg', 'maxHeightXl', 'colorPicking', 'id', 'disconnected'],
|
||||
data() {
|
||||
return {
|
||||
seed: 1.0,
|
||||
@@ -37,7 +37,9 @@
|
||||
|
||||
if (this.$vuetify.breakpoint.xl) {
|
||||
ret["max-height"] = this.maxHeightXl;
|
||||
} else if (this.$vuetify.breakpoint.mdAndUp) {
|
||||
} else if (this.$vuetify.breakpoint.lg) {
|
||||
ret["max-height"] = this.maxHeightLg;
|
||||
} else if (this.$vuetify.breakpoint.md) {
|
||||
ret["max-height"] = this.maxHeightMd;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,28 +1,46 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-radio-group
|
||||
v-model="localValue"
|
||||
row
|
||||
dark
|
||||
:mandatory="true"
|
||||
<v-row
|
||||
dense
|
||||
align="center"
|
||||
>
|
||||
<v-radio
|
||||
v-for="(name,index) in list"
|
||||
:key="index"
|
||||
color="#ffd843"
|
||||
:label="name"
|
||||
:value="index"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
</v-radio-group>
|
||||
<v-col :cols="12 - (inputCols || 8)">
|
||||
<tooltipped-label
|
||||
:tooltip="tooltip"
|
||||
:text="name"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col :cols="inputCols || 8">
|
||||
<v-radio-group
|
||||
v-model="localValue"
|
||||
row
|
||||
dark
|
||||
:mandatory="true"
|
||||
>
|
||||
<v-radio
|
||||
v-for="(radioName,index) in list"
|
||||
:key="index"
|
||||
color="#ffd843"
|
||||
:label="radioName"
|
||||
:value="index"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
</v-radio-group>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TooltippedLabel from "./cv-tooltipped-label";
|
||||
|
||||
export default {
|
||||
name: 'Radio',
|
||||
components: {
|
||||
TooltippedLabel
|
||||
},
|
||||
// eslint-disable-next-line vue/require-prop-types
|
||||
props: ['value', 'list', 'disabled'],
|
||||
props: ['name', 'value', 'list', 'disabled', 'inputCols', 'tooltip'],
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
hide-details
|
||||
class="align-center"
|
||||
dark
|
||||
color="accent"
|
||||
:color="inverted ? 'rgba(255, 255, 255, 0.2)' : 'accent'"
|
||||
:track-color="inverted ? 'accent' : undefined"
|
||||
thumb-color="accent"
|
||||
:step="step"
|
||||
@input="handleInput"
|
||||
@mousedown="$emit('rollback', localValue)"
|
||||
@@ -76,7 +78,7 @@ export default {
|
||||
TooltippedLabel,
|
||||
},
|
||||
// eslint-disable-next-line vue/require-prop-types
|
||||
props: ["name", "min", "max", "value", "step", "tooltip", "disabled"],
|
||||
props: ["name", "min", "max", "value", "step", "tooltip", "disabled", "inverted"],
|
||||
data() {
|
||||
return {
|
||||
prependFocused: false,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<v-tooltip
|
||||
:disabled="tooltip === undefined"
|
||||
right
|
||||
open-delay="600"
|
||||
open-delay="300"
|
||||
>
|
||||
<template v-slot:activator="{ on, attrs }">
|
||||
<span
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-row align="center">
|
||||
<v-row
|
||||
align="center"
|
||||
class="pl-6"
|
||||
>
|
||||
<v-col
|
||||
cols="10"
|
||||
md="5"
|
||||
lg="10"
|
||||
class="pt-0 pb-0 pl-6"
|
||||
no-gutters
|
||||
class="pa-0"
|
||||
>
|
||||
<CVselect
|
||||
v-if="isCameraNameEdit === false"
|
||||
@@ -59,7 +63,8 @@
|
||||
cols="10"
|
||||
md="5"
|
||||
lg="10"
|
||||
class="pt-0 pb-0 pl-6"
|
||||
no-gutters
|
||||
class="pa-0"
|
||||
>
|
||||
<CVselect
|
||||
v-model="currentPipelineIndex"
|
||||
@@ -139,14 +144,16 @@
|
||||
<v-col
|
||||
v-if="currentPipelineType >= 0"
|
||||
cols="10"
|
||||
md="5"
|
||||
md="11"
|
||||
lg="10"
|
||||
class="pt-0 pb-0 pl-6 ml-16"
|
||||
no-gutters
|
||||
class="pa-0"
|
||||
>
|
||||
<CVselect
|
||||
v-model="currentPipelineType"
|
||||
name="Type"
|
||||
:list="['Reflective', 'Shape']"
|
||||
tooltip="Changes the pipeline type, which changes the type of processing that will happen on input frames"
|
||||
:list="['Reflective Tape', 'Colored Shape']"
|
||||
@input="e => showTypeDialog(e)"
|
||||
/>
|
||||
</v-col>
|
||||
@@ -173,15 +180,9 @@
|
||||
<v-card-text>
|
||||
<CVinput
|
||||
v-model="newPipelineName"
|
||||
name="Pipeline"
|
||||
name="Name"
|
||||
:error-message="checkPipelineName"
|
||||
/>
|
||||
<CVselect
|
||||
v-model="newPipelineType"
|
||||
name="Pipeline Type"
|
||||
:list="['Reflective', 'Shape']"
|
||||
:disabled="isPipelineNameEdit"
|
||||
/>
|
||||
</v-card-text>
|
||||
<v-divider />
|
||||
<v-card-actions>
|
||||
@@ -263,7 +264,6 @@ export default {
|
||||
isPipelineNameEdit: false,
|
||||
namingDialog: false,
|
||||
newPipelineName: "",
|
||||
newPipelineType: 0,
|
||||
duplicateDialog: false,
|
||||
showPipeTypeDialog: false,
|
||||
proposedPipelineType : 0,
|
||||
@@ -277,12 +277,12 @@ export default {
|
||||
for (let cam in this.cameraList) {
|
||||
if (this.cameraList.hasOwnProperty(cam)) {
|
||||
if (this.newCameraName === this.cameraList[cam]) {
|
||||
return "A camera by that name already Exists"
|
||||
return "A camera by that name already exists"
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return "A camera name can only contain letters, numbers and spaces"
|
||||
return "A camera name can only contain letters, numbers, and spaces"
|
||||
}
|
||||
}
|
||||
return "";
|
||||
@@ -385,7 +385,7 @@ export default {
|
||||
if (this.isPipelineNameEdit) {
|
||||
this.handleInputWithIndex("changePipelineName", this.newPipelineName);
|
||||
} else {
|
||||
this.handleInputWithIndex("addNewPipeline", [this.newPipelineName, this.newPipelineType]); // 0 for reflective, 1 for colored shpae
|
||||
this.handleInputWithIndex("addNewPipeline", [this.newPipelineName, this.currentPipelineType]); // 0 for reflective, 1 for colored shpae
|
||||
}
|
||||
this.discardPipelineNameChange();
|
||||
}
|
||||
|
||||
@@ -6,8 +6,10 @@ function initColorPicker() {
|
||||
canvas = document.createElement('canvas');
|
||||
|
||||
image = document.querySelector('#normal-stream');
|
||||
canvas.width = image.width;
|
||||
canvas.height = image.height;
|
||||
if (image !== null) {
|
||||
canvas.width = image.width;
|
||||
canvas.height = image.height;
|
||||
}
|
||||
}
|
||||
|
||||
//Called on click of the image,
|
||||
|
||||
@@ -19,6 +19,8 @@ export default new Vuex.Store({
|
||||
connected: false,
|
||||
address: "",
|
||||
clients: 0,
|
||||
},
|
||||
networkInfo: {
|
||||
possibleRios: ["Loading..."],
|
||||
deviceips: ["Loading..."],
|
||||
},
|
||||
@@ -55,7 +57,8 @@ export default new Vuex.Store({
|
||||
// Settings that apply to all pipeline types
|
||||
cameraExposure: 1,
|
||||
cameraBrightness: 2,
|
||||
cameraGain: 3,
|
||||
cameraRedGain: 3,
|
||||
cameraBlueGain: 4,
|
||||
inputImageRotationMode: 0,
|
||||
cameraVideoModeIndex: 0,
|
||||
streamingFrameDivisor: 0,
|
||||
@@ -64,10 +67,13 @@ export default new Vuex.Store({
|
||||
hsvHue: [0, 15],
|
||||
hsvSaturation: [0, 15],
|
||||
hsvValue: [0, 25],
|
||||
hueInverted: false,
|
||||
contourArea: [0, 12],
|
||||
contourRatio: [0, 12],
|
||||
contourFullness: [0, 12],
|
||||
contourSpecklePercentage: 5,
|
||||
contourFilterRangeX: 5,
|
||||
contourFilterRangeY: 5,
|
||||
contourGroupingMode: 0,
|
||||
contourIntersection: 0,
|
||||
contourSortMode: 0,
|
||||
@@ -151,6 +157,7 @@ export default new Vuex.Store({
|
||||
calibrationData: set('calibrationData'),
|
||||
metrics: set('metrics'),
|
||||
ntConnectionInfo: set('ntConnectionInfo'),
|
||||
networkInfo: set('networkInfo'),
|
||||
backendConnected: set('backendConnected'),
|
||||
logString: (state, newStr) => {
|
||||
const str = state.logMessages;
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
<CVselect
|
||||
v-model="currentCameraIndex"
|
||||
name="Camera"
|
||||
select-cols="10"
|
||||
:list="$store.getters.cameraList"
|
||||
:select-cols="$vuetify.breakpoint.mdAndUp ? 10 : 7"
|
||||
@input="handleInput('currentCamera',currentCameraIndex)"
|
||||
/>
|
||||
<CVnumberinput
|
||||
@@ -28,6 +28,7 @@
|
||||
:tooltip="cameraSettings.isFovConfigurable ? 'Field of view (in degrees) of the camera measured across the diagonal of the frame, in a video mode which covers the whole sensor area.' : 'This setting is managed by a vendor'"
|
||||
name="Maximum diagonal FOV"
|
||||
:disabled="!cameraSettings.isFovConfigurable"
|
||||
:label-cols="$vuetify.breakpoint.mdAndUp ? undefined : 7"
|
||||
/>
|
||||
<br>
|
||||
<CVnumberinput
|
||||
@@ -35,6 +36,7 @@
|
||||
name="Camera pitch"
|
||||
tooltip="How many degrees above the horizontal the physical camera is tilted"
|
||||
:step="0.01"
|
||||
:label-cols="$vuetify.breakpoint.mdAndUp ? undefined : 7"
|
||||
/>
|
||||
<br>
|
||||
<v-btn
|
||||
@@ -89,26 +91,26 @@
|
||||
<CVnumberinput
|
||||
v-model="squareSizeIn"
|
||||
name="Pattern Spacing (in)"
|
||||
label-cols="5"
|
||||
tooltip="Spacing between pattern features in inches"
|
||||
:disabled="isCalibrating"
|
||||
:rules="[v => (v > 0) || 'Size must be positive']"
|
||||
:label-cols="$vuetify.breakpoint.mdAndUp ? 5 : 7"
|
||||
/>
|
||||
<CVnumberinput
|
||||
v-model="boardWidth"
|
||||
name="Board width"
|
||||
label-cols="5"
|
||||
tooltip="Width of the board in dots or chessboard squares"
|
||||
:disabled="isCalibrating"
|
||||
:rules="[v => (v >= 4) || 'Width must be at least 4']"
|
||||
:label-cols="$vuetify.breakpoint.mdAndUp ? 5 : 7"
|
||||
/>
|
||||
<CVnumberinput
|
||||
v-model="boardHeight"
|
||||
name="Board height"
|
||||
label-cols="5"
|
||||
tooltip="Height of the board in dots or chessboard squares"
|
||||
:disabled="isCalibrating"
|
||||
:rules="[v => (v >= 4) || 'Height must be at least 4']"
|
||||
:label-cols="$vuetify.breakpoint.mdAndUp ? 5 : 7"
|
||||
/>
|
||||
</v-form>
|
||||
</v-col>
|
||||
@@ -194,13 +196,24 @@
|
||||
@input="e => handlePipelineUpdate('cameraBrightness', e)"
|
||||
/>
|
||||
<CVslider
|
||||
v-if="$store.getters.currentPipelineSettings.cameraGain !== -1"
|
||||
v-model="$store.getters.currentPipelineSettings.cameraGain"
|
||||
name="Gain"
|
||||
:min="0"
|
||||
:max="100"
|
||||
slider-cols="8"
|
||||
@input="e => handlePipelineUpdate('cameraGain', e)"
|
||||
v-if="$store.getters.currentPipelineSettings.cameraRedGain !== -1"
|
||||
v-model="$store.getters.currentPipelineSettings.cameraRedGain"
|
||||
name="Red AWB Gain"
|
||||
min="0"
|
||||
max="100"
|
||||
tooltip="Controls red automatic white balance gain, which affects how the camera captures colors in different conditions"
|
||||
:slider-cols="8"
|
||||
@input="e => handlePipelineData('cameraRedGain', e)"
|
||||
/>
|
||||
<CVslider
|
||||
v-if="$store.getters.currentPipelineSettings.cameraBlueGain !== -1"
|
||||
v-model="$store.getters.currentPipelineSettings.cameraBlueGain"
|
||||
name="Blue AWB Gain"
|
||||
min="0"
|
||||
max="100"
|
||||
tooltip="Controls blue automatic white balance gain, which affects how the camera captures colors in different conditions"
|
||||
:slider-cols="8"
|
||||
@input="e => handlePipelineData('cameraBlueGain', e)"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
:class="fpsTooLow ? 'ml-2 mt-1' : 'mt-2'"
|
||||
x-small
|
||||
label
|
||||
:color="fpsTooLow ? 'red' : 'transparent'"
|
||||
:color="fpsTooLow ? 'error' : 'transparent'"
|
||||
:text-color="fpsTooLow ? 'white' : 'grey'"
|
||||
>
|
||||
<span class="pr-1">{{ Math.round($store.state.pipelineResults.fps) }} FPS –</span>
|
||||
@@ -38,13 +38,6 @@
|
||||
<span v-else-if="!$store.getters.currentPipelineSettings.inputShouldShow">HSV thresholds are too broad; narrow them for better performance</span>
|
||||
<span v-else>stop viewing the color stream for better performance</span>
|
||||
</v-chip>
|
||||
<v-chip small label color="red" text-color="white" v-if="!$store.state.ntConnectionInfo.connected || $store.state.settings.networkSettings.runNTServer">
|
||||
<span>
|
||||
{{ $store.state.settings.networkSettings.runNTServer ?
|
||||
"NetworkTables Server Enabled! Photonlib may not work" :
|
||||
"NetworkTables not connected!" }}
|
||||
</span>
|
||||
</v-chip>
|
||||
<v-switch
|
||||
v-model="driverMode"
|
||||
label="Driver Mode"
|
||||
@@ -71,7 +64,8 @@
|
||||
:disconnected="!$store.state.backendConnected"
|
||||
scale="100"
|
||||
:max-height="$store.getters.isDriverMode ? '40vh' : '300px'"
|
||||
:max-height-md="$store.getters.isDriverMode ? '50vh' : '320px'"
|
||||
:max-height-md="$store.getters.isDriverMode ? '50vh' : '380px'"
|
||||
:max-height-lg="$store.getters.isDriverMode ? '55vh' : '390px'"
|
||||
:max-height-xl="$store.getters.isDriverMode ? '60vh' : '450px'"
|
||||
:alt="'Stream' + idx"
|
||||
:color-picking="$store.state.colorPicking && idx === 0"
|
||||
@@ -90,23 +84,18 @@
|
||||
>
|
||||
<v-card
|
||||
color="primary"
|
||||
class="mt-3"
|
||||
>
|
||||
<!-- <v-btn @click="onCamNameChange">-->
|
||||
<!-- Reload-->
|
||||
<!-- </v-btn>-->
|
||||
<camera-and-pipeline-select @camera-name-changed="reloadStreams" />
|
||||
</v-card>
|
||||
<v-card
|
||||
:disabled="$store.getters.isDriverMode || $store.state.colorPicking"
|
||||
class="mt-6 mb-3"
|
||||
class="mt-3"
|
||||
color="primary"
|
||||
>
|
||||
<v-row
|
||||
align="center"
|
||||
class="pl-3 pr-3"
|
||||
>
|
||||
<!-- -->
|
||||
<v-col lg="12">
|
||||
<p style="color: white;">
|
||||
Processing mode:
|
||||
@@ -206,21 +195,24 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
<!-- snack bar and modal -->
|
||||
|
||||
<v-snackbar
|
||||
v-model="snackbar"
|
||||
:timeout="3000"
|
||||
top
|
||||
v-model="showNTWarning"
|
||||
color="error"
|
||||
timeout="-1"
|
||||
top
|
||||
>
|
||||
<span style="color:#000">Can not remove the only pipeline!</span>
|
||||
<v-btn
|
||||
color="black"
|
||||
text
|
||||
@click="snackbar = false"
|
||||
>
|
||||
Close
|
||||
</v-btn>
|
||||
{{ $store.state.settings.networkSettings.runNTServer ?
|
||||
"NetworkTables server enabled! PhotonLib may not work." :
|
||||
"NetworkTables not connected! Are you on a network with a robot?" }}
|
||||
<template v-slot:action>
|
||||
<v-btn
|
||||
text
|
||||
@click="hideNTWarning = true"
|
||||
>
|
||||
Hide
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-snackbar>
|
||||
|
||||
<v-dialog
|
||||
@@ -286,10 +278,10 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
selectedTabsData: [0, 0, 0, 0],
|
||||
snackbar: false,
|
||||
counterData: 0,
|
||||
dialog: false,
|
||||
processingModeOverride: false
|
||||
processingModeOverride: false,
|
||||
hideNTWarning: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -434,7 +426,12 @@ export default {
|
||||
// return this.$store.state.ntConnectionInfo.connected && this.$store.state.backendConnected;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
showNTWarning: {
|
||||
get() {
|
||||
return (!this.$store.state.ntConnectionInfo.connected || this.$store.state.settings.networkSettings.runNTServer) && this.$store.state.settings.networkSettings.teamNumber > 0 && this.$store.state.backendConnected && !this.hideNTWarning;
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.$store.state.connectedCallbacks.push(this.reloadStreams)
|
||||
|
||||
@@ -1,140 +1,169 @@
|
||||
<template>
|
||||
<div>
|
||||
<CVrangeSlider
|
||||
v-model="contourArea"
|
||||
name="Area"
|
||||
min="0"
|
||||
max="100"
|
||||
step="0.1"
|
||||
@input="handlePipelineData('contourArea')"
|
||||
v-model="contourArea"
|
||||
name="Area"
|
||||
min="0"
|
||||
max="100"
|
||||
step="0.01"
|
||||
@input="handlePipelineData('contourArea')"
|
||||
/>
|
||||
<CVrangeSlider
|
||||
v-model="contourRatio"
|
||||
v-if="currentPipelineType() !== 3"
|
||||
name="Ratio (W/H)"
|
||||
tooltip="Min and max ratio between the width and height of a contour's bounding rectangle"
|
||||
min="0"
|
||||
max="100"
|
||||
step="0.1"
|
||||
@input="handlePipelineData('contourRatio')"
|
||||
v-if="currentPipelineType() !== 3"
|
||||
v-model="contourRatio"
|
||||
name="Ratio (W/H)"
|
||||
tooltip="Min and max ratio between the width and height of a contour's bounding rectangle"
|
||||
min="0"
|
||||
max="100"
|
||||
step="0.1"
|
||||
@input="handlePipelineData('contourRatio')"
|
||||
/>
|
||||
<CVselect
|
||||
v-model="contourTargetOrientation"
|
||||
name="Target Orientation"
|
||||
tooltip="Used to determine how to calculate target landmarks, as well as aspect ratio"
|
||||
:list="['Portrait', 'Landscape']"
|
||||
@input="handlePipelineData('contourTargetOrientation')"
|
||||
@rollback="e=> rollback('contourTargetOrientation', e)"
|
||||
/>
|
||||
<CVrangeSlider
|
||||
v-model="contourFullness"
|
||||
v-if="currentPipelineType() !== 3"
|
||||
name="Fullness"
|
||||
tooltip="Min and max ratio between a contour's area and its bounding rectangle"
|
||||
min="0"
|
||||
max="100"
|
||||
@input="handlePipelineData('contourFullness')"
|
||||
v-if="currentPipelineType() !== 3"
|
||||
v-model="contourFullness"
|
||||
name="Fullness"
|
||||
tooltip="Min and max ratio between a contour's area and its bounding rectangle"
|
||||
min="0"
|
||||
max="100"
|
||||
@input="handlePipelineData('contourFullness')"
|
||||
/>
|
||||
<CVrangeSlider
|
||||
v-model="contourPerimeter"
|
||||
v-if="currentPipelineType() === 3"
|
||||
name="Perimeter"
|
||||
tooltip="Min and max perimeter of the shape, in pixels"
|
||||
min="0"
|
||||
max="4000"
|
||||
@input="handlePipelineData('contourPerimeter')"
|
||||
v-if="currentPipelineType() === 3"
|
||||
v-model="contourPerimeter"
|
||||
name="Perimeter"
|
||||
tooltip="Min and max perimeter of the shape, in pixels"
|
||||
min="0"
|
||||
max="4000"
|
||||
@input="handlePipelineData('contourPerimeter')"
|
||||
/>
|
||||
<CVslider
|
||||
v-model="contourSpecklePercentage"
|
||||
name="Speckle Rejection"
|
||||
tooltip="Rejects contours whose average area is less than the given percentage of the average area of all the other contours"
|
||||
min="0"
|
||||
max="100"
|
||||
:slider-cols="largeBox"
|
||||
@input="handlePipelineData('contourSpecklePercentage')"
|
||||
v-model="contourSpecklePercentage"
|
||||
name="Speckle Rejection"
|
||||
tooltip="Rejects contours whose average area is less than the given percentage of the average area of all the other contours"
|
||||
min="0"
|
||||
max="100"
|
||||
:slider-cols="largeBox"
|
||||
@input="handlePipelineData('contourSpecklePercentage')"
|
||||
/>
|
||||
<template v-if="currentPipelineType() !== 3">
|
||||
<CVselect
|
||||
v-model="contourGroupingMode"
|
||||
name="Target Grouping"
|
||||
tooltip="Whether or not every two targets are paired with each other (good for e.g. 2019 targets)"
|
||||
:select-cols="largeBox"
|
||||
:list="['Single','Dual']"
|
||||
@input="handlePipelineData('contourGroupingMode')"
|
||||
<CVslider
|
||||
v-model="contourFilterRangeX"
|
||||
name="X filter tightness"
|
||||
tooltip="Rejects contours whose center X is further than X standard deviations above/below the mean X location"
|
||||
min="0.1"
|
||||
max="4"
|
||||
step="0.1"
|
||||
:slider-cols="largeBox"
|
||||
@input="handlePipelineData('contourFilterRangeX')"
|
||||
/>
|
||||
<CVslider
|
||||
v-model="contourFilterRangeY"
|
||||
name="Y filter tightness"
|
||||
tooltip="Rejects contours whose center Y is further than X standard deviations above/below the mean Y location"
|
||||
min="0.1"
|
||||
max="4"
|
||||
step="0.1"
|
||||
:slider-cols="largeBox"
|
||||
@input="handlePipelineData('contourFilterRangeY')"
|
||||
/>
|
||||
<CVselect
|
||||
v-model="contourIntersection"
|
||||
name="Target Intersection"
|
||||
tooltip="If target grouping is in dual mode it will use this dropdown to decide how targets are grouped with adjacent targets"
|
||||
:select-cols="largeBox"
|
||||
:list="['None','Up','Down','Left','Right']"
|
||||
:disabled="contourGroupingMode === 0"
|
||||
@input="handlePipelineData('contourIntersection')"
|
||||
v-model="contourGroupingMode"
|
||||
name="Target Grouping"
|
||||
tooltip="Whether or not every two targets are paired with each other (good for e.g. 2019 targets)"
|
||||
:select-cols="largeBox"
|
||||
:list="['Single','Dual','2orMore']"
|
||||
@input="handlePipelineData('contourGroupingMode')"
|
||||
/>
|
||||
<CVselect
|
||||
v-model="contourIntersection"
|
||||
name="Target Intersection"
|
||||
tooltip="If target grouping is in dual mode it will use this dropdown to decide how targets are grouped with adjacent targets"
|
||||
:select-cols="largeBox"
|
||||
:list="['None','Up','Down','Left','Right']"
|
||||
:disabled="contourGroupingMode === 0"
|
||||
@input="handlePipelineData('contourIntersection')"
|
||||
/>
|
||||
</template>
|
||||
<!-- If we arent not a shape, we are a shape-->
|
||||
<template v-else>
|
||||
<v-divider class="mt-3"/>
|
||||
<v-divider class="mt-3" />
|
||||
<CVselect
|
||||
v-model="contourShape"
|
||||
name="Target Shape"
|
||||
tooltip="The shape of targets to look for"
|
||||
:select-cols="largeBox"
|
||||
:list="['Circle', 'Polygon', 'Triangle', 'Quadrilateral']"
|
||||
@input="handlePipelineData('contourShape')"
|
||||
v-model="contourShape"
|
||||
name="Target Shape"
|
||||
tooltip="The shape of targets to look for"
|
||||
:select-cols="largeBox"
|
||||
:list="['Circle', 'Polygon', 'Triangle', 'Quadrilateral']"
|
||||
@input="handlePipelineData('contourShape')"
|
||||
/>
|
||||
|
||||
<!-- Accuracy % is only for polygons-->
|
||||
<CVslider
|
||||
v-model="accuracyPercentage"
|
||||
:disabled="currentPipelineSettings().contourShape < 1"
|
||||
name="Shape Simplification"
|
||||
tooltip="How much we should simply the input contour before checking how many sides it has"
|
||||
min="0"
|
||||
max="100"
|
||||
:slider-cols="largeBox"
|
||||
@input="handlePipelineData('accuracyPercentage')"
|
||||
v-model="accuracyPercentage"
|
||||
:disabled="currentPipelineSettings().contourShape < 1"
|
||||
name="Shape Simplification"
|
||||
tooltip="How much we should simply the input contour before checking how many sides it has"
|
||||
min="0"
|
||||
max="100"
|
||||
:slider-cols="largeBox"
|
||||
@input="handlePipelineData('accuracyPercentage')"
|
||||
/>
|
||||
<!-- Similarly, the threshold is only for circles -->
|
||||
<CVslider
|
||||
v-model="circleDetectThreshold"
|
||||
:disabled="currentPipelineSettings().contourShape !== 0"
|
||||
name="Circle match distance"
|
||||
tooltip="How close the centroid of a contour must be to the center of a circle in order for them to be matched"
|
||||
min="1"
|
||||
max="100"
|
||||
:slider-cols="largeBox"
|
||||
@input="handlePipelineData('circleDetectThreshold')"
|
||||
v-model="circleDetectThreshold"
|
||||
:disabled="currentPipelineSettings().contourShape !== 0"
|
||||
name="Circle match distance"
|
||||
tooltip="How close the centroid of a contour must be to the center of a circle in order for them to be matched"
|
||||
min="1"
|
||||
max="100"
|
||||
:slider-cols="largeBox"
|
||||
@input="handlePipelineData('circleDetectThreshold')"
|
||||
/>
|
||||
<CVrangeSlider
|
||||
v-model="contourRadius"
|
||||
:disabled="currentPipelineSettings().contourShape !== 0"
|
||||
name="Radius"
|
||||
min="0"
|
||||
max="100"
|
||||
step="1"
|
||||
@input="handlePipelineData('contourRadius')"
|
||||
v-model="contourRadius"
|
||||
:disabled="currentPipelineSettings().contourShape !== 0"
|
||||
name="Radius"
|
||||
min="0"
|
||||
max="100"
|
||||
step="1"
|
||||
label-cols="3"
|
||||
@input="handlePipelineData('contourRadius')"
|
||||
/>
|
||||
<CVslider
|
||||
v-model="maxCannyThresh"
|
||||
:disabled="currentPipelineSettings().contourShape !== 0"
|
||||
name="Max Canny Threshold"
|
||||
min="1"
|
||||
max="100"
|
||||
:slider-cols="largeBox"
|
||||
@input="handlePipelineData('maxCannyThresh')"
|
||||
v-model="maxCannyThresh"
|
||||
:disabled="currentPipelineSettings().contourShape !== 0"
|
||||
name="Max Canny Threshold"
|
||||
min="1"
|
||||
max="100"
|
||||
:slider-cols="largeBox"
|
||||
@input="handlePipelineData('maxCannyThresh')"
|
||||
/>
|
||||
<CVslider
|
||||
v-model="circleAccuracy"
|
||||
:disabled="currentPipelineSettings().contourShape !== 0"
|
||||
name="Circle Accuracy"
|
||||
min="0"
|
||||
max="100"
|
||||
:slider-cols="largeBox"
|
||||
@input="handlePipelineData('circleAccuracy')"
|
||||
v-model="circleAccuracy"
|
||||
:disabled="currentPipelineSettings().contourShape !== 0"
|
||||
name="Circle Accuracy"
|
||||
min="1"
|
||||
max="100"
|
||||
:slider-cols="largeBox"
|
||||
@input="handlePipelineData('circleAccuracy')"
|
||||
/>
|
||||
<v-divider class="mt-3"/>
|
||||
<v-divider class="mt-3" />
|
||||
</template>
|
||||
<CVselect
|
||||
v-model="contourSortMode"
|
||||
name="Target Sort"
|
||||
tooltip="Chooses the sorting mode used to determine the 'best' targets to provide to user code"
|
||||
:select-cols="largeBox"
|
||||
:list="['Largest','Smallest','Highest','Lowest','Rightmost','Leftmost','Centermost']"
|
||||
@input="handlePipelineData('contourSortMode')"
|
||||
@rollback="e => rollback('contourSortMode', e)"
|
||||
v-model="contourSortMode"
|
||||
name="Target Sort"
|
||||
tooltip="Chooses the sorting mode used to determine the 'best' targets to provide to user code"
|
||||
:select-cols="largeBox"
|
||||
:list="['Largest','Smallest','Highest','Lowest','Rightmost','Leftmost','Centermost']"
|
||||
@input="handlePipelineData('contourSortMode')"
|
||||
@rollback="e => rollback('contourSortMode', e)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -182,6 +211,14 @@ export default {
|
||||
this.$store.commit("mutatePipeline", {"contourRatio": val});
|
||||
}
|
||||
},
|
||||
contourTargetOrientation: {
|
||||
get() {
|
||||
return this.$store.getters.currentPipelineSettings.contourTargetOrientation
|
||||
},
|
||||
set(val) {
|
||||
this.$store.commit("mutatePipeline", {"contourTargetOrientation": val});
|
||||
}
|
||||
},
|
||||
contourFullness: {
|
||||
get() {
|
||||
return this.$store.getters.currentPipelineSettings.contourFullness
|
||||
@@ -206,6 +243,25 @@ export default {
|
||||
this.$store.commit("mutatePipeline", {"contourSpecklePercentage": val});
|
||||
}
|
||||
},
|
||||
contourFilterRangeX: {
|
||||
get() {
|
||||
console.log(this.$store.getters.currentPipelineSettings.contourFilterRangeX)
|
||||
return this.$store.getters.currentPipelineSettings.contourFilterRangeX
|
||||
},
|
||||
set(val) {
|
||||
console.log("set")
|
||||
console.log(val)
|
||||
this.$store.commit("mutatePipeline", {"contourFilterRangeX": val});
|
||||
}
|
||||
},
|
||||
contourFilterRangeY: {
|
||||
get() {
|
||||
return this.$store.getters.currentPipelineSettings.contourFilterRangeY
|
||||
},
|
||||
set(val) {
|
||||
this.$store.commit("mutatePipeline", {"contourFilterRangeY": val});
|
||||
}
|
||||
},
|
||||
contourGroupingMode: {
|
||||
get() {
|
||||
return this.$store.getters.currentPipelineSettings.contourGroupingMode
|
||||
|
||||
@@ -22,15 +22,26 @@
|
||||
@rollback="e => rollback('cameraBrightness', e)"
|
||||
/>
|
||||
<CVslider
|
||||
v-if="cameraGain !== -1"
|
||||
v-model="cameraGain"
|
||||
name="Gain"
|
||||
v-if="cameraRedGain !== -1"
|
||||
v-model="cameraRedGain"
|
||||
name="Red AWB Gain"
|
||||
min="0"
|
||||
max="100"
|
||||
tooltip="Controls automatic white balance gain, which affects how the camera captures colors in different conditions"
|
||||
tooltip="Controls red automatic white balance gain, which affects how the camera captures colors in different conditions"
|
||||
:slider-cols="largeBox"
|
||||
@input="handlePipelineData('cameraGain')"
|
||||
@rollback="e => rollback('cameraGain', e)"
|
||||
@input="handlePipelineData('cameraRedGain')"
|
||||
@rollback="e => rollback('cameraRedGain', e)"
|
||||
/>
|
||||
<CVslider
|
||||
v-if="cameraBlueGain !== -1"
|
||||
v-model="cameraBlueGain"
|
||||
name="Blue AWB Gain"
|
||||
min="0"
|
||||
max="100"
|
||||
tooltip="Controls blue automatic white balance gain, which affects how the camera captures colors in different conditions"
|
||||
:slider-cols="largeBox"
|
||||
@input="handlePipelineData('cameraBlueGain')"
|
||||
@rollback="e => rollback('cameraBlueGain', e)"
|
||||
/>
|
||||
<CVselect
|
||||
v-model="inputImageRotationMode"
|
||||
@@ -105,12 +116,20 @@
|
||||
this.$store.commit("mutatePipeline", {"cameraBrightness": parseInt(val)});
|
||||
}
|
||||
},
|
||||
cameraGain: {
|
||||
cameraRedGain: {
|
||||
get() {
|
||||
return parseInt(this.$store.getters.currentPipelineSettings.cameraGain)
|
||||
return parseInt(this.$store.getters.currentPipelineSettings.cameraRedGain)
|
||||
},
|
||||
set(val) {
|
||||
this.$store.commit("mutatePipeline", {"cameraGain": parseInt(val)});
|
||||
this.$store.commit("mutatePipeline", {"cameraRedGain": parseInt(val)});
|
||||
}
|
||||
},
|
||||
cameraBlueGain: {
|
||||
get() {
|
||||
return parseInt(this.$store.getters.currentPipelineSettings.cameraBlueGain)
|
||||
},
|
||||
set(val) {
|
||||
this.$store.commit("mutatePipeline", {"cameraBlueGain": parseInt(val)});
|
||||
}
|
||||
},
|
||||
inputImageRotationMode: {
|
||||
@@ -170,7 +189,11 @@
|
||||
// It would probably be cleaner if this checked that we're on the Raspi 3 instead of checking for GPU accel status
|
||||
const width = this.$store.getters.videoFormatList[
|
||||
this.$store.getters.currentCameraSettings.currentPipelineSettings.cameraVideoModeIndex]['width'];
|
||||
return unfilteredStreamDivisors.filter((x) => !this.$store.state.settings.general.gpuAcceleration || width / x < 400);
|
||||
|
||||
// If GPU acceleration is enabled, the downsized width must be below 400px
|
||||
// This check should be skipped if we're currently in driver mode
|
||||
return unfilteredStreamDivisors.filter((x) => this.$store.getters.isDriverMode
|
||||
|| !this.$store.state.settings.general.gpuAcceleration || width / x < 400);
|
||||
},
|
||||
getNumSkippedStreamDivisors() {
|
||||
return unfilteredStreamDivisors.length - this.getRawStreamDivisors().length;
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<span class="white--text">Target Manipulation</span>
|
||||
<v-divider class="mt-2" />
|
||||
|
||||
<CVselect
|
||||
v-model="contourTargetOffsetPointEdge"
|
||||
name="Target Offset Point"
|
||||
@@ -31,8 +28,7 @@
|
||||
|
||||
@rollback="e=> rollback('outputShowMultipleTargets', e)"
|
||||
/>
|
||||
<span class="white--text">Robot Offset</span>
|
||||
<v-divider class="mt-2" />
|
||||
<v-divider />
|
||||
<CVselect
|
||||
v-model="offsetRobotOffsetMode"
|
||||
name="Robot Offset Mode"
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
targetList: ['2020 High Goal Outer', '2020 High Goal Inner', '2019 Dual Target', 'Power Cell (7in)', '2016 High Goal'], //Keep in sync with TargetModel.java
|
||||
targetList: ['2020 High Goal Outer', '2020 High Goal Inner', '2019 Dual Target', '2020 Power Cell (7in)','2022 Cargo Ball (9.5in)', '2016 High Goal'], //Keep in sync with TargetModel.java
|
||||
snackbar: {
|
||||
color: "Success",
|
||||
text: ""
|
||||
|
||||
@@ -1,66 +1,79 @@
|
||||
<template>
|
||||
<div>
|
||||
<div :style="{'--averageHue': averageHue}">
|
||||
<CVrangeSlider
|
||||
v-model="hsvHue"
|
||||
name="Hue"
|
||||
tooltip="Describes color"
|
||||
:min="0"
|
||||
:max="180"
|
||||
@input="handlePipelineData('hsvHue')"
|
||||
@rollback="e => rollback('hue',e)"
|
||||
id="hue-slider"
|
||||
v-model="hsvHue"
|
||||
:class="hueInverted ? 'inverted-slider' : 'normal-slider'"
|
||||
name="Hue"
|
||||
tooltip="Describes color"
|
||||
:min="0"
|
||||
:max="180"
|
||||
:inverted="hueInverted"
|
||||
@input="handlePipelineData('hsvHue')"
|
||||
@rollback="e => rollback('hue',e)"
|
||||
/>
|
||||
<CVrangeSlider
|
||||
v-model="hsvSaturation"
|
||||
name="Saturation"
|
||||
tooltip="Describes colorfulness; the smaller this value the 'whiter' the color becomes"
|
||||
:min="0"
|
||||
:max="255"
|
||||
@input="handlePipelineData('hsvSaturation')"
|
||||
@rollback="e => rollback('saturation',e)"
|
||||
id="sat-slider"
|
||||
v-model="hsvSaturation"
|
||||
class="normal-slider"
|
||||
name="Saturation"
|
||||
tooltip="Describes colorfulness; the smaller this value the 'whiter' the color becomes"
|
||||
:min="0"
|
||||
:max="255"
|
||||
@input="handlePipelineData('hsvSaturation')"
|
||||
@rollback="e => rollback('saturation',e)"
|
||||
/>
|
||||
<CVrangeSlider
|
||||
v-model="hsvValue"
|
||||
name="Value"
|
||||
tooltip="Describes lightness; the smaller this value the 'blacker' the color becomes"
|
||||
:min="0"
|
||||
:max="255"
|
||||
@input="handlePipelineData('hsvValue')"
|
||||
@rollback="e => rollback('value',e)"
|
||||
id="value-slider"
|
||||
v-model="hsvValue"
|
||||
class="normal-slider"
|
||||
name="Value"
|
||||
tooltip="Describes lightness; the smaller this value the 'blacker' the color becomes"
|
||||
:min="0"
|
||||
:max="255"
|
||||
@input="handlePipelineData('hsvValue')"
|
||||
@rollback="e => rollback('value',e)"
|
||||
/>
|
||||
<template v-if="this.currentPipelineType() === 3">
|
||||
<CVSwitch
|
||||
v-model="hueInverted"
|
||||
name="Invert hue"
|
||||
tooltip="Selects the hue range outside of the hue slider bounds instead of inside"
|
||||
@input="handlePipelineData('hueInverted')"
|
||||
@rollback="e => rollback('hueInverted',e)"
|
||||
/>
|
||||
<template v-if="currentPipelineType() === 3">
|
||||
<CVSwitch
|
||||
v-model="erode"
|
||||
name="Erode"
|
||||
tooltip="Removes pixels around the edges of white areas in the thresholded image"
|
||||
@input="handlePipelineData('erode')"
|
||||
@rollback="e => rollback('erode',e)"
|
||||
v-model="erode"
|
||||
name="Erode"
|
||||
tooltip="Removes pixels around the edges of white areas in the thresholded image"
|
||||
@input="handlePipelineData('erode')"
|
||||
@rollback="e => rollback('erode',e)"
|
||||
/>
|
||||
<CVSwitch
|
||||
v-model="dilate"
|
||||
class="mb-0"
|
||||
name="Dilate"
|
||||
tooltip="Adds pixels around the edges of white areas in the thresholded image"
|
||||
@input="handlePipelineData('dilate')"
|
||||
@rollback="e => rollback('dilate',e)"
|
||||
v-model="dilate"
|
||||
class="mb-0"
|
||||
name="Dilate"
|
||||
tooltip="Adds pixels around the edges of white areas in the thresholded image"
|
||||
@input="handlePipelineData('dilate')"
|
||||
@rollback="e => rollback('dilate',e)"
|
||||
/>
|
||||
</template>
|
||||
<v-divider class="mb-3 mt-3"/>
|
||||
<div class="pt-3 white--text">
|
||||
Color Picker
|
||||
</div>
|
||||
<v-divider
|
||||
class="mt-3"
|
||||
class="mt-3"
|
||||
/>
|
||||
<v-row
|
||||
justify="center"
|
||||
class="mt-3 mb-3"
|
||||
justify="center"
|
||||
class="mt-3 mb-3"
|
||||
>
|
||||
<template v-if="!$store.state.colorPicking">
|
||||
<v-btn
|
||||
color="accent"
|
||||
class="ma-2 black--text"
|
||||
small
|
||||
@click="setFunction(3)"
|
||||
color="accent"
|
||||
class="ma-2 black--text"
|
||||
small
|
||||
@click="setFunction(hueInverted ? 2 : 3)"
|
||||
>
|
||||
<v-icon left>
|
||||
mdi-minus
|
||||
@@ -68,21 +81,21 @@
|
||||
Shrink Range
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="accent"
|
||||
class="ma-2 black--text"
|
||||
small
|
||||
@click="setFunction(1)"
|
||||
color="accent"
|
||||
class="ma-2 black--text"
|
||||
small
|
||||
@click="setFunction(1)"
|
||||
>
|
||||
<v-icon left>
|
||||
mdi-plus-minus
|
||||
</v-icon>
|
||||
Set To Average
|
||||
{{ hueInverted ? "Exclude" : "Set to" }} Average
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="accent"
|
||||
class="ma-2 black--text"
|
||||
small
|
||||
@click="setFunction(2)"
|
||||
color="accent"
|
||||
class="ma-2 black--text"
|
||||
small
|
||||
@click="setFunction(hueInverted ? 3: 2)"
|
||||
>
|
||||
<v-icon left>
|
||||
mdi-plus
|
||||
@@ -92,16 +105,17 @@
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-btn
|
||||
color="accent"
|
||||
class="ma-2 black--text"
|
||||
style="width: 30%;"
|
||||
small
|
||||
@click="setFunction(0)"
|
||||
color="accent"
|
||||
class="ma-2 black--text"
|
||||
style="width: 30%;"
|
||||
small
|
||||
@click="setFunction(0)"
|
||||
>
|
||||
Cancel
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-row>
|
||||
<v-divider class="mb-3" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -133,9 +147,41 @@ export default {
|
||||
this.$store.commit("mutatePipeline", {"hsvHue": val})
|
||||
}
|
||||
},
|
||||
averageHue: {
|
||||
get() {
|
||||
var isInverted = this.$store.getters.currentPipelineSettings.hueInverted;
|
||||
const arr = this.$store.getters.currentPipelineSettings.hsvHue;
|
||||
var retVal = 0;
|
||||
|
||||
if (Array.isArray(arr)) {
|
||||
retVal = (arr[0] + arr[1]);
|
||||
} else {
|
||||
retVal = (arr.first + arr.second);
|
||||
}
|
||||
|
||||
if(isInverted){
|
||||
retVal += 180;
|
||||
}
|
||||
|
||||
if(retVal > 360){
|
||||
retVal -= 360;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
|
||||
},
|
||||
},
|
||||
hueInverted: {
|
||||
get() {
|
||||
return this.$store.getters.currentPipelineSettings.hueInverted;
|
||||
},
|
||||
set(val) {
|
||||
this.$store.commit("mutatePipeline", {"hueInverted": val});
|
||||
}
|
||||
},
|
||||
hsvSaturation: {
|
||||
get() {
|
||||
return this.$store.getters.currentPipelineSettings.hsvSaturation
|
||||
return this.$store.getters.currentPipelineSettings.hsvSaturation;
|
||||
},
|
||||
set(val) {
|
||||
this.$store.commit("mutatePipeline", {"hsvSaturation": val})
|
||||
@@ -143,15 +189,15 @@ export default {
|
||||
},
|
||||
hsvValue: {
|
||||
get() {
|
||||
return this.$store.getters.currentPipelineSettings.hsvValue
|
||||
return this.$store.getters.currentPipelineSettings.hsvValue;
|
||||
},
|
||||
set(val) {
|
||||
this.$store.commit("mutatePipeline", {"hsvValue": val})
|
||||
this.$store.commit("mutatePipeline", {"hsvValue": val});
|
||||
}
|
||||
},
|
||||
erode: {
|
||||
get() {
|
||||
return this.$store.getters.currentPipelineSettings.erode
|
||||
return this.$store.getters.currentPipelineSettings.erode;
|
||||
},
|
||||
set(val) {
|
||||
this.$store.commit("mutatePipeline", {"erode": val});
|
||||
@@ -159,7 +205,7 @@ export default {
|
||||
},
|
||||
dilate: {
|
||||
get() {
|
||||
return this.$store.getters.currentPipelineSettings.dilate
|
||||
return this.$store.getters.currentPipelineSettings.dilate;
|
||||
},
|
||||
set(val) {
|
||||
this.$store.commit("mutatePipeline", {"dilate": val});
|
||||
@@ -233,3 +279,31 @@ export default {
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
#hue-slider >>> .v-slider {
|
||||
background: linear-gradient( to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100% );
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 0px 5px #333, inset 0px 0px 3px #333;
|
||||
}
|
||||
#sat-slider >>> .v-slider {
|
||||
background: linear-gradient( to right, #fff 0%, hsl(var(--averageHue), 100%, 50%) 100% );
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 0px 5px #333, inset 0px 0px 3px #333;
|
||||
}
|
||||
#value-slider >>> .v-slider {
|
||||
background: linear-gradient( to right, #000 0%, hsl(var(--averageHue), 100%, 50%) 100% );
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 0px 5px #333, inset 0px 0px 3px #333;
|
||||
}
|
||||
>>> .v-slider__thumb {
|
||||
outline: black solid thin;
|
||||
}
|
||||
.normal-slider >>> .v-slider__track-fill {
|
||||
outline: black solid thin;
|
||||
}
|
||||
|
||||
.inverted-slider >>> .v-slider__track-background {
|
||||
outline: black solid thin;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
return {
|
||||
selectedTab: 0,
|
||||
snack: false,
|
||||
calibrationInProgress: false,
|
||||
snackbar: {
|
||||
color: "accent",
|
||||
text: ""
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
<v-col
|
||||
cols="12"
|
||||
sm="6"
|
||||
lg="3"
|
||||
md="4"
|
||||
>
|
||||
<v-btn
|
||||
color="secondary"
|
||||
@@ -106,7 +106,7 @@
|
||||
<v-col
|
||||
cols="12"
|
||||
sm="6"
|
||||
lg="3"
|
||||
md="4"
|
||||
>
|
||||
<v-btn
|
||||
color="secondary"
|
||||
@@ -120,7 +120,21 @@
|
||||
</v-col>
|
||||
<v-col
|
||||
cols="12"
|
||||
lg="3"
|
||||
md="4"
|
||||
>
|
||||
<v-btn
|
||||
color="secondary"
|
||||
@click="$refs.offlineUpdate.click()"
|
||||
>
|
||||
<v-icon left>
|
||||
mdi-update
|
||||
</v-icon>
|
||||
Offline Update
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col
|
||||
cols="12"
|
||||
lg="6"
|
||||
>
|
||||
<v-btn
|
||||
color="red"
|
||||
@@ -129,19 +143,19 @@
|
||||
<v-icon left>
|
||||
mdi-restart
|
||||
</v-icon>
|
||||
Restart Photon
|
||||
Restart PhotonVision
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col
|
||||
cols="12"
|
||||
lg="3"
|
||||
lg="6"
|
||||
>
|
||||
<v-btn
|
||||
color="red"
|
||||
@click="restartDevice()"
|
||||
>
|
||||
<v-icon left>
|
||||
mdi-restart
|
||||
mdi-restart-alert
|
||||
</v-icon>
|
||||
Restart Device
|
||||
</v-btn>
|
||||
@@ -172,6 +186,15 @@
|
||||
:href="'http://' + this.$address + '/api/settings/photonvision_config.zip'"
|
||||
download="photonvision-settings.zip"
|
||||
/>
|
||||
|
||||
<!-- Special hidden new jar upload input that gets 'clicked' when the user posts a new .jar -->
|
||||
<input
|
||||
ref="offlineUpdate"
|
||||
type="file"
|
||||
accept=".jar"
|
||||
style="display: none;"
|
||||
@change="doOfflineUpdate"
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -181,6 +204,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
snack: false,
|
||||
uploadPercentage: 0.0,
|
||||
snackbar: {
|
||||
color: "success",
|
||||
text: ""
|
||||
@@ -226,7 +250,7 @@ export default {
|
||||
{headers: {"Content-Type": "multipart/form-data"}}).then(() => {
|
||||
this.snackbar = {
|
||||
color: "success",
|
||||
text: "Settings imported successfully! Program will now exit...",
|
||||
text: "Settings imported successfully! PhotonVision will restart in the background...",
|
||||
};
|
||||
this.snack = true;
|
||||
}).catch(err => {
|
||||
@@ -249,6 +273,52 @@ export default {
|
||||
this.snack = true;
|
||||
});
|
||||
},
|
||||
doOfflineUpdate(event) {
|
||||
this.snackbar = {
|
||||
color: "secondary",
|
||||
text: "New Software Upload in Process..."
|
||||
};
|
||||
this.snack = true;
|
||||
|
||||
let formData = new FormData();
|
||||
formData.append("jarData", event.target.files[0]);
|
||||
this.axios.post("http://" + this.$address + "/api/settings/offlineUpdate", formData,
|
||||
{headers: {"Content-Type": "multipart/form-data"},
|
||||
onUploadProgress: function( progressEvent ) {
|
||||
this.uploadPercentage = parseInt( Math.round( ( progressEvent.loaded / progressEvent.total ) * 100 ) );
|
||||
if(this.uploadPercentage < 99.5){
|
||||
this.snackbar.text = "New Software Upload in Process, " + this.uploadPercentage + "% complete";
|
||||
} else {
|
||||
this.snackbar.text = "Installing uploaded software...";
|
||||
}
|
||||
|
||||
}.bind(this)
|
||||
}).then(() => {
|
||||
this.snackbar = {
|
||||
color: "success",
|
||||
text: "New .jar copied successfully! PhotonVision will restart in the background...",
|
||||
};
|
||||
this.snack = true;
|
||||
}).catch(err => {
|
||||
if (err.response) {
|
||||
this.snackbar = {
|
||||
color: "error",
|
||||
text: "Error while uploading new .jar file! Could not process provided file.",
|
||||
};
|
||||
} else if (err.request) {
|
||||
this.snackbar = {
|
||||
color: "error",
|
||||
text: "Error while uploading new .jar file! No respond to upload attempt.",
|
||||
};
|
||||
} else {
|
||||
this.snackbar = {
|
||||
color: "error",
|
||||
text: "Error while uploading new .jar file!",
|
||||
};
|
||||
}
|
||||
this.snack = true;
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -266,6 +336,8 @@ export default {
|
||||
text-align: left;
|
||||
margin-bottom: 10px;
|
||||
width: 100%;
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.infoElem {
|
||||
|
||||
@@ -10,44 +10,52 @@
|
||||
name="Team Number"
|
||||
:rules="[v => (v > 0) || 'Team number must be greater than zero', v => (v < 10000) || 'Team number must have fewer than five digits']"
|
||||
class="mb-4"
|
||||
:label-cols="$vuetify.breakpoint.mdAndUp ? undefined : 5"
|
||||
/>
|
||||
<v-chip label color="red" text-color="white" v-if="parseInt(teamNumber) < 1 && !runNTServer">
|
||||
<span>
|
||||
Team number not set! NetworkTables cannot connect.
|
||||
</span>
|
||||
</v-chip>
|
||||
<v-banner
|
||||
v-show="(teamNumber < 1 || teamNumber > 10000) && !runNTServer"
|
||||
rounded
|
||||
color="red"
|
||||
text-color="white"
|
||||
>
|
||||
Team number is unset or invalid. NetworkTables will not be able to connect.
|
||||
</v-banner>
|
||||
<CVradio
|
||||
v-model="connectionType"
|
||||
v-show="$store.state.settings.networkSettings.supported"
|
||||
v-model="connectionType"
|
||||
:input-cols="inputCols"
|
||||
name="IP Assignment Mode"
|
||||
tooltip="DHCP will make the radio (router) automatically assign an IP address; this may result in an IP address that changes across reboots. Static IP assignment means that you pick the IP address and it won't change."
|
||||
:list="['DHCP','Static']"
|
||||
:disabled="!$store.state.settings.networkSettings.supported"
|
||||
/>
|
||||
<template v-if="!isDHCP">
|
||||
<CVinput
|
||||
v-model="staticIp"
|
||||
:input-cols="inputCols"
|
||||
:rules="[v => isIPv4(v) || 'Invalid IPv4 address']"
|
||||
name="IP"
|
||||
/>
|
||||
</template>
|
||||
<CVinput
|
||||
v-if="!isDHCP"
|
||||
v-model="staticIp"
|
||||
:input-cols="inputCols"
|
||||
:rules="[v => isIPv4(v) || 'Invalid IPv4 address']"
|
||||
name="IP"
|
||||
/>
|
||||
<CVinput
|
||||
v-model="hostname"
|
||||
:input-cols="inputCols"
|
||||
:rules="[v => isHostname(v) || 'Invalid hostname']"
|
||||
name="Hostname"
|
||||
/>
|
||||
Advanced
|
||||
<v-divider/>
|
||||
<CVSwitch
|
||||
v-model="runNTServer"
|
||||
name="Run NetworkTables Server (Debugging Only!)"
|
||||
tooltip="If enabled, this device will create a NT server. This is useful for home debugging, but should be disabled on-robot."
|
||||
class="mt-3 mb-3"
|
||||
v-model="runNTServer"
|
||||
name="Run NetworkTables Server (Debugging Only)"
|
||||
tooltip="If enabled, this device will create a NT server. This is useful for home debugging, but should be disabled on-robot."
|
||||
class="mt-3 mb-3"
|
||||
:text-cols="$vuetify.breakpoint.mdAndUp ? undefined : 5"
|
||||
/>
|
||||
<v-chip label color="red" text-color="white" v-if="runNTServer">
|
||||
<span>
|
||||
Disable this switch if you're on a robot! Photonlib will NOT work.
|
||||
</span>
|
||||
</v-chip>
|
||||
<v-banner
|
||||
v-show="runNTServer"
|
||||
rounded
|
||||
color="red"
|
||||
text-color="white"
|
||||
>
|
||||
This switch is intended for testing; it should be off on a robot. PhotonLib will NOT work!
|
||||
</v-banner>
|
||||
</v-form>
|
||||
<v-btn
|
||||
color="accent"
|
||||
@@ -58,55 +66,60 @@
|
||||
>
|
||||
Save
|
||||
</v-btn>
|
||||
<v-divider class="mt-4 mb-4"/>
|
||||
<v-divider class="mt-4 mb-4" />
|
||||
<v-row>
|
||||
<v-col cols="6">
|
||||
|
||||
<v-col
|
||||
cols="12"
|
||||
sm="6"
|
||||
>
|
||||
<v-simple-table
|
||||
fixed-header
|
||||
height="100%"
|
||||
dense
|
||||
fixed-header
|
||||
height="100%"
|
||||
dense
|
||||
>
|
||||
<template v-slot:default>
|
||||
<thead style="font-size: 1.25rem;">
|
||||
<tr>
|
||||
<th>
|
||||
Device IPs
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
Device IPs
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(value, index) in $store.state.ntConnectionInfo.deviceips"
|
||||
<tr
|
||||
v-for="(value, index) in $store.state.networkInfo.deviceips"
|
||||
:key="index"
|
||||
>
|
||||
<td>{{ value }}</td>
|
||||
</tr>
|
||||
>
|
||||
<td>{{ value }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</template>
|
||||
</v-simple-table>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<v-col
|
||||
cols="12"
|
||||
sm="6"
|
||||
>
|
||||
<v-simple-table
|
||||
fixed-header
|
||||
height="100%"
|
||||
dense
|
||||
fixed-header
|
||||
height="100%"
|
||||
dense
|
||||
>
|
||||
<template v-slot:default>
|
||||
<thead style="font-size: 1.25rem;">
|
||||
<tr>
|
||||
<th>
|
||||
Possible RoboRIOs
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
Possible RoboRIOs
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(value, index) in $store.state.ntConnectionInfo.possibleRios"
|
||||
<tr
|
||||
v-for="(value, index) in $store.state.networkInfo.possibleRios"
|
||||
:key="index"
|
||||
>
|
||||
<td>{{ value }}</td>
|
||||
</tr>
|
||||
>
|
||||
<td>{{ value }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</template>
|
||||
</v-simple-table>
|
||||
@@ -148,7 +161,7 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
inputCols() {
|
||||
return this.$vuetify.breakpoint.smAndUp ? 10 : 7;
|
||||
return this.$vuetify.breakpoint.mdAndUp ? 10 : 7;
|
||||
},
|
||||
isDHCP() {
|
||||
return this.settings.connectionType === 0;
|
||||
|
||||
@@ -5,17 +5,13 @@ apply from: "${rootDir}/shared/common.gradle"
|
||||
dependencies {
|
||||
implementation project(':photon-targeting')
|
||||
|
||||
implementation 'io.javalin:javalin:3.7.0'
|
||||
implementation 'io.javalin:javalin:4.2.0'
|
||||
|
||||
implementation 'org.msgpack:msgpack-core:0.8.20'
|
||||
implementation 'org.msgpack:jackson-dataformat-msgpack:0.8.20'
|
||||
implementation 'org.msgpack:msgpack-core:0.9.0'
|
||||
implementation 'org.msgpack:jackson-dataformat-msgpack:0.9.0'
|
||||
|
||||
// wpiutil
|
||||
implementation "edu.wpi.first.wpiutil:wpiutil-jni:$wpilibVersion:linuxaarch64bionic"
|
||||
implementation "edu.wpi.first.wpiutil:wpiutil-jni:$wpilibVersion:linuxraspbian"
|
||||
implementation "edu.wpi.first.wpiutil:wpiutil-jni:$wpilibVersion:linuxx86-64"
|
||||
implementation "edu.wpi.first.wpiutil:wpiutil-jni:$wpilibVersion:osxx86-64"
|
||||
implementation "edu.wpi.first.wpiutil:wpiutil-jni:$wpilibVersion:windowsx86-64"
|
||||
jniPlatforms.each { implementation "edu.wpi.first.wpiutil:wpiutil-jni:$wpilibVersion:$it" }
|
||||
|
||||
// JOGL stuff (currently we only distribute for aarch64, which is Pi 4)
|
||||
implementation "org.jogamp.gluegen:gluegen-rt:$joglVersion"
|
||||
@@ -29,7 +25,8 @@ dependencies {
|
||||
}
|
||||
|
||||
task writeCurrentVersionJava {
|
||||
writePhotonVersionFile(Path.of("$projectDir", "src", "main", "java", "org", "photonvision", "PhotonVersion.java"),
|
||||
def versionFileIn = file("${rootDir}/shared/PhotonVersion.java.in")
|
||||
writePhotonVersionFile(versionFileIn, Path.of("$projectDir", "src", "main", "java", "org", "photonvision", "PhotonVersion.java"),
|
||||
versionString)
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common;
|
||||
|
||||
public enum ProgramStatus {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.configuration;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
@@ -31,7 +32,6 @@ import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
import org.photonvision.common.util.TimedTaskManager;
|
||||
import org.photonvision.common.util.file.FileUtils;
|
||||
import org.photonvision.common.util.file.JacksonUtils;
|
||||
import org.photonvision.vision.pipeline.CVPipelineSettings;
|
||||
@@ -56,6 +56,7 @@ public class ConfigManager {
|
||||
final File configDirectoryFile;
|
||||
|
||||
private long saveRequestTimestamp = -1;
|
||||
private Thread settingsSaveThread;
|
||||
|
||||
public static ConfigManager getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
@@ -96,7 +97,8 @@ public class ConfigManager {
|
||||
new File(Path.of(configDirectoryFile.toString(), NET_SET_FNAME).toUri());
|
||||
this.camerasFolder = new File(Path.of(configDirectoryFile.toString(), "cameras").toUri());
|
||||
|
||||
TimedTaskManager.getInstance().addTask("ConfigManager", this::checkSaveAndWrite, 1000);
|
||||
settingsSaveThread = new Thread(this::saveAndWriteTask);
|
||||
settingsSaveThread.start();
|
||||
}
|
||||
|
||||
public void load() {
|
||||
@@ -424,12 +426,24 @@ public class ConfigManager {
|
||||
saveRequestTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private void checkSaveAndWrite() {
|
||||
private void saveAndWriteTask() {
|
||||
// Only save if 1 second has past since the request was made
|
||||
if (saveRequestTimestamp > 0 && (System.currentTimeMillis() - saveRequestTimestamp) > 1000L) {
|
||||
saveRequestTimestamp = -1;
|
||||
logger.debug("Saving to disk...");
|
||||
saveToDisk();
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
if (saveRequestTimestamp > 0 && (System.currentTimeMillis() - saveRequestTimestamp) > 1000L) {
|
||||
saveRequestTimestamp = -1;
|
||||
logger.debug("Saving to disk...");
|
||||
saveToDisk();
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("Exception waiting for settings semaphor", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void unloadCameraConfigs() {
|
||||
this.config.getCameraConfigurations().clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.configuration;
|
||||
|
||||
public class HardwareSettings {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.configuration;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.dataflow;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.dataflow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.dataflow;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.dataflow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.dataflow;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.dataflow.events;
|
||||
|
||||
import org.photonvision.common.dataflow.DataChangeDestination;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.dataflow.events;
|
||||
|
||||
import org.photonvision.common.dataflow.DataChangeDestination;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.dataflow.events;
|
||||
|
||||
import io.javalin.websocket.WsContext;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.dataflow.events;
|
||||
|
||||
import io.javalin.websocket.WsContext;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.dataflow.networktables;
|
||||
|
||||
import edu.wpi.first.networktables.EntryListenerFlags;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.dataflow.networktables;
|
||||
|
||||
import edu.wpi.first.networktables.EntryNotification;
|
||||
@@ -24,10 +25,12 @@ import java.util.List;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import org.opencv.core.Point;
|
||||
import org.photonvision.common.dataflow.CVPipelineResultConsumer;
|
||||
import org.photonvision.common.dataflow.structures.Packet;
|
||||
import org.photonvision.targeting.PhotonPipelineResult;
|
||||
import org.photonvision.targeting.PhotonTrackedTarget;
|
||||
import org.photonvision.targeting.TargetCorner;
|
||||
import org.photonvision.vision.pipeline.result.CVPipelineResult;
|
||||
import org.photonvision.vision.target.TrackedTarget;
|
||||
|
||||
@@ -209,9 +212,20 @@ public class NTDataPublisher implements CVPipelineResultConsumer {
|
||||
public static List<PhotonTrackedTarget> simpleFromTrackedTargets(List<TrackedTarget> targets) {
|
||||
var ret = new ArrayList<PhotonTrackedTarget>();
|
||||
for (var t : targets) {
|
||||
var points = new Point[4];
|
||||
t.getMinAreaRect().points(points);
|
||||
var cornerList = new ArrayList<TargetCorner>();
|
||||
|
||||
for (int i = 0; i < 4; i++) cornerList.add(new TargetCorner(points[i].x, points[i].y));
|
||||
|
||||
ret.add(
|
||||
new PhotonTrackedTarget(
|
||||
t.getYaw(), t.getPitch(), t.getArea(), t.getSkew(), t.getCameraToTarget()));
|
||||
t.getYaw(),
|
||||
t.getPitch(),
|
||||
t.getArea(),
|
||||
t.getSkew(),
|
||||
t.getCameraToTarget(),
|
||||
cornerList));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -14,22 +14,15 @@
|
||||
* 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.common.dataflow.networktables;
|
||||
|
||||
import edu.wpi.first.cscore.CameraServerJNI;
|
||||
import edu.wpi.first.networktables.LogMessage;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import org.photonvision.PhotonVersion;
|
||||
import org.photonvision.common.configuration.ConfigManager;
|
||||
import org.photonvision.common.configuration.NetworkConfig;
|
||||
import org.photonvision.common.dataflow.DataChangeService;
|
||||
import org.photonvision.common.dataflow.events.OutgoingUIEvent;
|
||||
@@ -86,6 +79,7 @@ public class NetworkTablesManager {
|
||||
private void broadcastConnectedStatusImpl() {
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
var subMap = new HashMap<String, Object>();
|
||||
|
||||
subMap.put("connected", ntInstance.isConnected());
|
||||
if (ntInstance.isConnected()) {
|
||||
var connections = getInstance().ntInstance.getConnections();
|
||||
@@ -98,73 +92,6 @@ public class NetworkTablesManager {
|
||||
map.put("ntConnectionInfo", subMap);
|
||||
DataChangeService.getInstance()
|
||||
.publishEvent(new OutgoingUIEvent<>("networkTablesConnected", map));
|
||||
|
||||
// Seperate from the above so we don't hold stuff up
|
||||
System.setProperty("java.net.preferIPv4Stack", "true");
|
||||
subMap.put(
|
||||
"deviceips",
|
||||
Arrays.stream(CameraServerJNI.getNetworkInterfaces())
|
||||
.filter(it -> !it.equals("0.0.0.0"))
|
||||
.toArray());
|
||||
logger.info("Searching for rios");
|
||||
List<String> possibleRioList = new ArrayList<>();
|
||||
for (var ip : CameraServerJNI.getNetworkInterfaces()) {
|
||||
logger.info("Trying " + ip);
|
||||
var possibleRioAddr = getPossibleRioAddress(ip);
|
||||
if (possibleRioAddr != null) {
|
||||
logger.info("Maybe found " + ip);
|
||||
searchForHost(possibleRioList, possibleRioAddr);
|
||||
} else {
|
||||
logger.info("Didn't match RIO IP");
|
||||
}
|
||||
}
|
||||
String name =
|
||||
"roboRIO-"
|
||||
+ ConfigManager.getInstance().getConfig().getNetworkConfig().teamNumber
|
||||
+ "-FRC.local";
|
||||
searchForHost(possibleRioList, name);
|
||||
name =
|
||||
"roboRIO-"
|
||||
+ ConfigManager.getInstance().getConfig().getNetworkConfig().teamNumber
|
||||
+ "-FRC.lan";
|
||||
searchForHost(possibleRioList, name);
|
||||
name =
|
||||
"roboRIO-"
|
||||
+ ConfigManager.getInstance().getConfig().getNetworkConfig().teamNumber
|
||||
+ "-FRC.frc-field.local";
|
||||
searchForHost(possibleRioList, name);
|
||||
subMap.put("possibleRios", possibleRioList.toArray());
|
||||
DataChangeService.getInstance()
|
||||
.publishEvent(new OutgoingUIEvent<>("networkTablesConnected", map));
|
||||
}
|
||||
|
||||
String getPossibleRioAddress(String ip) {
|
||||
try {
|
||||
InetAddress addr = InetAddress.getByName(ip);
|
||||
var address = addr.getAddress();
|
||||
if (address[0] != (byte) (10 & 0xff)) return null;
|
||||
address[3] = (byte) (2 & 0xff);
|
||||
return InetAddress.getByAddress(address).getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void searchForHost(List<String> list, String hostname) {
|
||||
try {
|
||||
logger.info("Looking up " + hostname);
|
||||
InetAddress testAddr = InetAddress.getByName(hostname);
|
||||
logger.info("Pinging " + hostname);
|
||||
var canContact = testAddr.isReachable(500);
|
||||
if (canContact) {
|
||||
logger.info("Was able to connect to " + hostname);
|
||||
if (!list.contains(hostname)) list.add(hostname);
|
||||
} else {
|
||||
logger.info("Unable to reach " + hostname);
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private void broadcastVersion() {
|
||||
@@ -185,8 +112,7 @@ public class NetworkTablesManager {
|
||||
logger.info("Starting NT Client");
|
||||
ntInstance.stopServer();
|
||||
|
||||
// ntInstance.startClientTeam(teamNumber);
|
||||
ntInstance.startClient("localhost");
|
||||
ntInstance.startClientTeam(teamNumber);
|
||||
ntInstance.startDSClient();
|
||||
if (ntInstance.isConnected()) {
|
||||
logger.info("[NetworkTablesManager] Connected to the robot!");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.dataflow.websocket;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.hardware.GPIO;
|
||||
|
||||
import org.photonvision.common.configuration.HardwareConfig;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.hardware.GPIO;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.hardware.GPIO.pi;
|
||||
|
||||
@SuppressWarnings("SpellCheckingInspection")
|
||||
|
||||
@@ -14,16 +14,17 @@
|
||||
* 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.common.hardware.GPIO.pi;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* A class that defines the exceptions that can be thrown by Pigpio.
|
||||
*
|
||||
* <p>Credit to nkolban
|
||||
* https://github.com/nkolban/jpigpio/blob/master/JPigpio/src/jpigpio/PigpioException.java
|
||||
*/
|
||||
* A class that defines the exceptions that can be thrown by Pigpio.
|
||||
*
|
||||
* <p>Credit to nkolban
|
||||
* https://github.com/nkolban/jpigpio/blob/master/JPigpio/src/jpigpio/PigpioException.java
|
||||
*/
|
||||
@SuppressWarnings({"SpellCheckingInspection", "unused", "RedundantSuppression"})
|
||||
public class PigpioException extends Exception {
|
||||
private int rc = -99999999;
|
||||
@@ -65,10 +66,10 @@ public class PigpioException extends Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the error code that was returned by the underlying Pigpio call.
|
||||
*
|
||||
* @return The error code that was returned by the underlying Pigpio call.
|
||||
*/
|
||||
* Retrieve the error code that was returned by the underlying Pigpio call.
|
||||
*
|
||||
* @return The error code that was returned by the underlying Pigpio call.
|
||||
*/
|
||||
public int getErrorCode() {
|
||||
return rc;
|
||||
} // End of getErrorCode
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.hardware.GPIO.pi;
|
||||
|
||||
import static org.photonvision.common.hardware.GPIO.pi.PigpioException.*;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.hardware.GPIO.pi;
|
||||
|
||||
public class PigpioPulse {
|
||||
@@ -22,14 +23,14 @@ public class PigpioPulse {
|
||||
int delayMicros;
|
||||
|
||||
/**
|
||||
* Initialises a pulse.
|
||||
*
|
||||
* @param gpioOn GPIO number to switch on at the start of the pulse. If zero, then no GPIO will be
|
||||
* switched on.
|
||||
* @param gpioOff GPIO number to switch off at the start of the pulse. If zero, then no GPIO will
|
||||
* be switched off.
|
||||
* @param delayMicros the delay in microseconds before the next pulse.
|
||||
*/
|
||||
* Initialises a pulse.
|
||||
*
|
||||
* @param gpioOn GPIO number to switch on at the start of the pulse. If zero, then no GPIO will be
|
||||
* switched on.
|
||||
* @param gpioOff GPIO number to switch off at the start of the pulse. If zero, then no GPIO will
|
||||
* be switched off.
|
||||
* @param delayMicros the delay in microseconds before the next pulse.
|
||||
*/
|
||||
public PigpioPulse(int gpioOn, int gpioOff, int delayMicros) {
|
||||
this.gpioOn = gpioOn != 0 ? 1 << gpioOn : 0;
|
||||
this.gpioOff = gpioOff != 0 ? 1 << gpioOff : 0;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.hardware.GPIO.pi;
|
||||
|
||||
import static org.photonvision.common.hardware.GPIO.pi.PigpioException.*;
|
||||
@@ -40,12 +41,12 @@ public class PigpioSocket {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and starts a socket connection to a pigpio daemon on a remote host with the specified
|
||||
* address and port
|
||||
*
|
||||
* @param addr Address of remote pigpio daemon
|
||||
* @param port Port of remote pigpio daemon
|
||||
*/
|
||||
* Creates and starts a socket connection to a pigpio daemon on a remote host with the specified
|
||||
* address and port
|
||||
*
|
||||
* @param addr Address of remote pigpio daemon
|
||||
* @param port Port of remote pigpio daemon
|
||||
*/
|
||||
public PigpioSocket(String addr, int port) {
|
||||
try {
|
||||
commandSocket = new PigpioSocketLock(addr, port);
|
||||
@@ -55,10 +56,10 @@ public class PigpioSocket {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconnects to the pigpio daemon
|
||||
*
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
* Reconnects to the pigpio daemon
|
||||
*
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
public void reconnect() throws PigpioException {
|
||||
try {
|
||||
commandSocket.reconnect();
|
||||
@@ -69,10 +70,10 @@ public class PigpioSocket {
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminates the connection to the pigpio daemon
|
||||
*
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
* Terminates the connection to the pigpio daemon
|
||||
*
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
public void gpioTerminate() throws PigpioException {
|
||||
try {
|
||||
commandSocket.terminate();
|
||||
@@ -83,12 +84,12 @@ public class PigpioSocket {
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the GPIO level
|
||||
*
|
||||
* @param pin Pin to read from
|
||||
* @return Value of the pin
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
* Read the GPIO level
|
||||
*
|
||||
* @param pin Pin to read from
|
||||
* @return Value of the pin
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
public boolean gpioRead(int pin) throws PigpioException {
|
||||
try {
|
||||
int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_READ.value, pin);
|
||||
@@ -101,12 +102,12 @@ public class PigpioSocket {
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the GPIO level
|
||||
*
|
||||
* @param pin Pin to write to
|
||||
* @param value Value to write
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
* Write the GPIO level
|
||||
*
|
||||
* @param pin Pin to write to
|
||||
* @param value Value to write
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
public void gpioWrite(int pin, boolean value) throws PigpioException {
|
||||
try {
|
||||
int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_WRITE.value, pin, value ? 1 : 0);
|
||||
@@ -118,10 +119,10 @@ public class PigpioSocket {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all waveforms and any data added by calls to {@link #waveAddGeneric(ArrayList)}
|
||||
*
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
* Clears all waveforms and any data added by calls to {@link #waveAddGeneric(ArrayList)}
|
||||
*
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
public void waveClear() throws PigpioException {
|
||||
try {
|
||||
int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_WVCLR.value);
|
||||
@@ -133,12 +134,12 @@ public class PigpioSocket {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a number of pulses to the current waveform
|
||||
*
|
||||
* @param pulses ArrayList of pulses to add
|
||||
* @return the new total number of pulses in the current waveform
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
* Adds a number of pulses to the current waveform
|
||||
*
|
||||
* @param pulses ArrayList of pulses to add
|
||||
* @return the new total number of pulses in the current waveform
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
private int waveAddGeneric(ArrayList<PigpioPulse> pulses) throws PigpioException {
|
||||
// pigpio wave message format
|
||||
|
||||
@@ -174,12 +175,12 @@ public class PigpioSocket {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates pulses and adds them to the current waveform
|
||||
*
|
||||
* @param pulseTimeMillis Pulse length in milliseconds
|
||||
* @param blinks Number of times to pulse. -1 for repeat
|
||||
* @param pinNo Pin to pulse
|
||||
*/
|
||||
* Creates pulses and adds them to the current waveform
|
||||
*
|
||||
* @param pulseTimeMillis Pulse length in milliseconds
|
||||
* @param blinks Number of times to pulse. -1 for repeat
|
||||
* @param pinNo Pin to pulse
|
||||
*/
|
||||
private void addBlinkPulsesToWaveform(int pulseTimeMillis, int blinks, int pinNo) {
|
||||
boolean repeat = blinks == -1;
|
||||
|
||||
@@ -207,13 +208,13 @@ public class PigpioSocket {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and sends a waveform to the given pins with the specified parameters.
|
||||
*
|
||||
* @param pulseTimeMillis Pulse length in milliseconds
|
||||
* @param blinks Number of times to pulse. -1 for repeat
|
||||
* @param pins Pins to pulse
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
* Generates and sends a waveform to the given pins with the specified parameters.
|
||||
*
|
||||
* @param pulseTimeMillis Pulse length in milliseconds
|
||||
* @param blinks Number of times to pulse. -1 for repeat
|
||||
* @param pins Pins to pulse
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
public void generateAndSendWaveform(int pulseTimeMillis, int blinks, int... pins)
|
||||
throws PigpioException {
|
||||
if (pins.length == 0) return;
|
||||
@@ -262,11 +263,11 @@ public class PigpioSocket {
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the transmission of the current waveform
|
||||
*
|
||||
* @return success
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
* Stops the transmission of the current waveform
|
||||
*
|
||||
* @return success
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
public boolean waveTxStop() throws PigpioException {
|
||||
try {
|
||||
int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_WVHLT.value);
|
||||
@@ -279,12 +280,12 @@ public class PigpioSocket {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a waveform from the data provided by the prior calls to {@link
|
||||
* #waveAddGeneric(ArrayList)} Upon success a wave ID greater than or equal to 0 is returned
|
||||
*
|
||||
* @return ID of the created waveform
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
* Creates a waveform from the data provided by the prior calls to {@link
|
||||
* #waveAddGeneric(ArrayList)} Upon success a wave ID greater than or equal to 0 is returned
|
||||
*
|
||||
* @return ID of the created waveform
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
public int waveCreate() throws PigpioException {
|
||||
try {
|
||||
int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_WVCRE.value);
|
||||
@@ -297,11 +298,11 @@ public class PigpioSocket {
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the waveform with specified wave ID
|
||||
*
|
||||
* @param waveId ID of the waveform to delete
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
* Deletes the waveform with specified wave ID
|
||||
*
|
||||
* @param waveId ID of the waveform to delete
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
public void waveDelete(int waveId) throws PigpioException {
|
||||
try {
|
||||
int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_WVDEL.value, waveId);
|
||||
@@ -313,12 +314,12 @@ public class PigpioSocket {
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmits the waveform with specified wave ID. The waveform is sent once
|
||||
*
|
||||
* @param waveId ID of the waveform to transmit
|
||||
* @return The number of DMA control blocks in the waveform
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
* Transmits the waveform with specified wave ID. The waveform is sent once
|
||||
*
|
||||
* @param waveId ID of the waveform to transmit
|
||||
* @return The number of DMA control blocks in the waveform
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
public int waveSendOnce(int waveId) throws PigpioException {
|
||||
try {
|
||||
int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_WVTX.value, waveId);
|
||||
@@ -330,13 +331,13 @@ public class PigpioSocket {
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmits the waveform with specified wave ID. The waveform cycles until cancelled (either by
|
||||
* the sending of a new waveform or {@link #waveTxStop()}
|
||||
*
|
||||
* @param waveId ID of the waveform to transmit
|
||||
* @return The number of DMA control blocks in the waveform
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
* Transmits the waveform with specified wave ID. The waveform cycles until cancelled (either by
|
||||
* the sending of a new waveform or {@link #waveTxStop()}
|
||||
*
|
||||
* @param waveId ID of the waveform to transmit
|
||||
* @return The number of DMA control blocks in the waveform
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
public int waveSendRepeat(int waveId) throws PigpioException {
|
||||
try {
|
||||
int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_WVTXR.value, waveId);
|
||||
@@ -348,14 +349,14 @@ public class PigpioSocket {
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts hardware PWM on a GPIO at the specified frequency and dutycycle
|
||||
*
|
||||
* @param pin GPIO pin to start PWM on
|
||||
* @param pwmFrequency Frequency to run at (1Hz-125MHz). Frequencies above 30MHz are unlikely to
|
||||
* work
|
||||
* @param pwmDuty Duty cycle to run at (0-1,000,000)
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
* Starts hardware PWM on a GPIO at the specified frequency and dutycycle
|
||||
*
|
||||
* @param pin GPIO pin to start PWM on
|
||||
* @param pwmFrequency Frequency to run at (1Hz-125MHz). Frequencies above 30MHz are unlikely to
|
||||
* work
|
||||
* @param pwmDuty Duty cycle to run at (0-1,000,000)
|
||||
* @throws PigpioException on failure
|
||||
*/
|
||||
public void hardwarePWM(int pin, int pwmFrequency, int pwmDuty) throws PigpioException {
|
||||
try {
|
||||
ByteBuffer bb = ByteBuffer.allocate(4);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.hardware.GPIO.pi;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
@@ -23,9 +24,9 @@ import java.net.Socket;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Credit to nkolban
|
||||
* https://github.com/nkolban/jpigpio/blob/master/JPigpio/src/jpigpio/SocketLock.java
|
||||
*/
|
||||
* Credit to nkolban
|
||||
* https://github.com/nkolban/jpigpio/blob/master/JPigpio/src/jpigpio/SocketLock.java
|
||||
*/
|
||||
final class PigpioSocketLock {
|
||||
private static final int replyTimeoutMillis = 1000;
|
||||
|
||||
@@ -81,16 +82,16 @@ final class PigpioSocketLock {
|
||||
}
|
||||
|
||||
/**
|
||||
* Send extended command to pigpiod and return result code
|
||||
*
|
||||
* @param cmd Command to send
|
||||
* @param p1 Command parameter 1
|
||||
* @param p2 Command parameter 2
|
||||
* @param p3 Command parameter 3 (usually length of extended data - see paramater ext)
|
||||
* @param ext Array of bytes containing extended data
|
||||
* @return Command result code
|
||||
* @throws IOException in case of network connection error
|
||||
*/
|
||||
* Send extended command to pigpiod and return result code
|
||||
*
|
||||
* @param cmd Command to send
|
||||
* @param p1 Command parameter 1
|
||||
* @param p2 Command parameter 2
|
||||
* @param p3 Command parameter 3 (usually length of extended data - see paramater ext)
|
||||
* @param ext Array of bytes containing extended data
|
||||
* @return Command result code
|
||||
* @throws IOException in case of network connection error
|
||||
*/
|
||||
@SuppressWarnings("UnusedAssignment")
|
||||
public synchronized int sendCmd(int cmd, int p1, int p2, int p3, byte[] ext) throws IOException {
|
||||
ByteBuffer bb = ByteBuffer.allocate(16 + ext.length);
|
||||
@@ -135,11 +136,11 @@ final class PigpioSocketLock {
|
||||
}
|
||||
|
||||
/**
|
||||
* Read all remaining bytes coming from pigpiod
|
||||
*
|
||||
* @param data Array to store read bytes.
|
||||
* @throws IOException if unable to read from network
|
||||
*/
|
||||
* Read all remaining bytes coming from pigpiod
|
||||
*
|
||||
* @param data Array to store read bytes.
|
||||
* @throws IOException if unable to read from network
|
||||
*/
|
||||
public void readBytes(byte[] data) throws IOException {
|
||||
in.readFully(data);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.hardware;
|
||||
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.common.hardware;
|
||||
|
||||
public enum PiVersion {
|
||||
PI_B("Pi Model B"),
|
||||
COMPUTE_MODULE("Compute Module Rev"),
|
||||
ZERO_W("Pi Zero W Rev 1.1"),
|
||||
ZERO_2_W("Raspberry Pi Zero 2"),
|
||||
PI_3("Pi 3"),
|
||||
PI_4("Pi 4"),
|
||||
COMPUTE_MODULE_3("Compute Module 3"),
|
||||
UNKNOWN("UNKNOWN");
|
||||
|
||||
private final String identifier;
|
||||
|
||||
PiVersion(String s) {
|
||||
this.identifier = s.toLowerCase();
|
||||
}
|
||||
|
||||
public static PiVersion getPiVersion() {
|
||||
if (!Platform.isRaspberryPi()) return PiVersion.UNKNOWN;
|
||||
String piString = Platform.currentPiVersionStr;
|
||||
for (PiVersion p : PiVersion.values()) {
|
||||
if (piString.toLowerCase().contains(p.identifier)) return p;
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.hardware;
|
||||
|
||||
import edu.wpi.first.util.RuntimeDetector;
|
||||
@@ -45,7 +46,11 @@ public enum Platform {
|
||||
|
||||
private static final String OS_NAME = System.getProperty("os.name");
|
||||
private static final String OS_ARCH = System.getProperty("os.arch");
|
||||
public static final Platform CurrentPlatform = getCurrentPlatform();
|
||||
|
||||
// These are queried on init and should never change after
|
||||
public static final Platform currentPlatform = getCurrentPlatform();
|
||||
protected static final String currentPiVersionStr = getPiVersionString();
|
||||
public static final PiVersion currentPiVersion = PiVersion.getPiVersion();
|
||||
|
||||
private static String UnknownPlatformString =
|
||||
String.format("Unknown Platform. OS: %s, Architecture: %s", OS_NAME, OS_ARCH);
|
||||
@@ -61,7 +66,7 @@ public enum Platform {
|
||||
}
|
||||
|
||||
public static boolean isRaspberryPi() {
|
||||
return CurrentPlatform.equals(LINUX_RASPBIAN);
|
||||
return currentPlatform.equals(LINUX_RASPBIAN);
|
||||
}
|
||||
|
||||
@SuppressWarnings("StatementWithEmptyBody")
|
||||
@@ -114,4 +119,22 @@ public enum Platform {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
// Querry /proc/device-tree/model. This should return the model of the pi
|
||||
// Versions here:
|
||||
// https://github.com/raspberrypi/linux/blob/rpi-5.10.y/arch/arm/boot/dts/bcm2710-rpi-cm3.dts
|
||||
private static String getPiVersionString() {
|
||||
if (!isRaspberryPi()) return "";
|
||||
try {
|
||||
shell.executeBashCommand("cat /proc/device-tree/model");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (shell.getExitCode() == 0) {
|
||||
// We expect it to be in the format "raspberry pi X model X"
|
||||
return shell.getOutput();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.hardware;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.hardware;
|
||||
|
||||
import edu.wpi.first.networktables.EntryNotification;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.hardware.metrics;
|
||||
|
||||
public class CPUMetrics extends MetricsBase {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.hardware.metrics;
|
||||
|
||||
public class DiskMetrics extends MetricsBase {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.hardware.metrics;
|
||||
|
||||
public class GPUMetrics extends MetricsBase {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.hardware.metrics;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.hardware.metrics;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.hardware.metrics;
|
||||
|
||||
public class RAMMetrics extends MetricsBase {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.logging;
|
||||
|
||||
public enum LogGroup {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.logging;
|
||||
|
||||
public enum LogLevel {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.logging;
|
||||
|
||||
import java.io.*;
|
||||
@@ -125,8 +126,9 @@ public class Logger {
|
||||
}
|
||||
|
||||
public static void cleanLogs(Path folderToClean) {
|
||||
LinkedList<File> logFileList =
|
||||
new LinkedList<>(Arrays.asList(folderToClean.toFile().listFiles()));
|
||||
File[] logs = folderToClean.toFile().listFiles();
|
||||
if (logs == null) return;
|
||||
LinkedList<File> logFileList = new LinkedList<>(Arrays.asList(logs));
|
||||
HashMap<File, Date> logFileStartDateMap = new HashMap<>();
|
||||
|
||||
// Remove any files from the list for which we can't parse a start date from their name.
|
||||
@@ -231,12 +233,12 @@ public class Logger {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an error message with the stack trace of a Throwable. The stacktrace will only be printed
|
||||
* if the current LogLevel is TRACE
|
||||
*
|
||||
* @param message
|
||||
* @param t
|
||||
*/
|
||||
* Logs an error message with the stack trace of a Throwable. The stacktrace will only be printed
|
||||
* if the current LogLevel is TRACE
|
||||
*
|
||||
* @param message
|
||||
* @param t
|
||||
*/
|
||||
public void error(String message, Throwable t) {
|
||||
log(message, LogLevel.ERROR);
|
||||
log(convertStackTraceToString(t), LogLevel.ERROR, LogLevel.DEBUG);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.networking;
|
||||
|
||||
import java.net.InterfaceAddress;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.networking;
|
||||
|
||||
import org.photonvision.common.configuration.ConfigManager;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.networking;
|
||||
|
||||
public enum NetworkMode {
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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.common.networking;
|
||||
|
||||
import edu.wpi.first.cscore.CameraServerJNI;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import org.photonvision.common.dataflow.DataChangeService;
|
||||
import org.photonvision.common.dataflow.events.OutgoingUIEvent;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
|
||||
public class RoborioFinder {
|
||||
private static RoborioFinder instance;
|
||||
private static final Logger logger = new Logger(RoborioFinder.class, LogGroup.General);
|
||||
|
||||
public static RoborioFinder getInstance() {
|
||||
if (instance == null) instance = new RoborioFinder();
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void findRios() {
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
var subMap = new HashMap<String, Object>();
|
||||
// Seperate from the above so we don't hold stuff up
|
||||
System.setProperty("java.net.preferIPv4Stack", "true");
|
||||
subMap.put(
|
||||
"deviceips",
|
||||
Arrays.stream(CameraServerJNI.getNetworkInterfaces())
|
||||
.filter(it -> !it.equals("0.0.0.0"))
|
||||
.toArray());
|
||||
logger.info("Searching for rios");
|
||||
List<String> possibleRioList = new ArrayList<>();
|
||||
for (var ip : CameraServerJNI.getNetworkInterfaces()) {
|
||||
logger.info("Trying " + ip);
|
||||
var possibleRioAddr = getPossibleRioAddress(ip);
|
||||
if (possibleRioAddr != null) {
|
||||
logger.info("Maybe found " + ip);
|
||||
searchForHost(possibleRioList, possibleRioAddr);
|
||||
} else {
|
||||
logger.info("Didn't match RIO IP");
|
||||
}
|
||||
}
|
||||
|
||||
// String name =
|
||||
// "roboRIO-"
|
||||
// +
|
||||
// ConfigManager.getInstance().getConfig().getNetworkConfig().teamNumber
|
||||
// + "-FRC.local";
|
||||
// searchForHost(possibleRioList, name);
|
||||
// name =
|
||||
// "roboRIO-"
|
||||
// +
|
||||
// ConfigManager.getInstance().getConfig().getNetworkConfig().teamNumber
|
||||
// + "-FRC.lan";
|
||||
// searchForHost(possibleRioList, name);
|
||||
// name =
|
||||
// "roboRIO-"
|
||||
// +
|
||||
// ConfigManager.getInstance().getConfig().getNetworkConfig().teamNumber
|
||||
// + "-FRC.frc-field.local";
|
||||
// searchForHost(possibleRioList, name);
|
||||
// subMap.put("possibleRios", possibleRioList.toArray());
|
||||
|
||||
subMap.put("possibleRios", possibleRioList.toArray());
|
||||
map.put("networkInfo", subMap);
|
||||
DataChangeService.getInstance().publishEvent(new OutgoingUIEvent<>("deviceIpInfo", map));
|
||||
}
|
||||
|
||||
String getPossibleRioAddress(String ip) {
|
||||
try {
|
||||
InetAddress addr = InetAddress.getByName(ip);
|
||||
var address = addr.getAddress();
|
||||
if (address[0] != (byte) (10 & 0xff)) return null;
|
||||
address[3] = (byte) (2 & 0xff);
|
||||
return InetAddress.getByAddress(address).getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void searchForHost(List<String> list, String hostname) {
|
||||
try {
|
||||
logger.info("Looking up " + hostname);
|
||||
InetAddress testAddr = InetAddress.getByName(hostname);
|
||||
logger.info("Pinging " + hostname);
|
||||
var canContact = testAddr.isReachable(500);
|
||||
if (canContact) {
|
||||
logger.info("Was able to connect to " + hostname);
|
||||
if (!list.contains(hostname)) list.add(hostname);
|
||||
} else {
|
||||
logger.info("Unable to reach " + hostname);
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.scripting;
|
||||
|
||||
public enum ScriptCommandType {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.scripting;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.scripting;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.scripting;
|
||||
|
||||
public enum ScriptEventType {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.scripting;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -65,8 +66,7 @@ public class ScriptManager {
|
||||
|
||||
private void handleEvent(ScriptEventType eventType) {
|
||||
var toRun =
|
||||
events
|
||||
.parallelStream()
|
||||
events.parallelStream()
|
||||
.filter(e -> e.config.eventType == eventType)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
@@ -128,7 +128,7 @@ public class ScriptManager {
|
||||
}
|
||||
|
||||
public static void queueEvent(ScriptEventType eventType) {
|
||||
if (!Platform.CurrentPlatform.isWindows()) {
|
||||
if (!Platform.currentPlatform.isWindows()) {
|
||||
try {
|
||||
queuedEvents.putLast(eventType);
|
||||
logger.info("Queued event: " + eventType.name());
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.util;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.util;
|
||||
|
||||
public class MemoryManager {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.util;
|
||||
|
||||
public class ReflectionUtils {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.util;
|
||||
|
||||
import java.io.*;
|
||||
@@ -43,12 +44,12 @@ public class ShellExec {
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a bash command. We can handle complex bash commands including multiple executions (; |
|
||||
* and ||), quotes, expansions ($), escapes (\), e.g.: "cd /abc/def; mv ghi 'older ghi '$(whoami)"
|
||||
*
|
||||
* @param command Bash command to execute
|
||||
* @return true if bash got started, but your command may have failed.
|
||||
*/
|
||||
* Execute a bash command. We can handle complex bash commands including multiple executions (; |
|
||||
* and ||), quotes, expansions ($), escapes (\), e.g.: "cd /abc/def; mv ghi 'older ghi '$(whoami)"
|
||||
*
|
||||
* @param command Bash command to execute
|
||||
* @return true if bash got started, but your command may have failed.
|
||||
*/
|
||||
public int executeBashCommand(String command, boolean wait) throws IOException {
|
||||
logger.debug("Executing \"" + command + "\"");
|
||||
|
||||
@@ -71,25 +72,25 @@ public class ShellExec {
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a command in current folder, and wait for process to end
|
||||
*
|
||||
* @param command command ("c:/some/folder/script.bat" or "some/folder/script.sh")
|
||||
* @param args 0..n command line arguments
|
||||
* @return process exit code
|
||||
*/
|
||||
* Execute a command in current folder, and wait for process to end
|
||||
*
|
||||
* @param command command ("c:/some/folder/script.bat" or "some/folder/script.sh")
|
||||
* @param args 0..n command line arguments
|
||||
* @return process exit code
|
||||
*/
|
||||
public int execute(String command, String... args) throws IOException {
|
||||
return execute(command, null, true, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a command.
|
||||
*
|
||||
* @param command command ("c:/some/folder/script.bat" or "some/folder/script.sh")
|
||||
* @param workdir working directory or NULL to use command folder
|
||||
* @param wait wait for process to end
|
||||
* @param args 0..n command line arguments
|
||||
* @return process exit code
|
||||
*/
|
||||
* Execute a command.
|
||||
*
|
||||
* @param command command ("c:/some/folder/script.bat" or "some/folder/script.sh")
|
||||
* @param workdir working directory or NULL to use command folder
|
||||
* @param wait wait for process to end
|
||||
* @param args 0..n command line arguments
|
||||
* @return process exit code
|
||||
*/
|
||||
public int execute(String command, String workdir, boolean wait, String... args)
|
||||
throws IOException {
|
||||
String[] cmdArr;
|
||||
@@ -153,10 +154,10 @@ public class ShellExec {
|
||||
// ********************************************
|
||||
|
||||
/**
|
||||
* StreamGobbler reads inputstream to "gobble" it. This is used by Executor class when running a
|
||||
* commandline applications. Gobblers must read/purge INSTR and ERRSTR process streams.
|
||||
* http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=4
|
||||
*/
|
||||
* StreamGobbler reads inputstream to "gobble" it. This is used by Executor class when running a
|
||||
* commandline applications. Gobblers must read/purge INSTR and ERRSTR process streams.
|
||||
* http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=4
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
private static class StreamGobbler extends Thread {
|
||||
private InputStream is;
|
||||
@@ -186,19 +187,19 @@ public class ShellExec {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get inputstream buffer or null if stream was not consumed.
|
||||
*
|
||||
* @return Output stream
|
||||
*/
|
||||
* Get inputstream buffer or null if stream was not consumed.
|
||||
*
|
||||
* @return Output stream
|
||||
*/
|
||||
public String getOutput() {
|
||||
return (output != null ? output.toString() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is input stream completed.
|
||||
*
|
||||
* @return if input stream is completed
|
||||
*/
|
||||
* Is input stream completed.
|
||||
*
|
||||
* @return if input stream is completed
|
||||
*/
|
||||
public boolean isCompleted() {
|
||||
return completed;
|
||||
}
|
||||
|
||||
@@ -14,10 +14,12 @@
|
||||
* 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.common.util;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import edu.wpi.first.cscore.CameraServerCvJNI;
|
||||
import edu.wpi.first.math.util.Units;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -97,6 +99,26 @@ public class TestUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public enum WPI2022Image {
|
||||
kTerminal12ft6in(Units.feetToMeters(12.5)),
|
||||
kTerminal22ft6in(Units.feetToMeters(22.5));
|
||||
|
||||
public static double FOV = 68.5;
|
||||
|
||||
public final double distanceMeters;
|
||||
public final Path path;
|
||||
|
||||
Path getPath() {
|
||||
var filename = this.toString().substring(1).replace('_', '-');
|
||||
return Path.of("2022", "WPI", filename + ".png");
|
||||
}
|
||||
|
||||
WPI2022Image(double distanceMeters) {
|
||||
this.distanceMeters = distanceMeters;
|
||||
this.path = getPath();
|
||||
}
|
||||
}
|
||||
|
||||
public enum PolygonTestImages {
|
||||
kPolygons;
|
||||
|
||||
@@ -133,7 +155,8 @@ public class TestUtils {
|
||||
}
|
||||
|
||||
private static Path getResourcesFolderPath(boolean testMode) {
|
||||
return Path.of("../test-resources").toAbsolutePath();
|
||||
System.out.println("CWD: " + Path.of("").toAbsolutePath().toString());
|
||||
return Path.of("test-resources").toAbsolutePath();
|
||||
}
|
||||
|
||||
public static Path getTestMode2019ImagePath() {
|
||||
@@ -148,6 +171,12 @@ public class TestUtils {
|
||||
.resolve(WPI2020Image.kBlueGoal_108in_Center.path);
|
||||
}
|
||||
|
||||
public static Path getTestMode2022ImagePath() {
|
||||
return getResourcesFolderPath(true)
|
||||
.resolve("testimages")
|
||||
.resolve(WPI2022Image.kTerminal22ft6in.path);
|
||||
}
|
||||
|
||||
public static Path getTestImagesPath(boolean testMode) {
|
||||
return getResourcesFolderPath(testMode).resolve("testimages");
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.util;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
@@ -14,12 +14,14 @@
|
||||
* 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.common.util.file;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.PosixFileAttributes;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
@@ -60,7 +62,7 @@ public class FileUtils {
|
||||
public static void deleteFile(Path path) {
|
||||
try {
|
||||
Files.delete(path);
|
||||
} catch (FileNotFoundException fe) {
|
||||
} catch (FileNotFoundException | NoSuchFileException fe) {
|
||||
logger.warn("Tried to delete file \"" + path + "\" but it did not exist");
|
||||
} catch (IOException e) {
|
||||
logger.error("Exception deleting file \"" + path + "\"!", e);
|
||||
@@ -76,7 +78,7 @@ public class FileUtils {
|
||||
}
|
||||
|
||||
public static void setFilePerms(Path path) throws IOException {
|
||||
if (!Platform.CurrentPlatform.isWindows()) {
|
||||
if (!Platform.currentPlatform.isWindows()) {
|
||||
File thisFile = path.toFile();
|
||||
Set<PosixFilePermission> perms =
|
||||
Files.readAttributes(path, PosixFileAttributes.class).permissions();
|
||||
@@ -94,7 +96,7 @@ public class FileUtils {
|
||||
}
|
||||
|
||||
public static void setAllPerms(Path path) {
|
||||
if (!Platform.CurrentPlatform.isWindows()) {
|
||||
if (!Platform.currentPlatform.isWindows()) {
|
||||
String command = String.format("chmod 777 -R %s", path.toString());
|
||||
try {
|
||||
Process p = Runtime.getRuntime().exec(command);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.util.file;
|
||||
|
||||
import com.fasterxml.jackson.core.json.JsonReadFeature;
|
||||
@@ -33,7 +34,7 @@ import java.nio.file.Path;
|
||||
|
||||
public class JacksonUtils {
|
||||
public static <T> void serialize(Path path, T object) throws IOException {
|
||||
serialize(path, object, false);
|
||||
serialize(path, object, true);
|
||||
}
|
||||
|
||||
public static <T> void serialize(Path path, T object, boolean forceSync) throws IOException {
|
||||
@@ -79,7 +80,7 @@ public class JacksonUtils {
|
||||
|
||||
public static <T> void serialize(Path path, T object, Class<T> ref, StdSerializer<T> serializer)
|
||||
throws IOException {
|
||||
serialize(path, object, ref, serializer, false);
|
||||
serialize(path, object, ref, serializer, true);
|
||||
}
|
||||
|
||||
public static <T> void serialize(
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.common.util.file;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
public class ProgramDirectoryUtilities {
|
||||
private static String getJarName() {
|
||||
return new File(
|
||||
ProgramDirectoryUtilities.class
|
||||
.getProtectionDomain()
|
||||
.getCodeSource()
|
||||
.getLocation()
|
||||
.getPath())
|
||||
.getName();
|
||||
}
|
||||
|
||||
private static boolean runningFromJAR() {
|
||||
String jarName = getJarName();
|
||||
return jarName.contains(".jar");
|
||||
}
|
||||
|
||||
public static String getProgramDirectory() {
|
||||
if (runningFromJAR()) {
|
||||
return getCurrentJARDirectory();
|
||||
} else {
|
||||
return System.getProperty("user.dir");
|
||||
}
|
||||
}
|
||||
|
||||
private static String getCurrentJARDirectory() {
|
||||
try {
|
||||
return new File(
|
||||
ProgramDirectoryUtilities.class
|
||||
.getProtectionDomain()
|
||||
.getCodeSource()
|
||||
.getLocation()
|
||||
.toURI()
|
||||
.getPath())
|
||||
.getParent();
|
||||
} catch (URISyntaxException exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.util.java;
|
||||
|
||||
public interface TriConsumer<T, U, V> {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.util.math;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -14,9 +14,12 @@
|
||||
* 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.common.util.math;
|
||||
|
||||
import edu.wpi.first.util.WPIUtilJNI;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class MathUtils {
|
||||
MathUtils() {}
|
||||
@@ -63,20 +66,68 @@ public class MathUtils {
|
||||
return (int) Math.floor(map((double) value, inMin, inMax, outMin, outMax) + 0.5);
|
||||
}
|
||||
|
||||
public static long wpiNanoTime() {
|
||||
return microsToNanos(WPIUtilJNI.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* Linearly interpolates between two values.
|
||||
*
|
||||
* @param startValue The start value.
|
||||
* @param endValue The end value.
|
||||
* @param t The fraction for interpolation.
|
||||
* @return The interpolated value.
|
||||
*/
|
||||
* Get the value of the nTh percentile of a list
|
||||
*
|
||||
* @param list The list to evaluate
|
||||
* @param p The percentile, in [0,100]
|
||||
* @return
|
||||
*/
|
||||
public static double getPercentile(List<Double> list, double p) {
|
||||
if ((p > 100) || (p <= 0)) {
|
||||
throw new IllegalArgumentException("invalid quantile value: " + p);
|
||||
}
|
||||
|
||||
if (list.size() == 0) {
|
||||
return Double.NaN;
|
||||
}
|
||||
if (list.size() == 1) {
|
||||
return list.get(0); // always return single value for n = 1
|
||||
}
|
||||
|
||||
// Sort array. We avoid a third copy here by just creating the
|
||||
// list directly.
|
||||
double[] sorted = new double[list.size()];
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
sorted[i] = list.get(i);
|
||||
}
|
||||
Arrays.sort(sorted);
|
||||
|
||||
return evaluateSorted(sorted, p);
|
||||
}
|
||||
|
||||
private static double evaluateSorted(final double[] sorted, final double p) {
|
||||
double n = sorted.length;
|
||||
double pos = p * (n + 1) / 100;
|
||||
double fpos = Math.floor(pos);
|
||||
int intPos = (int) fpos;
|
||||
double dif = pos - fpos;
|
||||
|
||||
if (pos < 1) {
|
||||
return sorted[0];
|
||||
}
|
||||
if (pos >= n) {
|
||||
return sorted[sorted.length - 1];
|
||||
}
|
||||
double lower = sorted[intPos - 1];
|
||||
double upper = sorted[intPos];
|
||||
return lower + dif * (upper - lower);
|
||||
}
|
||||
|
||||
/**
|
||||
* Linearly interpolates between two values.
|
||||
*
|
||||
* @param startValue The start value.
|
||||
* @param endValue The end value.
|
||||
* @param t The fraction for interpolation.
|
||||
* @return The interpolated value.
|
||||
*/
|
||||
@SuppressWarnings("ParameterName")
|
||||
public static double lerp(double startValue, double endValue, double t) {
|
||||
return startValue + (endValue - startValue) * t;
|
||||
}
|
||||
|
||||
public static long wpiNanoTime() {
|
||||
return microsToNanos(WPIUtilJNI.now());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.util.numbers;
|
||||
|
||||
import org.opencv.core.Point;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.util.numbers;
|
||||
|
||||
public class IntegerCouple extends NumberCouple<Integer> {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.util.numbers;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.common.util.numbers;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -25,9 +26,9 @@ import java.util.StringJoiner;
|
||||
@SuppressWarnings("unused")
|
||||
public class NumberListUtils {
|
||||
/**
|
||||
* @param collection an ArrayList of Comparable objects
|
||||
* @return the median of collection
|
||||
*/
|
||||
* @param collection an ArrayList of Comparable objects
|
||||
* @return the median of collection
|
||||
*/
|
||||
public static <T extends Number> double median(List<T> collection, Comparator<T> comp) {
|
||||
double result;
|
||||
int n = collection.size() / 2;
|
||||
@@ -57,9 +58,9 @@ public class NumberListUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param collection an ArrayList of Numbers
|
||||
* @return the mean of collection
|
||||
*/
|
||||
* @param collection an ArrayList of Numbers
|
||||
* @return the mean of collection
|
||||
*/
|
||||
public static double mean(final List<? extends Number> collection) {
|
||||
BigDecimal sum = BigDecimal.ZERO;
|
||||
for (final Number number : collection) {
|
||||
@@ -69,11 +70,11 @@ public class NumberListUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param collection a collection of Comparable objects
|
||||
* @param n the position of the desired object, using the ordering defined on the collection
|
||||
* elements
|
||||
* @return the nth smallest object
|
||||
*/
|
||||
* @param collection a collection of Comparable objects
|
||||
* @param n the position of the desired object, using the ordering defined on the collection
|
||||
* elements
|
||||
* @return the nth smallest object
|
||||
*/
|
||||
public static <T> T nthSmallest(List<T> collection, int n, Comparator<T> comp) {
|
||||
T result, pivot;
|
||||
ArrayList<T> underPivot = new ArrayList<>(),
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.raspi;
|
||||
|
||||
import java.io.File;
|
||||
@@ -22,6 +23,7 @@ import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import org.photonvision.common.hardware.PiVersion;
|
||||
import org.photonvision.common.hardware.Platform;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
@@ -83,7 +85,13 @@ public class PicamJNI {
|
||||
}
|
||||
|
||||
public static boolean isSupported() {
|
||||
return libraryLoaded && !isVCSMSupported() && getSensorModel() != SensorModel.Disconnected;
|
||||
return libraryLoaded
|
||||
&& isVCSMSupported()
|
||||
&& getSensorModel() != SensorModel.Disconnected
|
||||
&& Platform.isRaspberryPi()
|
||||
&& (Platform.currentPiVersion == PiVersion.PI_3
|
||||
|| Platform.currentPiVersion == PiVersion.COMPUTE_MODULE_3
|
||||
|| Platform.currentPiVersion == PiVersion.ZERO_2_W);
|
||||
}
|
||||
|
||||
public static SensorModel getSensorModel() {
|
||||
@@ -109,29 +117,35 @@ public class PicamJNI {
|
||||
// Everything here is static because multiple picams are unsupported at the hardware level
|
||||
|
||||
/**
|
||||
* Called once for each video mode change. Starts a native thread running MMAL that stays alive
|
||||
* until destroyCamera is called.
|
||||
*
|
||||
* @return true on error.
|
||||
*/
|
||||
* Called once for each video mode change. Starts a native thread running MMAL that stays alive
|
||||
* until destroyCamera is called.
|
||||
*
|
||||
* @return true on error.
|
||||
*/
|
||||
public static native boolean createCamera(int width, int height, int fps);
|
||||
|
||||
/**
|
||||
* Destroys MMAL and EGL contexts. Called once for each video mode change *before* createCamera.
|
||||
*
|
||||
* @return true on error.
|
||||
*/
|
||||
* Destroys MMAL and EGL contexts. Called once for each video mode change *before* createCamera.
|
||||
*
|
||||
* @return true on error.
|
||||
*/
|
||||
public static native boolean destroyCamera();
|
||||
|
||||
public static native void setThresholds(
|
||||
double hL, double sL, double vL, double hU, double sU, double vU);
|
||||
|
||||
public static native void setInvertHue(boolean shouldInvert);
|
||||
|
||||
public static native boolean setExposure(int exposure);
|
||||
|
||||
public static native boolean setBrightness(int brightness);
|
||||
|
||||
// This adjusts the analog gain (normalized to 0-100); ignores the digital gain
|
||||
public static native boolean setGain(int gain);
|
||||
|
||||
// Adjusts the auto white balance gains, which are normalized 0-100 in the native code
|
||||
public static native boolean setAwbGain(int red, int blue);
|
||||
|
||||
public static native boolean setRotation(int rotation);
|
||||
|
||||
public static native void setShouldCopyColor(boolean shouldCopyColor);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.calibration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.calibration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.camera;
|
||||
|
||||
public enum CameraQuirk {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* 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.camera;
|
||||
|
||||
public enum CameraType {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user