Compare commits

...

87 Commits

Author SHA1 Message Date
Matt
da8d70f887 Ensure cameras with gain will always have it enabled (#388) 2022-01-11 17:12:30 -08:00
Tyler Veness
80a0d8de1c Fix test resources path (#386) 2022-01-10 21:40:43 -08:00
Matt
3ad476bc28 Send corners of min area rectangles (#382)
Adds a new corners entry to targets. Breaks byte-packed backwards compatibility. 

Co-authored-by: Tyler Veness <calcmogul@gmail.com>
2022-01-10 20:31:36 -08:00
Matt
e6d8e05b91 2022 grouping and test mode updates (#381)
Add 2022 images, "2orMore" grouping mode, and 2022 test mode
Test mode now preserves old settings
2022-01-10 16:51:06 -08:00
Tyler Veness
46fa17dfd8 Upgrade spotless and shadow (#385)
Fixes Log4J vulnerability
2022-01-10 11:56:45 -08:00
Matt
43c35286f3 Reword restart modal (#374)
Clarifies that photon will restart (when running as service)
2022-01-09 16:02:32 -08:00
Matt
3d317f7035 Switch to Releaser from eine/tip (#379) 2022-01-09 09:00:25 -08:00
Tyler Veness
a161bd5be9 Upgrade all maven dependencies for 2022 (#377)
This also fixes compilation with JDK 17.
2022-01-08 10:17:28 -08:00
Matt
0f730fc28d Update WPILib to 2022.1.1 rc 1 (#376) 2022-01-06 20:14:51 -08:00
Matt
12e06b09c3 Add Pi zero 2 W to PiVersion enum (#373)
This approach is quite brittle, but it's easy to get working and can ship in an initial 2022 release. It's necessary to prevent GPU acceleration from happening on Pi 4s though. Let's try to put together something better for future releases.
2022-01-05 20:34:42 -08:00
Matt
641101f574 Fix NetworkTables team connection bug (#375) 2021-12-31 22:55:32 -06:00
Matt
6a1201432c Hard-code picam resolutions (#366)
* Hard-code picam resolutions

* Address review comment
2021-12-30 23:19:25 -08:00
Matt
e77a06bfa6 Remove pipeline type select (#371) 2021-12-23 20:27:59 -08:00
dependabot[bot]
8b0b18bd07 Bump y18n from 4.0.0 to 4.0.3 in /photon-client (#372)
Bumps [y18n](https://github.com/yargs/y18n) from 4.0.0 to 4.0.3.
- [Release notes](https://github.com/yargs/y18n/releases)
- [Changelog](https://github.com/yargs/y18n/blob/y18n-v4.0.3/CHANGELOG.md)
- [Commits](https://github.com/yargs/y18n/compare/v4.0.0...y18n-v4.0.3)

---
updated-dependencies:
- dependency-name: y18n
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-20 10:39:21 -08:00
Matt
1766f3bf0f Add picam resolution type, remove k prefix (#365)
Changes "kUnknown" to "Picam" in resolution dropdown; similarly "kBGR" -> "BGR"
2021-12-20 10:08:17 -08:00
Matt
4578fa756c Null-check files to be cleaned in Logger (#367) 2021-12-19 06:52:13 -06:00
Matt
d6e1e28fc2 Add pi version check (#360)
Should prevent GPU acceleration on Pi 4
2021-12-18 13:12:53 -05:00
Matt
49048c3998 Don't limit divisors in driver mode (#363) 2021-12-18 12:56:52 -05:00
Matt
8b079d9b20 Add pi-only JAR createion (#362) 2021-12-18 12:53:08 -05:00
icemannie
1522adaa0e Remove pipeline index/driver mode/led mode caching in PhotonCamera 2021-12-15 12:16:09 -05:00
Matt
3cd57b8b43 Don't generate Pi images on PRs (#350) 2021-12-03 23:19:20 -05:00
Chris Gerth
c944967476 Offline Update (.jar replace) (#340)
Allows users to upload a new JAR to a Pi. Also bumps the pi image to increase the heap size.
2021-12-03 23:08:51 -05:00
Matt
dbd631da61 Suffix pi image zips with "-image.zip" (#339)
* Suffix pi image zips with "-image.zip"

* Update main.yml
2021-11-29 21:43:41 -06:00
mdurrani808
f731ae37d2 Updated README.md (#344) 2021-11-29 21:20:46 -06:00
Matt
0a8da1a0bd Fix image glob in dev releases (#338)
Should now upload pi images to dev releases
2021-11-28 18:32:33 -05:00
Matt
be5f0880c8 Update WPILib to 2022.1.1-beta-3-1-g4ba80a3 (#337)
This fixes raspberry pi crashes
2021-11-28 07:34:13 -06:00
Banks T
a02cd4a50e Explicitly specify JDK11 for photon-targeting (#334) 2021-11-27 00:22:51 -05:00
Tyler Veness
efd31543f6 Upgrade athena Docker container from 2021 to 2022 (#332)
The proper compiler wasn't available for 2022 native-utils, so
linuxathena artifact builds were silently skipped.
2021-11-26 09:44:05 -05:00
Matt
a151f23319 Add team number dialog, NT connected chip (#313)
Makes NT connection status visible from the UI
2021-11-25 15:43:29 -05:00
Matt
822811c853 Upload a new image on releases (#329)
Uploads a new Pi image (without any hardware configs) on releases and pushes to dev
2021-11-25 15:42:58 -05:00
Matt
23834c87f4 Only let in tags starting with "v" (#328) 2021-11-23 20:22:54 -05:00
Tyler Veness
f103a6b712 Run wpiformat (#327) 2021-11-23 19:09:51 -05:00
Chris Gerth
3257736ffa Add .pdf taraget generation (#307)
* Added rectangle target generation

* added dot grid support

* dot grid functional. Added branding and ruler

* comments, rebuild, testing

* proper branding rework

* undo index.html commit
2021-11-21 20:34:06 -05:00
Tyler Veness
9df25eda88 Add clangd files to .gitignore (#318) 2021-11-21 20:30:10 -05:00
Tyler Veness
5a13739818 Remove unused private variable (#317) 2021-11-21 20:30:03 -05:00
Tyler Veness
5ca39e7f84 Upgrade to Gradle 7.2 and WPILib 2022 (#316) 2021-11-21 20:22:56 -05:00
Matt
ffe34f00fe Ensure skew is always in [-90, 90] (#319) 2021-11-21 18:50:15 -06:00
Matt
a5cc0808c4 Make photon targeting respect snapshot repo (#309) 2021-11-06 20:12:50 -04:00
Banks T
08fafe2607 Fix file delete, possible null on perms set (#303) 2021-11-06 19:58:07 -04:00
Matt
a2af7d9273 Add red warning text with server mode, team number not set (#308) 2021-11-03 09:12:29 -04:00
Matt
a731c7a8db Revert part of #288 (#306)
Fixes picam streaming/hangs
2021-10-30 12:27:41 -04:00
Matt
7e74da5cff Make photonlib complain if versions don't match (#302)
Adds messages if Photon isn't detected or major versions don't match
2021-10-18 22:31:18 -04:00
Matt
0977fd0dff Update PacketTest.java (#301)
Adds unit test to make sure the packet structure doesn't change
2021-10-16 09:42:21 -04:00
Matt
3241ef7b1b Update dev tag matcher (#300) 2021-10-07 11:13:11 -04:00
Matt
f922466d41 Fix contour grouping (#298)
Fixes bug where n+1 contours were needed to match a target of size n
2021-10-05 12:16:50 -04:00
Matt
243f06da2d Fix vision module stream index logic (#295)
Previous logic could cause stream index with multiple cameras to run away
2021-10-03 22:43:39 -04:00
Matt
44e91a184d Publish photon-targeting (#292)
Pushes photon-targeting to Maven
2021-10-03 10:58:35 -04:00
Matt
e6b0f398b6 Fix versioning picked up in CI (#291)
Fixes ignore blob in versioningHelper
2021-09-24 18:51:40 -04:00
Chris Gerth
5a475e1071 Fix crash in logging when log files are not writable (#286)
* Addresses null pointer crash in logging when log files are not writable

* One of these days, I'll be able to type code without spotless complaining.

But today is not that day.
2021-09-23 22:38:50 -04:00
Tyler Veness
f8def88e4d Rename tests to appease wpiformat (#290) 2021-09-23 21:13:04 -04:00
Chris Gerth
db09e5209f Colored shape fix (#288)
* Move test images out of resources folder

* Limit workers in CI

* Fix image area filtering bug in colored shape

* Add missing picam settings

* Swap to make blank/empty Mat when a picam doesn't supply a color image.

Co-authored-by: Matt <matthew.morley.ca@gmail.com>
2021-09-23 18:48:18 -04:00
Matt
9fdd945a52 Fix compilation with gradlew build (#284)
* Fix everything but test mode

* Update TestUtils.java

* Jank testutils fix

* Limit workers in CI
2021-09-07 06:49:07 -07:00
Matt
00b8e7d1c5 Add colored shape to the UI (#258)
* Fixup colored shape backend code

* More colored shape stuff

* Start adding shape change to drawing

* Mostly works!

* Add powercell image for shape test mode

* Make super-duper-sure to release stuff

Fixes colored shape leak

* Move approx poly dp into Contour

* Adjust epsilon threshold

* Add dialog to change pipeline type

* Run spotless

* Make yes red :>

* Move "no" button

* Fix duplication/deletion name logic and switching

* Fix compilation errors from rebase

* Update VisionSourceManager.java

* Update type dialog, remove duplicate popup

The dropdown still switches even if the user says "no" tho

Co-authored-by: Banks Troutman <btrout.dhrs@gmail.com>
2021-09-03 22:20:55 -04:00
Tyler Veness
798b8e398a Remove hasTargets member variable and fix docs warnings (#283)
hasTargets is redundant because the same information can be obtained by
checking the size of the targets array.
2021-09-03 22:19:38 -04:00
Tyler Veness
affb27038b Fix uninitialized variables in PhotonPipelineResult (#282)
If the default constructor is used, some member variables weren't properly initialized.
2021-09-02 20:48:05 -07:00
Tyler Veness
6767781a41 Update photonlib vendordep JSON URL (#281) 2021-09-01 05:11:03 -07:00
Matt
6007cc752d Add libpicam with gain slider bugfix (#278)
* Add libpicam with gain slider bugfix

* Patches to get zero-copy working with Pi3.

-- Success/Failure mistmatch assumptions - lots of functions in the JNI return true on failure (not true on success)
-- isVSCMSupported() is currently implemented to be "isVSCMNotSupported()"

Likely, we'll want at least some .so changes

Co-authored-by: Chris Gerth <chrisgerth010592@gmail.com>
2021-08-31 23:48:11 -04:00
Banks T
9cf5c77d69 CI test fix (#280)
Addresses OOM in CI
2021-08-31 20:27:51 -07:00
Matt
9dc5481d1c Push dev tag last 2021-04-02 17:04:22 -04:00
Matt
3948650e6c [photonlib] Fix C++ compilation errors (#266)
Properly builds linux athena artifacts and fixes vendor JSON bug
2021-03-28 14:36:03 -07:00
Chris Gerth
49fcdb64ed Update Sim Example Unit Conversions (#265)
Updated Java examples to fix radians/degrees mismatch.

Inspected c++ examples - they create a units::degree_t from the NT value, which should be handled properly inside the PhotonUtils methods.
2021-03-23 10:57:32 -07:00
Banks T
1e715ce4bb Sim target sorting and "easy" NT entries (#262)
* Typo fixes, add target sorting

* Add easy NT entries

* spotttttttttttttttttlessssssssssssss

* Run wpiformat

* Fix return on no targets

* formatting hell 2 electric boogaloo

Co-authored-by: Matt <matthew.morley.ca@gmail.com>
2021-03-23 12:47:26 -04:00
Vasista Vovveti
f9fd7a0b45 Handle GetLatestResult segfault (#259)
* Handle GetLatestResult segfault

* Update PhotonCamera.cpp

* Update PhotonCamera.cpp
2021-03-21 16:29:29 -04:00
Matt
e9a3c2d1b8 Pull tags in photon CI (#263) 2021-03-15 16:13:45 -07:00
Banks T
129575dd23 Fix Test Mode (#246)
* Fix test mode path and args

* spoooootless

* Fix unit test resources
2021-03-07 21:39:02 -05:00
Declan Freeman-Gleason
ba8d2691fc Add more GPU accel support detection and more libpicam error checks (#257) 2021-03-02 17:49:29 -05:00
Matt
f3d3a59ca0 Use snapshot repo for dev releases (#241) 2021-02-23 15:07:43 -05:00
Matt
b653fc7db1 deprecate hasTargets (#252)
hasTargets has the potential to allow users to inadvertantly create NPEs.
2021-02-23 15:07:31 -05:00
MarkGhebrial
71ee03a531 Draw 2D contours on top (#254)
Closes #102
2021-02-23 15:07:03 -05:00
Banks T
4a2493ff2e Remove spamy CSCore prints in unit tests (#255) 2021-02-23 11:37:01 -08:00
Banks T
0b20111824 Add update script (#248) 2021-02-01 11:00:29 -08:00
Banks T
449977e63b Update NetworkTablesManager.java (#250)
Allows NT connection on Romi
2021-01-29 12:20:16 -08:00
Banks T
b0504cbef5 Add VisionModule order determinism (#245)
This makes sure that VisionModules always appear in the same order.
2021-01-29 10:57:18 -08:00
Matt
fccb395564 Fix CVMat weirdness in calibration unit test (#240)
* Remove failing assertion

Cannot reproduce locally.

* comment out other test

* Update Calibrate3dPipeTest.java
2021-01-29 07:08:09 -05:00
Chris Gerth
b510417541 Fixup support for log messages in the client web interface (#247) 2021-01-26 23:48:14 -05:00
Chris Gerth
0330467874 Add Basic Sim Example (#237)
* WIP adding sim pose example

* WIP making examples buildable like WPI. Not quite there yet....

* Make examples runnable

* remove lock

* add lock

* WIP Adding a simpler example for simulation

* Spotless Apply

* Added simulation-supporting aim and range example

* Spotless, revised hand usage to be consistent across examples, and propagated required -1.0's to non-sim examples.

Co-authored-by: Prateek Machiraju <prateek.machiraju@gmail.com>
2021-01-26 23:26:15 -05:00
Banks T
4c15a46cda Move CameraConfiguration to VisionSource (#244) 2021-01-22 22:10:20 -08:00
Matt
d59f8d1227 Cleanup photonlib deps (#243)
Removes unnecessary photonlib dependencies and removes commons-math.
2021-01-22 22:05:39 -08:00
Matt
bbc8a3137b Fix photon-lib spelling in CI (#239) 2021-01-19 05:48:15 -08:00
Prateek Machiraju
951c038f19 Clean up buildscripts (#238)
Cleans up gradle buildscripts.
2021-01-18 19:12:57 -08:00
Prateek Machiraju
1b0c5af86e Make examples runnable, start work on examples (#235)
Co-authored-by: Chris <chrisgerth010592@gmail.com>
Co-authored-by: Matt <matthew.morley.ca@gmail.com>
2021-01-18 10:56:04 -05:00
Banks T
a113bd4192 Fix processing timings in CVPipelineResult (#236) 2021-01-17 23:28:00 -05:00
Matt
d3c23345da Use wpi::Now for image capture timestamp (#232)
This uses the same time source as CSCore does for image captures, and will make latency measurements more accurate.

Co-authored-by: Banks T <btrout.dhrs@gmail.com>
2021-01-17 14:57:21 -08:00
Matt
2e1b3d0f83 Add Photonlib (#231)
Merges Photonlib into Photonvision, along with the Photonlib code examples. Also creates a new PhotonTargeting library teams can depend on.
2021-01-16 20:41:47 -08:00
Banks T
58b39f47aa Split photon-server and photon-core (#211)
Uses multiple Gradle projects to support the split.
2021-01-14 18:45:26 -08:00
Declan Freeman-Gleason
dc0f8cf296 Fetch tags before building server so that the version string is correct (#230) 2021-01-12 12:37:41 -08:00
Declan Freeman-Gleason
3d34d1ca40 Remove 1280x720 on the Pi Camera v1 for now (#228) 2021-01-11 00:35:04 -05:00
718 changed files with 12076 additions and 4108 deletions

167
.clang-format Normal file
View File

@@ -0,0 +1,167 @@
---
Language: Cpp
BasedOnStyle: Google
AccessModifierOffset: -1
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: false
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: false
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
SortPriority: 0
- Regex: '^<.*\.h>'
Priority: 1
SortPriority: 0
- Regex: '^<.*'
Priority: 2
SortPriority: 0
- Regex: '.*'
Priority: 3
SortPriority: 0
IncludeIsMainRegex: '([-_](test|unittest))?$'
IncludeIsMainSourceRegex: ''
IndentCaseLabels: true
IndentGotoLabels: true
IndentPPDirectives: None
IndentWidth: 2
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
RawStringFormats:
- Language: Cpp
Delimiters:
- cc
- CC
- cpp
- Cpp
- CPP
- 'c++'
- 'C++'
CanonicalDelimiter: ''
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
CanonicalDelimiter: ''
BasedOnStyle: google
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
Standard: Auto
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseCRLF: false
UseTab: Never
...

1
.github/CODEOWNERS vendored
View File

@@ -1,3 +1,2 @@
# These owners will be the default owners for everything in the repo. # These owners will be the default owners for everything in the repo.
* @PhotonVision/program-devs * @PhotonVision/program-devs

View File

@@ -14,7 +14,7 @@ on:
jobs: jobs:
# This job builds the client (web view). # This job builds the client (web view).
build-client: photonclient-build:
# Let all steps run within the photon-client dir. # Let all steps run within the photon-client dir.
defaults: defaults:
@@ -31,7 +31,7 @@ jobs:
steps: steps:
# Checkout code. # Checkout code.
- uses: actions/checkout@v1 - uses: actions/checkout@v1
# Setup Node.js # Setup Node.js
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v1 uses: actions/setup-node@v1
@@ -49,12 +49,7 @@ jobs:
name: built-client name: built-client
path: photon-client/dist/ path: photon-client/dist/
build-server: photon-build-all:
# Let all steps run within the photon-server dir.
defaults:
run:
working-directory: photon-server
# The type of runner that the job will run on. # The type of runner that the job will run on.
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -62,7 +57,11 @@ jobs:
# Checkout code. # Checkout code.
- name: Checkout code - name: Checkout code
uses: actions/checkout@v1 uses: actions/checkout@v1
# Fetch tags.
- name: Fetch tags
run: git fetch --tags --force
# Install Java 11. # Install Java 11.
- name: Install Java 11 - name: Install Java 11
uses: actions/setup-java@v1 uses: actions/setup-java@v1
@@ -73,19 +72,28 @@ jobs:
- name: Gradle Build - name: Gradle Build
run: | run: |
chmod +x gradlew chmod +x gradlew
./gradlew build -x check ./gradlew build -x check --max-workers 1
# Run Gradle Tests.
- name: Gradle Tests
run: ./gradlew testHeadless -i --max-workers 1
# Generate Coverage Report.
- name: Gradle Coverage
run: ./gradlew jacocoTestReport --max-workers 1
# Run Tests Generate Coverage Report.
- name: Gradle Test and Coverage
run: ./gradlew jacocoTestReport
# Publish Coverage Report. # Publish Coverage Report.
- name: Publish Coverage Report - name: Publish Server Coverage Report
uses: codecov/codecov-action@v1 uses: codecov/codecov-action@v1
with: with:
file: ./photon-server/build/reports/jacoco/test/jacocoTestReport.xml file: ./photon-server/build/reports/jacoco/test/jacocoTestReport.xml
build-offline-docs: - name: Publish Core Coverage Report
uses: codecov/codecov-action@v1
with:
file: ./photon-core/build/reports/jacoco/test/jacocoTestReport.xml
photonserver-build-offline-docs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -99,18 +107,18 @@ jobs:
- uses: actions/setup-python@v2 - uses: actions/setup-python@v2
with: with:
python-version: '3.6' python-version: '3.6'
- name: Install dependencies - name: Install dependencies
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
pip install sphinx sphinx_rtd_theme sphinx-tabs sphinxext-opengraph doc8 pip install sphinx sphinx_rtd_theme sphinx-tabs sphinxext-opengraph doc8
pip install -r requirements.txt pip install -r requirements.txt
- name: Check the docs - name: Check the docs
run: | run: |
make linkcheck make linkcheck
make lint make lint
- name: Build the docs - name: Build the docs
run: | run: |
make html make html
@@ -121,13 +129,126 @@ jobs:
name: built-docs name: built-docs
path: build/html path: build/html
build-package: photonserver-check-lint:
needs: [build-client, build-server, build-offline-docs] # The type of runner that the job will run on.
runs-on: ubuntu-latest
# Let all steps run within the photon-server dir. steps:
defaults: # Checkout code.
run: - uses: actions/checkout@v1
working-directory: photon-server
# Install Java 11.
- uses: actions/setup-java@v1
with:
java-version: 11
# Check server code with Spotless.
- run: |
chmod +x gradlew
./gradlew spotlessCheck
# Building photonlib
photonlib-build-host:
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
artifact-name: Win64
- os: macos-latest
artifact-name: macOS
- os: ubuntu-latest
artifact-name: Linux
runs-on: ${{ matrix.os }}
name: "Photonlib - Build - ${{ matrix.artifact-name }}"
steps:
- uses: actions/checkout@v2.3.4
with:
fetch-depth: 0
- uses: actions/setup-java@v1
with:
java-version: 11
- run: git fetch --tags --force
- run: |
chmod +x gradlew
./gradlew photon-lib:build --max-workers 1
- run: ./gradlew photon-lib:publish photon-targeting:publish
name: Publish
env:
ARTIFACTORY_API_KEY: ${{ secrets.ARTIFACTORY_API_KEY }}
if: github.event_name == 'push'
photonlib-build-docker:
strategy:
fail-fast: false
matrix:
include:
- container: wpilib/roborio-cross-ubuntu:2022-18.04
artifact-name: Athena
- container: wpilib/raspbian-cross-ubuntu:10-18.04
artifact-name: Raspbian
- container: wpilib/aarch64-cross-ubuntu:bionic-18.04
artifact-name: Aarch64
runs-on: ubuntu-latest
container: ${{ matrix.container }}
name: "Photonlib - Build - ${{ matrix.artifact-name }}"
steps:
- uses: actions/checkout@v2.3.4
with:
fetch-depth: 0
- uses: actions/setup-java@v1
with:
java-version: 11
- run: |
chmod +x gradlew
./gradlew photon-lib:build --max-workers 1
- run: |
chmod +x gradlew
./gradlew photon-lib:publish
env:
ARTIFACTORY_API_KEY: ${{ secrets.ARTIFACTORY_API_KEY }}
if: github.event_name == 'push'
photonlib-wpiformat:
name: "wpiformat"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Fetch all history and metadata
run: |
git fetch --prune --unshallow
git checkout -b pr
git branch -f master origin/master
- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install clang-format
run: |
sudo sh -c "echo 'deb http://archive.ubuntu.com/ubuntu/ $(lsb_release -cs)-proposed restricted main multiverse universe' >> /etc/apt/sources.list.d/proposed-repositories.list"
sudo apt-get update -q
sudo apt-get install -y clang-format-12
- name: Install wpiformat
run: pip3 install wpiformat
- name: Run
run: wpiformat -clang 12
- name: Check Output
run: git --no-pager diff --exit-code HEAD
- name: Generate diff
run: git diff HEAD > wpiformat-fixes.patch
if: ${{ failure() }}
- uses: actions/upload-artifact@v2
with:
name: wpiformat fixes
path: wpiformat-fixes.patch
if: ${{ failure() }}
photon-build-package:
needs: [photonclient-build, photon-build-all, photonserver-build-offline-docs, photonlib-build-host, photonlib-build-docker]
# The type of runner that the job will run on. # The type of runner that the job will run on.
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -135,7 +256,7 @@ jobs:
steps: steps:
# Checkout code. # Checkout code.
- uses: actions/checkout@v1 - uses: actions/checkout@v1
# Install Java 11. # Install Java 11.
- uses: actions/setup-java@v1 - uses: actions/setup-java@v1
with: with:
@@ -143,8 +264,8 @@ jobs:
# Clear any existing web resources. # Clear any existing web resources.
- run: | - run: |
rm -rf src/main/resources/web/* rm -rf photon-server/src/main/resources/web/*
mkdir -p src/main/resources/web/docs mkdir -p photon-server/src/main/resources/web/docs
# Download client artifact to resources folder. # Download client artifact to resources folder.
- uses: actions/download-artifact@v2 - uses: actions/download-artifact@v2
@@ -156,65 +277,57 @@ jobs:
- uses: actions/download-artifact@v2 - uses: actions/download-artifact@v2
with: with:
name: built-docs name: built-docs
path: photon-server/src/main/resources/web/docs path: photon-server/src/main/resources/web/docs
# Build fat jar for both pi and everything
# Print folder contents.
- run: ls
working-directory: photon-server/src/main/resources/web/
# Build fat jar.
- run: | - run: |
chmod +x gradlew chmod +x gradlew
./gradlew shadowJar ./gradlew photon-server:shadowJar --max-workers 1
working-directory: photon-server ./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: |
chmod +x scripts/generatePiImage.sh
./scripts/generatePiImage.sh
# Upload final fat jar as artifact. # Upload final fat jar as artifact.
- uses: actions/upload-artifact@master - uses: actions/upload-artifact@master
with: with:
name: jar name: jar
path: photon-server/build/libs path: photon-server/build/libs
- uses: actions/upload-artifact@master
- uses: eine/tip@master with:
name: image
path: photonvision*.zip
- uses: pyTooling/Actions/releaser@r0
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
tag: 'Dev' tag: 'Dev'
rm: true rm: true
files: | files: |
photon-server/build/libs/*.jar photon-server/build/libs/*.jar
photonvision*.zip
if: github.event_name == 'push' if: github.event_name == 'push'
check-lint: photon-release:
# Let all steps run within the photon-server dir.
defaults:
run:
working-directory: photon-server
# The type of runner that the job will run on.
runs-on: ubuntu-latest
steps:
# Checkout code.
- uses: actions/checkout@v1
# Install Java 11.
- uses: actions/setup-java@v1
with:
java-version: 11
# Check server code with Spotless.
- run: |
chmod +x gradlew
./gradlew spotlessCheck
release:
if: startsWith(github.ref, 'refs/tags/v') if: startsWith(github.ref, 'refs/tags/v')
needs: [build-package] needs: [photon-build-package]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
# This *should* pull in fat and pi-only jars
- uses: actions/download-artifact@v2 - uses: actions/download-artifact@v2
with: with:
name: jar 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 - uses: softprops/action-gh-release@v1
with: with:
files: '**/*' files: '**/*'

35
.gitignore vendored
View File

@@ -104,17 +104,25 @@ fabric.properties
# Android studio 3.1+ serialized cache file # Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser .idea/caches/build_file_checksums.ser
# Temporary build files
photon-server/.gradle **/.gradle
photon-server/target **/target
photon-server/src/main/java/META-INF **/src/main/java/META-INF
photon-server/.settings **/.settings
photon-server/.classpath **/.classpath
photon-server/.project **/.project
photon-server/settings **/settings
photon-server/dependency-reduced-pom.xml **/dependency-reduced-pom.xml
# photon-server/photon-vision.iml # photon-server/photon-vision.iml
# compile_commands
compile_commands.json
# clang configuration and clangd cache
.clang
.clangd/
.cache/
New client/photon-client/* New client/photon-client/*
*.prefs *.prefs
@@ -126,3 +134,12 @@ photon-server/photon-vision
photon-server/src/main/resources/web photon-server/src/main/resources/web
photon-server/src/main/java/org/photonvision/PhotonVersion.java photon-server/src/main/java/org/photonvision/PhotonVersion.java
photon-server/src/main/generated/native/include/org_photonvision_raspi_PicamJNI.h photon-server/src/main/generated/native/include/org_photonvision_raspi_PicamJNI.h
*.bin
.gradle
.gradle/*
photonvision_config
build/spotlessJava
build/*
build
photon-lib/src/main/java/org/photonvision/PhotonVersion.java
/photonlib-java-examples/bin/

31
.styleguide Normal file
View File

@@ -0,0 +1,31 @@
cppHeaderFileInclude {
\.h$
\.hpp$
\.inc$
\.inl$
}
cppSrcFileInclude {
\.cpp$
}
modifiableFileExclude {
\.jpg$
\.png$
\.so$
}
includeProject {
^photonLib/
}
includeOtherLibs {
^frc/
^networktables/
^units/
^wpi/
}
licenseUpdateExclude {
\.java$
}

16
.styleguide-license Normal file
View File

@@ -0,0 +1,16 @@
/*
* 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/>.
*/

View File

@@ -14,4 +14,3 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */

View File

@@ -10,14 +10,23 @@ If you are interested in contributing code or documentation to the project, plea
## Authors ## 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 ## Acknowledgments
PhotonVision was forked from [Chameleon Vision](https://github.com/Chameleon-Vision/chameleon-vision/). Thank you to everyone who worked on the original project. PhotonVision was forked from [Chameleon Vision](https://github.com/Chameleon-Vision/chameleon-vision/). Thank you to everyone who worked on the original project.
* [WPILib](https://github.com/wpilibsuite) - Specifically [cscore](https://github.com/wpilibsuite/allwpilib/tree/master/cscore), [CameraServer](https://github.com/wpilibsuite/allwpilib/tree/master/cameraserver), [NTCore](https://github.com/wpilibsuite/allwpilib/tree/master/ntcore), and [OpenCV](https://github.com/wpilibsuite/thirdparty-opencv). * [WPILib](https://github.com/wpilibsuite) - Specifically [cscore](https://github.com/wpilibsuite/allwpilib/tree/master/cscore), [CameraServer](https://github.com/wpilibsuite/allwpilib/tree/master/cameraserver), [NTCore](https://github.com/wpilibsuite/allwpilib/tree/master/ntcore), and [OpenCV](https://github.com/wpilibsuite/thirdparty-opencv).
* [Apache Commons](https://commons.apache.org/) - Specifically [Commons Math](https://commons.apache.org/proper/commons-math/), and [Commons Lang](https://commons.apache.org/proper/commons-lang/) * [Apache Commons](https://commons.apache.org/) - Specifically [Commons Math](https://commons.apache.org/proper/commons-math/), and [Commons Lang](https://commons.apache.org/proper/commons-lang/)
@@ -27,5 +36,11 @@ PhotonVision was forked from [Chameleon Vision](https://github.com/Chameleon-Vis
* [FasterXML](https://github.com/FasterXML) - Specifically [jackson](https://github.com/FasterXML/jackson) * [FasterXML](https://github.com/FasterXML) - Specifically [jackson](https://github.com/FasterXML/jackson)
## License ## License
PhotonVision is licensed under the [GNU General Public License](https://www.gnu.org/licenses/gpl-3.0.html) 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)

54
build.gradle Normal file
View File

@@ -0,0 +1,54 @@
plugins {
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.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"
}
allprojects {
repositories {
jcenter()
maven { url = "https://maven.photonvision.org/repository/internal/" }
}
wpilibRepositories.addAllReleaseRepositories(it)
wpilibRepositories.addAllDevelopmentRepositories(it)
}
// Configure the version number.
apply from: "versioningHelper.gradle"
ext {
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()
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")
}
}

View File

@@ -2,4 +2,4 @@ coverage:
# Turning off commit status to prevent failed checks if coverage decreases # Turning off commit status to prevent failed checks if coverage decreases
status: status:
project: no project: no
patch: no patch: no

8
gradle.properties Normal file
View 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

View File

@@ -1,6 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionSha256Sum=5a3578b9f0bb162f5e08cf119f447dfb8fa950cedebb4d2a977e912a11a74b91
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@@ -82,6 +82,7 @@ esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
@@ -129,6 +130,7 @@ fi
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"` APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"` JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath # We build the pattern for arguments to be converted via cygpath

View File

@@ -1,100 +1,89 @@
@rem @rem
@rem Copyright 2015 the original author or authors. @rem Copyright 2015 the original author or authors.
@rem @rem
@rem Licensed under the Apache License, Version 2.0 (the "License"); @rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License. @rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at @rem You may obtain a copy of the License at
@rem @rem
@rem https://www.apache.org/licenses/LICENSE-2.0 @rem https://www.apache.org/licenses/LICENSE-2.0
@rem @rem
@rem Unless required by applicable law or agreed to in writing, software @rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS, @rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@if "%DEBUG%" == "" @echo off @if "%DEBUG%" == "" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@rem Gradle startup script for Windows @rem Gradle startup script for Windows
@rem @rem
@rem ########################################################################## @rem ##########################################################################
@rem Set local scope for the variables with windows NT shell @rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=. if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. @rem Resolve any "." and ".." in APP_HOME to make it shorter.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Find java.exe @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
if defined JAVA_HOME goto findJavaFromJavaHome set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
set JAVA_EXE=java.exe @rem Find java.exe
%JAVA_EXE% -version >NUL 2>&1 if defined JAVA_HOME goto findJavaFromJavaHome
if "%ERRORLEVEL%" == "0" goto init
set JAVA_EXE=java.exe
echo. %JAVA_EXE% -version >NUL 2>&1
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. if "%ERRORLEVEL%" == "0" goto execute
echo.
echo Please set the JAVA_HOME variable in your environment to match the echo.
echo location of your Java installation. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
goto fail echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=% goto fail
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
:findJavaFromJavaHome
if exist "%JAVA_EXE%" goto init set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% if exist "%JAVA_EXE%" goto execute
echo.
echo Please set the JAVA_HOME variable in your environment to match the echo.
echo location of your Java installation. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
goto fail echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
:init
@rem Get command-line arguments, handling Windows variants goto fail
if not "%OS%" == "Windows_NT" goto win9xME_args :execute
@rem Setup the command line
:win9xME_args
@rem Slurp the command line arguments. set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
set CMD_LINE_ARGS=
set _SKIP=2
@rem Execute Gradle
:win9xME_args_slurp "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
if "x%~1" == "x" goto execute
:end
set CMD_LINE_ARGS=%* @rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:execute
@rem Setup the command line :fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
@rem Execute Gradle exit /b 1
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:mainEnd
:end if "%OS%"=="Windows_NT" endlocal
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd :omega
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -2118,16 +2118,6 @@
"integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==", "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==",
"dev": true "dev": true
}, },
"@types/mini-css-extract-plugin": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/@types/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.1.tgz",
"integrity": "sha512-+mN04Oszdz9tGjUP/c1ReVwJXxSniLd7lF++sv+8dkABxVNthg6uccei+4ssKxRHGoMmPxdn7uBdJWONSJGTGQ==",
"dev": true,
"optional": true,
"requires": {
"@types/webpack": "*"
}
},
"@types/minimatch": { "@types/minimatch": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
@@ -2164,6 +2154,12 @@
"integrity": "sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ==", "integrity": "sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ==",
"dev": true "dev": true
}, },
"@types/raf": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.0.tgz",
"integrity": "sha512-taW5/WYqo36N7V39oYyHP9Ipfd5pNFvGTIQsNGj86xV88YQ7GnI30/yMfKDF7Zgin0m3e+ikX88FvImnK4RjGw==",
"optional": true
},
"@types/range-parser": { "@types/range-parser": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
@@ -2679,17 +2675,6 @@
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"dev": true "dev": true
}, },
"ansi-styles": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
"dev": true,
"optional": true,
"requires": {
"@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
"browserslist": { "browserslist": {
"version": "4.14.0", "version": "4.14.0",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.0.tgz", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.0.tgz",
@@ -2734,23 +2719,6 @@
"integrity": "sha512-Hpwa4obv7EGP+TjkCh/wVvbtNJewxmtg4yVJBLFnxo35vbPapBr138bUWENkb5j5L9JZJ9RXLn4OrXRG/cecPQ==", "integrity": "sha512-Hpwa4obv7EGP+TjkCh/wVvbtNJewxmtg4yVJBLFnxo35vbPapBr138bUWENkb5j5L9JZJ9RXLn4OrXRG/cecPQ==",
"dev": true "dev": true
}, },
"color-convert": {
"version": "2.0.1",
"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"
}
},
"color-name": {
"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
},
"electron-to-chromium": { "electron-to-chromium": {
"version": "1.3.554", "version": "1.3.554",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.554.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.554.tgz",
@@ -2784,13 +2752,6 @@
"path-exists": "^4.0.0" "path-exists": "^4.0.0"
} }
}, },
"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
},
"locate-path": { "locate-path": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@@ -2924,16 +2885,6 @@
"ansi-regex": "^5.0.0" "ansi-regex": "^5.0.0"
} }
}, },
"supports-color": {
"version": "7.2.0",
"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"
}
},
"terser-webpack-plugin": { "terser-webpack-plugin": {
"version": "2.3.8", "version": "2.3.8",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz",
@@ -2960,34 +2911,6 @@
"psl": "^1.1.28", "psl": "^1.1.28",
"punycode": "^2.1.1" "punycode": "^2.1.1"
} }
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.0.0-beta.5",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.0.0-beta.5.tgz",
"integrity": "sha512-ciWfzNefqWlmzKznCWY9hl+fPP4KlQ0A9MtHbJ/8DpyY+dAM8gDrjufIdxwTgC4szE4EZC3A6ip/BbrqM84GqA==",
"dev": true,
"optional": true,
"requires": {
"@types/mini-css-extract-plugin": "^0.9.1",
"chalk": "^3.0.0",
"hash-sum": "^2.0.0",
"loader-utils": "^1.2.3",
"merge-source-map": "^1.1.0",
"source-map": "^0.6.1"
},
"dependencies": {
"chalk": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
}
}
} }
} }
}, },
@@ -3559,8 +3482,7 @@
"atob": { "atob": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
"dev": true
}, },
"autoprefixer": { "autoprefixer": {
"version": "9.8.6", "version": "9.8.6",
@@ -3819,6 +3741,12 @@
} }
} }
}, },
"base64-arraybuffer": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz",
"integrity": "sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ==",
"optional": true
},
"base64-js": { "base64-js": {
"version": "1.3.1", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
@@ -4110,6 +4038,11 @@
"node-releases": "^1.1.44" "node-releases": "^1.1.44"
} }
}, },
"btoa": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz",
"integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g=="
},
"buffer": { "buffer": {
"version": "4.9.2", "version": "4.9.2",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
@@ -4390,6 +4323,45 @@
"integrity": "sha512-6ljkLtF1KM5fQ+5ZN0wuyVvvebJxgJPTmScOMaFuQN2QuOzvRJnWSKfzQskQU5IOU4Gap3zasYPIinzwUjoj/g==", "integrity": "sha512-6ljkLtF1KM5fQ+5ZN0wuyVvvebJxgJPTmScOMaFuQN2QuOzvRJnWSKfzQskQU5IOU4Gap3zasYPIinzwUjoj/g==",
"dev": true "dev": true
}, },
"canvg": {
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.9.tgz",
"integrity": "sha512-rDXcnRPuz4QHoCilMeoTxql+fvGqNAxp+qV/KHD8rOiJSAfVjFclbdUNHD2Uqfthr+VMg17bD2bVuk6F07oLGw==",
"optional": true,
"requires": {
"@babel/runtime": "^7.12.5",
"@types/raf": "^3.4.0",
"core-js": "^3.8.3",
"raf": "^3.4.1",
"regenerator-runtime": "^0.13.7",
"rgbcolor": "^1.0.1",
"stackblur-canvas": "^2.0.0",
"svg-pathdata": "^6.0.3"
},
"dependencies": {
"@babel/runtime": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.0.tgz",
"integrity": "sha512-Nht8L0O8YCktmsDV6FqFue7vQLRx3Hb0B37lS5y0jDRqRxlBG4wIJHnf9/bgSE2UyipKFA01YtS+npRdTWBUyw==",
"optional": true,
"requires": {
"regenerator-runtime": "^0.13.4"
}
},
"core-js": {
"version": "3.19.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.19.0.tgz",
"integrity": "sha512-L1TpFRWXZ76vH1yLM+z6KssLZrP8Z6GxxW4auoCj+XiViOzNPJCAuTIkn03BGdFe6Z5clX5t64wRIRypsZQrUg==",
"optional": true
},
"regenerator-runtime": {
"version": "0.13.9",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==",
"optional": true
}
}
},
"case-sensitive-paths-webpack-plugin": { "case-sensitive-paths-webpack-plugin": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.3.0.tgz", "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.3.0.tgz",
@@ -5172,6 +5144,15 @@
"timsort": "^0.3.0" "timsort": "^0.3.0"
} }
}, },
"css-line-break": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.0.1.tgz",
"integrity": "sha512-gwKYIMUn7xodIcb346wgUhE2Dt5O1Kmrc16PWi8sL4FTfyDj8P5095rzH7+O8CTZudJr+uw2GCI/hwEkDJFI2w==",
"optional": true,
"requires": {
"base64-arraybuffer": "^0.2.0"
}
},
"css-loader": { "css-loader": {
"version": "3.6.0", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz",
@@ -5809,6 +5790,12 @@
"domelementtype": "1" "domelementtype": "1"
} }
}, },
"dompurify": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.3.tgz",
"integrity": "sha512-dqnqRkPMAjOZE0FogZ+ceJNM2dZ3V/yNOuFB7+39qpO93hHhfRpHw3heYQC7DPK9FqbQTfBKUJhiSfz4MvXYwg==",
"optional": true
},
"domutils": { "domutils": {
"version": "1.7.0", "version": "1.7.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
@@ -6743,6 +6730,11 @@
"websocket-driver": ">=0.5.1" "websocket-driver": ">=0.5.1"
} }
}, },
"fflate": {
"version": "0.4.8",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz",
"integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA=="
},
"figgy-pudding": { "figgy-pudding": {
"version": "3.5.1", "version": "3.5.1",
"resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz",
@@ -7440,6 +7432,16 @@
} }
} }
}, },
"html2canvas": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.3.2.tgz",
"integrity": "sha512-4+zqv87/a1LsaCrINV69wVLGG8GBZcYBboz1JPWEgiXcWoD9kroLzccsBRU/L9UlfV2MAZ+3J92U9IQPVMDeSQ==",
"optional": true,
"requires": {
"css-line-break": "2.0.1",
"text-segmentation": "^1.0.2"
}
},
"htmlparser2": { "htmlparser2": {
"version": "3.10.1", "version": "3.10.1",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
@@ -8369,6 +8371,42 @@
"graceful-fs": "^4.1.6" "graceful-fs": "^4.1.6"
} }
}, },
"jspdf": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.4.0.tgz",
"integrity": "sha512-nsZ92YfbNG/EimR1yqmOkxf2D4iJRypBsw7pvP1aPiIEnoGITaLl6XDR/GYA36/R29vMZSBedpEhBCzutSGytA==",
"requires": {
"@babel/runtime": "^7.14.0",
"atob": "^2.1.2",
"btoa": "^1.2.1",
"canvg": "^3.0.6",
"core-js": "^3.6.0",
"dompurify": "^2.2.0",
"fflate": "^0.4.8",
"html2canvas": "^1.0.0-rc.5"
},
"dependencies": {
"@babel/runtime": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.0.tgz",
"integrity": "sha512-Nht8L0O8YCktmsDV6FqFue7vQLRx3Hb0B37lS5y0jDRqRxlBG4wIJHnf9/bgSE2UyipKFA01YtS+npRdTWBUyw==",
"requires": {
"regenerator-runtime": "^0.13.4"
}
},
"core-js": {
"version": "3.19.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.19.0.tgz",
"integrity": "sha512-L1TpFRWXZ76vH1yLM+z6KssLZrP8Z6GxxW4auoCj+XiViOzNPJCAuTIkn03BGdFe6Z5clX5t64wRIRypsZQrUg==",
"optional": true
},
"regenerator-runtime": {
"version": "0.13.9",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
}
}
},
"jsprim": { "jsprim": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
@@ -9658,8 +9696,7 @@
"performance-now": { "performance-now": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
"dev": true
}, },
"picomatch": { "picomatch": {
"version": "2.2.2", "version": "2.2.2",
@@ -10546,6 +10583,15 @@
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
"dev": true "dev": true
}, },
"raf": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
"optional": true,
"requires": {
"performance-now": "^2.1.0"
}
},
"randombytes": { "randombytes": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -10923,6 +10969,12 @@
"integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=",
"dev": true "dev": true
}, },
"rgbcolor": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz",
"integrity": "sha1-1lBezbMEplldom+ktDMHMGd1lF0=",
"optional": true
},
"rimraf": { "rimraf": {
"version": "2.7.1", "version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@@ -11648,6 +11700,12 @@
"integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
"dev": true "dev": true
}, },
"stackblur-canvas": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.5.0.tgz",
"integrity": "sha512-EeNzTVfj+1In7aSLPKDD03F/ly4RxEuF/EX0YcOG0cKoPXs+SLZxDawQbexQDBzwROs4VKLWTOaZQlZkGBFEIQ==",
"optional": true
},
"stackframe": { "stackframe": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz",
@@ -11955,6 +12013,12 @@
"has-flag": "^3.0.0" "has-flag": "^3.0.0"
} }
}, },
"svg-pathdata": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz",
"integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==",
"optional": true
},
"svg-tags": { "svg-tags": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz",
@@ -12041,6 +12105,15 @@
} }
} }
}, },
"text-segmentation": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.2.tgz",
"integrity": "sha512-uTqvLxdBrVnx/CFQOtnf8tfzSXFm+1Qxau7Xi54j4OPTZokuDOX8qncQzrg2G8ZicAMOM8TgzFAYTb+AqNO4Cw==",
"optional": true,
"requires": {
"utrie": "^1.0.1"
}
},
"text-table": { "text-table": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@@ -12613,6 +12686,23 @@
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
"dev": true "dev": true
}, },
"utrie": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.1.tgz",
"integrity": "sha512-JPaDXF3vzgZxfeEwutdGzlrNoVFL5UvZcbO6Qo9D4GoahrieUPoMU8GCpVpR7MQqcKhmShIh8VlbEN3PLM3EBg==",
"optional": true,
"requires": {
"base64-arraybuffer": "^1.0.1"
},
"dependencies": {
"base64-arraybuffer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.1.tgz",
"integrity": "sha512-vFIUq7FdLtjZMhATwDul5RZWv2jpXQ09Pd6jcVEOvIsqCWTRFD/ONHNfyOS8dA/Ippi5dsIgpyKWKZaAKZltbA==",
"optional": true
}
}
},
"uuid": { "uuid": {
"version": "3.3.3", "version": "3.3.3",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
@@ -12701,6 +12791,94 @@
} }
} }
}, },
"vue-loader-v16": {
"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,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"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
},
"emojis-list": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
"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
},
"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,
"requires": {
"minimist": "^1.2.5"
}
},
"loader-utils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
"dev": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"vue-native-websocket": { "vue-native-websocket": {
"version": "git+https://github.com/PhotonVision/vue-native-websocket.git#7a327918e03b215b6899b0d648c5130ece1fa912", "version": "git+https://github.com/PhotonVision/vue-native-websocket.git#7a327918e03b215b6899b0d648c5130ece1fa912",
"from": "git+https://github.com/PhotonVision/vue-native-websocket.git#7a32791", "from": "git+https://github.com/PhotonVision/vue-native-websocket.git#7a32791",
@@ -12749,7 +12927,7 @@
"dependencies": { "dependencies": {
"acorn": { "acorn": {
"version": "3.3.0", "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=" "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo="
} }
} }
@@ -13653,7 +13831,7 @@
}, },
"browserify-aes": { "browserify-aes": {
"version": "1.2.0", "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==", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
"requires": { "requires": {
"buffer-xor": "^1.0.3", "buffer-xor": "^1.0.3",
@@ -13687,7 +13865,7 @@
}, },
"browserify-rsa": { "browserify-rsa": {
"version": "4.0.1", "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=", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
"requires": { "requires": {
"bn.js": "^4.1.0", "bn.js": "^4.1.0",
@@ -13718,7 +13896,7 @@
}, },
"buffer": { "buffer": {
"version": "4.9.1", "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=", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
"requires": { "requires": {
"base64-js": "^1.0.2", "base64-js": "^1.0.2",
@@ -13854,7 +14032,7 @@
}, },
"chalk": { "chalk": {
"version": "1.1.3", "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=", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"requires": { "requires": {
"ansi-styles": "^2.2.1", "ansi-styles": "^2.2.1",
@@ -14101,7 +14279,7 @@
}, },
"create-hash": { "create-hash": {
"version": "1.2.0", "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==", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
"requires": { "requires": {
"cipher-base": "^1.0.1", "cipher-base": "^1.0.1",
@@ -14113,7 +14291,7 @@
}, },
"create-hmac": { "create-hmac": {
"version": "1.1.7", "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==", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
"requires": { "requires": {
"cipher-base": "^1.0.3", "cipher-base": "^1.0.3",
@@ -14320,7 +14498,7 @@
}, },
"diffie-hellman": { "diffie-hellman": {
"version": "5.0.3", "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==", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
"requires": { "requires": {
"bn.js": "^4.1.0", "bn.js": "^4.1.0",
@@ -14396,7 +14574,7 @@
"dependencies": { "dependencies": {
"debug": { "debug": {
"version": "2.3.3", "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=", "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=",
"requires": { "requires": {
"ms": "0.7.2" "ms": "0.7.2"
@@ -14435,7 +14613,7 @@
}, },
"debug": { "debug": {
"version": "2.3.3", "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=", "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=",
"requires": { "requires": {
"ms": "0.7.2" "ms": "0.7.2"
@@ -15886,7 +16064,7 @@
}, },
"http-errors": { "http-errors": {
"version": "1.6.3", "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=", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
"requires": { "requires": {
"depd": "~1.1.2", "depd": "~1.1.2",
@@ -16372,7 +16550,7 @@
"dependencies": { "dependencies": {
"lodash": { "lodash": {
"version": "3.10.1", "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=" "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y="
} }
} }
@@ -16408,7 +16586,7 @@
"dependencies": { "dependencies": {
"minimist": { "minimist": {
"version": "1.2.0", "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=" "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
} }
} }
@@ -16617,7 +16795,7 @@
}, },
"readable-stream": { "readable-stream": {
"version": "1.0.34", "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=", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
"requires": { "requires": {
"core-util-is": "~1.0.0", "core-util-is": "~1.0.0",
@@ -16776,7 +16954,7 @@
}, },
"minimist": { "minimist": {
"version": "1.2.0", "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=" "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
}, },
"path-exists": { "path-exists": {
@@ -16903,7 +17081,7 @@
}, },
"minimist": { "minimist": {
"version": "0.0.8", "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=" "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
}, },
"mixin-deep": { "mixin-deep": {
@@ -16927,7 +17105,7 @@
}, },
"mkdirp": { "mkdirp": {
"version": "0.5.1", "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=", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
@@ -16954,7 +17132,7 @@
"dependencies": { "dependencies": {
"commander": { "commander": {
"version": "2.9.0", "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=", "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=",
"requires": { "requires": {
"graceful-readlink": ">= 1.0.0" "graceful-readlink": ">= 1.0.0"
@@ -17241,7 +17419,7 @@
}, },
"onetime": { "onetime": {
"version": "1.1.0", "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=" "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k="
}, },
"optimist": { "optimist": {
@@ -17346,7 +17524,7 @@
}, },
"parse-asn1": { "parse-asn1": {
"version": "5.1.1", "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==", "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==",
"requires": { "requires": {
"asn1.js": "^4.0.0", "asn1.js": "^4.0.0",
@@ -17569,7 +17747,7 @@
}, },
"public-encrypt": { "public-encrypt": {
"version": "4.0.2", "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==", "integrity": "sha512-4kJ5Esocg8X3h8YgJsKAuoesBgB7mqH3eowiDzMUPKiRDDE7E/BqqZD1hnTByIaAFiwAw246YEltSq7tdrOH0Q==",
"requires": { "requires": {
"bn.js": "^4.1.0", "bn.js": "^4.1.0",
@@ -17685,7 +17863,7 @@
}, },
"readable-stream": { "readable-stream": {
"version": "2.3.6", "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==", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": { "requires": {
"core-util-is": "~1.0.0", "core-util-is": "~1.0.0",
@@ -17998,7 +18176,7 @@
}, },
"sha.js": { "sha.js": {
"version": "2.4.11", "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==", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
"requires": { "requires": {
"inherits": "^2.0.1", "inherits": "^2.0.1",
@@ -18176,7 +18354,7 @@
"dependencies": { "dependencies": {
"debug": { "debug": {
"version": "2.3.3", "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=", "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=",
"requires": { "requires": {
"ms": "0.7.2" "ms": "0.7.2"
@@ -18205,7 +18383,7 @@
"dependencies": { "dependencies": {
"debug": { "debug": {
"version": "2.3.3", "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=", "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=",
"requires": { "requires": {
"ms": "0.7.2" "ms": "0.7.2"
@@ -18243,7 +18421,7 @@
}, },
"debug": { "debug": {
"version": "2.3.3", "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=", "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=",
"requires": { "requires": {
"ms": "0.7.2" "ms": "0.7.2"
@@ -18269,7 +18447,7 @@
"dependencies": { "dependencies": {
"debug": { "debug": {
"version": "2.2.0", "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=", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
"requires": { "requires": {
"ms": "0.7.1" "ms": "0.7.1"
@@ -18464,7 +18642,7 @@
}, },
"table": { "table": {
"version": "3.8.3", "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=", "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=",
"requires": { "requires": {
"ajv": "^4.7.0", "ajv": "^4.7.0",
@@ -18521,7 +18699,7 @@
}, },
"through": { "through": {
"version": "2.3.8", "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=" "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
}, },
"time-stamp": { "time-stamp": {
@@ -18697,7 +18875,7 @@
}, },
"yargs": { "yargs": {
"version": "3.10.0", "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=", "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
"requires": { "requires": {
"camelcase": "^1.0.2", "camelcase": "^1.0.2",
@@ -19339,7 +19517,7 @@
}, },
"wrap-ansi": { "wrap-ansi": {
"version": "2.1.0", "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=", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
"requires": { "requires": {
"string-width": "^1.0.1", "string-width": "^1.0.1",
@@ -20250,9 +20428,9 @@
"dev": true "dev": true
}, },
"y18n": { "y18n": {
"version": "4.0.0", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
"dev": true "dev": true
}, },
"yallist": { "yallist": {

View File

@@ -13,6 +13,7 @@
"axios": "^0.19.2", "axios": "^0.19.2",
"core-js": "^2.6.11", "core-js": "^2.6.11",
"downloadjs": "^1.4.7", "downloadjs": "^1.4.7",
"jspdf": "^2.4.0",
"material-design-icons-iconfont": "^5.0.1", "material-design-icons-iconfont": "^5.0.1",
"msgpack5": "^4.2.1", "msgpack5": "^4.2.1",
"vue": "^2.6.12", "vue": "^2.6.12",

View File

@@ -17,4 +17,4 @@
<!-- built files will be auto injected --> <!-- built files will be auto injected -->
</body> </body>
</html> </html>

View File

@@ -91,25 +91,51 @@
</v-list-item-content> </v-list-item-content>
</v-list-item> </v-list-item>
<v-list-item style="position: absolute; bottom: 0; left: 0;"> <div style="position: absolute; bottom: 0; left: 0;">
<v-list-item-icon> <v-list-item>
<v-icon v-if="$store.state.backendConnected"> <v-list-item-icon>
mdi-wifi <v-icon v-if="$store.state.settings.networkSettings.runNTServer">mdi-server</v-icon>
</v-icon> <img v-else-if="$store.state.ntConnectionInfo.connected" src="@/assets/robot.svg" alt="">
<v-icon <img v-else class="pulse" style="border-radius: 100%" src="@/assets/robot-off.svg" alt="">
v-else </v-list-item-icon>
class="pulse" <v-list-item-content>
style="border-radius: 100%;" <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!
mdi-wifi-off </v-list-item-title>
</v-icon> <v-list-item-title class="text-wrap" v-else-if="$store.state.ntConnectionInfo.connected && $store.state.backendConnected">
</v-list-item-icon> Robot connected! {{$store.state.ntConnectionInfo.address}}
<v-list-item-content> </v-list-item-title>
<v-list-item-title> <v-list-item-title class="text-wrap" v-else>
{{ $store.state.backendConnected ? "Connected" : "Trying to connect..." }} Not connected to robot!
</v-list-item-title> </v-list-item-title>
</v-list-item-content> <a
</v-list-item> href="/#/settings"
style="color:#FFD843"
>{{"Team: " + $store.state.settings.networkSettings.teamNumber}}</a>
</v-list-item-content>
</v-list-item>
<v-list-item>
<v-list-item-icon>
<v-icon v-if="$store.state.backendConnected">
mdi-wifi
</v-icon>
<v-icon
v-else
class="pulse"
style="border-radius: 100%;"
>
mdi-wifi-off
</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title class="text-wrap">
{{ $store.state.backendConnected ? "Backend Connected" : "Trying to connect..." }}
</v-list-item-title>
</v-list-item-content>
</v-list-item>
</div>
</v-list> </v-list>
</v-navigation-drawer> </v-navigation-drawer>
<v-main> <v-main>
@@ -132,13 +158,35 @@
> >
<logs /> <logs />
</v-dialog> </v-dialog>
<v-dialog
v-model="needsTeamNumberSet"
width="500"
dark
persistent
>
<v-card
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.
</v-card-text>
</v-card>
</v-dialog>
</v-app> </v-app>
</template> </template>
<script> <script>
import Logs from "./views/LogsView" import Logs from "./views/LogsView"
// import {mapState} from "vuex";
export default { export default {
name: 'App', name: 'App',
components: { components: {
Logs Logs
@@ -147,8 +195,16 @@ import Logs from "./views/LogsView"
// Used so that we can switch back to the previously selected pipeline after camera calibration // Used so that we can switch back to the previously selected pipeline after camera calibration
previouslySelectedIndices: [], previouslySelectedIndices: [],
timer: undefined, timer: undefined,
teamNumberDialog: true
}), }),
computed: { computed: {
needsTeamNumberSet: {
get() {
return this.$store.state.settings.networkSettings.teamNumber < 1
&& this.teamNumberDialog && this.$store.state.backendConnected
&& !this.$route.name.toLowerCase().includes("settings");
}
},
compact: { compact: {
get() { get() {
if (this.$store.state.compactMode === undefined) { if (this.$store.state.compactMode === undefined) {
@@ -163,6 +219,12 @@ import Logs from "./views/LogsView"
localStorage.setItem("compactMode", value); 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() { created() {
document.addEventListener("keydown", e => { document.addEventListener("keydown", e => {
@@ -197,12 +259,12 @@ import Logs from "./views/LogsView"
} }
}; };
this.$options.sockets.onopen = () => { this.$options.sockets.onopen = () => {
this.$store.state.backendConnected = true; this.$store.commit("backendConnected", true)
this.$store.state.connectedCallbacks.forEach(it => it()) this.$store.state.connectedCallbacks.forEach(it => it())
}; };
let closed = () => { let closed = () => {
this.$store.state.backendConnected = false; this.$store.commit("backendConnected", false)
}; };
this.$options.sockets.onclose = closed; this.$options.sockets.onclose = closed;
this.$options.sockets.onerror = closed; this.$options.sockets.onerror = closed;
@@ -213,6 +275,8 @@ import Logs from "./views/LogsView"
handleMessage(key, value) { handleMessage(key, value) {
if (key === "logMessage") { if (key === "logMessage") {
this.logMessage(value["logMessage"], value["logLevel"]); this.logMessage(value["logMessage"], value["logLevel"]);
} else if(key === "log"){
this.logMessage(value["logMessage"]["logMessage"], value["logMessage"]["logLevel"]);
} else if (key === "updatePipelineResult") { } else if (key === "updatePipelineResult") {
this.$store.commit('mutatePipelineResults', value) this.$store.commit('mutatePipelineResults', value)
} else if (this.$store.state.hasOwnProperty(key)) { } else if (this.$store.state.hasOwnProperty(key)) {
@@ -341,4 +405,4 @@ import Logs from "./views/LogsView"
font-size: 14px !important; font-size: 14px !important;
} }
} }
</style> </style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path fill="white" d="M23 15V18C23 18.5 22.64 18.88 22.17 18.97L18.97 15.77C19 15.68 19 15.59 19 15.5C19 14.12 17.88 13 16.5 13C16.41 13 16.32 13 16.23 13.03L10.2 7H11V5.73C10.4 5.39 10 4.74 10 4C10 2.9 10.9 2 12 2S14 2.9 14 4C14 4.74 13.6 5.39 13 5.73V7H14C17.87 7 21 10.13 21 14H22C22.55 14 23 14.45 23 15M22.11 21.46L20.84 22.73L19.89 21.78C19.62 21.92 19.32 22 19 22H5C3.9 22 3 21.11 3 20V19H2C1.45 19 1 18.55 1 18V15C1 14.45 1.45 14 2 14H3C3 11.53 4.29 9.36 6.22 8.11L1.11 3L2.39 1.73L22.11 21.46M10 15.5C10 14.12 8.88 13 7.5 13S5 14.12 5 15.5 6.12 18 7.5 18 10 16.88 10 15.5M16.07 17.96L14.04 15.93C14.23 16.97 15.04 17.77 16.07 17.96Z" /></svg>

After

Width:  |  Height:  |  Size: 928 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path fill="white" d="M12,2C13.1,2 14,2.9 14,4C14,4.74 13.6,5.39 13,5.73V7H14C17.87,7 21,10.13 21,14H22C22.55,14 23,14.45 23,15V18C23,18.55 22.55,19 22,19H21V20C21,21.1 20.1,22 19,22H5C3.9,22 3,21.1 3,20V19H2C1.45,19 1,18.55 1,18V15C1,14.45 1.45,14 2,14H3C3,10.13 6.13,7 10,7H11V5.73C10.4,5.39 10,4.74 10,4C10,2.9 10.9,2 12,2M7.5,13C6.12,13 5,14.12 5,15.5C5,16.88 6.12,18 7.5,18C8.88,18 10,16.88 10,15.5C10,14.12 8.88,13 7.5,13M16.5,13C15.12,13 14,14.12 14,15.5C14,16.88 15.12,18 16.5,18C17.88,18 19,16.88 19,15.5C19,14.12 17.88,13 16.5,13Z" /></svg>

After

Width:  |  Height:  |  Size: 827 B

View File

@@ -51,4 +51,4 @@
.hover:hover { .hover:hover {
color: white !important; color: white !important;
} }
</style> </style>

View File

@@ -59,4 +59,4 @@
} }
}, },
} }
</script> </script>

View File

@@ -61,4 +61,4 @@ s
</script> </script>
<style lang="css" scoped> <style lang="css" scoped>
</style> </style>

View File

@@ -54,4 +54,4 @@
<style lang="" scoped> <style lang="" scoped>
</style> </style>

View File

@@ -41,4 +41,4 @@
<style lang="" scoped> <style lang="" scoped>
</style> </style>

View File

@@ -15,6 +15,7 @@
:value="localValue" :value="localValue"
:max="max" :max="max"
:min="min" :min="min"
:disabled="disabled"
hide-details hide-details
class="align-center" class="align-center"
dark dark
@@ -34,7 +35,7 @@
hide-details hide-details
single-line single-line
type="number" type="number"
style="width: 50px" style="width: 60px"
:step="step" :step="step"
@input="handleChange" @input="handleChange"
@focus="prependFocused = true" @focus="prependFocused = true"
@@ -53,7 +54,7 @@
hide-details hide-details
single-line single-line
type="number" type="number"
style="width: 50px" style="width: 60px"
:step="step" :step="step"
@input="handleChange" @input="handleChange"
@focus="appendFocused = true" @focus="appendFocused = true"
@@ -75,7 +76,7 @@ export default {
TooltippedLabel, TooltippedLabel,
}, },
// eslint-disable-next-line vue/require-prop-types // eslint-disable-next-line vue/require-prop-types
props: ["name", "min", "max", "value", "step", "tooltip"], props: ["name", "min", "max", "value", "step", "tooltip", "disabled"],
data() { data() {
return { return {
prependFocused: false, prependFocused: false,
@@ -128,4 +129,4 @@ export default {
</script> </script>
<style lang="" scoped> <style lang="" scoped>
</style> </style>

View File

@@ -62,4 +62,4 @@ import TooltippedLabel from "./cv-tooltipped-label";
</script> </script>
<style> <style>
</style> </style>

View File

@@ -105,4 +105,4 @@ export default {
</script> </script>
<style lang="" scoped> <style lang="" scoped>
</style> </style>

View File

@@ -48,4 +48,4 @@ export default {
<style lang="" scoped> <style lang="" scoped>
</style> </style>

View File

@@ -24,4 +24,4 @@
// eslint-disable-next-line vue/require-prop-types // eslint-disable-next-line vue/require-prop-types
props: ['text', 'tooltip'], props: ['text', 'tooltip'],
} }
</script> </script>

View File

@@ -151,4 +151,4 @@
width: 80px; width: 80px;
text-align: center; text-align: center;
} }
</style> </style>

View File

@@ -88,7 +88,11 @@
menu menu
</v-icon> </v-icon>
</template> </template>
<v-list dense> <v-list
dark
dense
color="primary"
>
<v-list-item @click="toPipelineNameChange"> <v-list-item @click="toPipelineNameChange">
<v-list-item-title> <v-list-item-title>
<CVicon <CVicon
@@ -119,7 +123,7 @@
/> />
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
<v-list-item @click="openDuplicateDialog"> <v-list-item @click="duplicatePipeline">
<v-list-item-title> <v-list-item-title>
<CVicon <CVicon
color="#c5c5c5" color="#c5c5c5"
@@ -132,67 +136,52 @@
</v-list> </v-list>
</v-menu> </v-menu>
</v-col> </v-col>
<v-col
v-if="currentPipelineType >= 0"
cols="10"
md="5"
lg="10"
class="pt-0 pb-0 pl-6"
>
<CVselect
v-model="currentPipelineType"
name="Type"
:list="['Reflective', 'Shape']"
@input="e => showTypeDialog(e)"
/>
</v-col>
</v-row> </v-row>
<!--pipeline duplicate dialog-->
<v-dialog
v-model="duplicateDialog"
dark
width="500"
height="357"
>
<v-card dark>
<v-card-title
class="headline"
primary-title
>
Duplicate Pipeline
</v-card-title>
<v-card-text>
<CVselect
v-model="pipeIndexToDuplicate"
name="Pipeline"
:list="$store.getters.pipelineList"
/>
</v-card-text>
<v-divider />
<v-card-actions>
<v-spacer />
<v-btn
color="#ffd843"
@click="duplicatePipeline"
>
Duplicate
</v-btn>
<v-btn
color="error"
@click="closeDuplicateDialog"
>
Cancel
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!--pipeline naming dialog--> <!--pipeline naming dialog-->
<v-dialog <v-dialog
v-model="namingDialog" v-model="namingDialog"
dark dark
persistent
width="500" width="500"
height="357" height="357"
> >
<v-card dark> <v-card
dark
color="primary"
>
<v-card-title <v-card-title
class="headline" class="headline"
primary-title primary-title
> >
Pipeline Name {{ isPipelineNameEdit ? "Edit Pipeline Name" : "Create Pipeline" }}
</v-card-title> </v-card-title>
<v-card-text> <v-card-text>
<CVinput <CVinput
v-model="newPipelineName" v-model="newPipelineName"
name="Pipeline" name="Name"
:error-message="checkPipelineName" :error-message="checkPipelineName"
@Enter="savePipelineNameChange"
/> />
<!-- <CVselect-->
<!-- v-model="currentPipelineType"-->
<!-- name="Pipeline Type"-->
<!-- :list="['Reflective', 'Shape']"-->
<!-- :disabled="true"-->
<!-- />-->
</v-card-text> </v-card-text>
<v-divider /> <v-divider />
<v-card-actions> <v-card-actions>
@@ -213,163 +202,206 @@
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</v-dialog> </v-dialog>
<v-dialog
v-model="showPipeTypeDialog"
width="600"
>
<v-card
color="primary"
dark
>
<v-card-title>Change Pipeline Type</v-card-title>
<v-card-text>
Changing the type of this pipeline will erase the current pipeline's settings and replace it with a new {{ ['Reflective', 'Shape'][proposedPipelineType] }} pipeline. <b class="red--text format_bold">You will lose all settings for the pipeline
"{{ ($store.getters.isDriverMode ? ['Driver Mode'] : []).concat($store.getters.pipelineList)[currentPipelineIndex] }}."</b> Are you sure you want to do this?
<v-row
class="mt-6"
style="display: flex; align-items: center; justify-content: center"
align="center"
>
<v-btn
class="mr-3"
color="red"
width="250"
@click="e => changePipeType(true)"
>
Yes, replace this pipeline
</v-btn>
<v-btn
class="ml-10"
color="secondary"
width="250"
@click="e => changePipeType(false)"
>
No, take me back
</v-btn>
</v-row>
</v-card-text>
</v-card>
</v-dialog>
</div> </div>
</template> </template>
<script> <script>
import CVicon from '../common/cv-icon' import CVicon from '../common/cv-icon'
import CVselect from '../common/cv-select' import CVselect from '../common/cv-select'
import CVinput from '../common/cv-input' import CVinput from '../common/cv-input'
export default {
name: "CameraAndPipelineSelect",
components: {
CVicon,
CVselect,
CVinput
},
data: () => {
return {
re: RegExp("^[A-Za-z0-9 \\-)(]*[A-Za-z0-9][A-Za-z0-9 \\-)(.]*$"),
isCameraNameEdit: false,
newCameraName: "",
cameraNameError: "",
isPipelineNameEdit: false,
namingDialog: false,
newPipelineName: "",
duplicateDialog: false,
pipeIndexToDuplicate: undefined
}
},
computed: {
checkCameraName() {
if (this.newCameraName !== this.$store.getters.cameraList[this.currentCameraIndex]) {
if (this.re.test(this.newCameraName)) {
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"
}
}
}
} else {
return "A camera name can only contain letters, numbers and spaces"
}
}
return "";
},
checkPipelineName() {
if (this.newPipelineName !== this.$store.getters.pipelineList[this.currentPipelineIndex - 1] || this.isPipelineNameEdit === false) {
if (this.re.test(this.newPipelineName)) {
for (let pipe in this.$store.getters.pipelineList) {
if (this.$store.getters.pipelineList.hasOwnProperty(pipe)) {
if (this.newPipelineName === this.$store.getters.pipelineList[pipe]) {
return "A pipeline with this name already exists"
}
}
}
} else {
return "A pipeline name can only contain letters, numbers, and spaces"
}
}
return ""
},
currentCameraIndex: {
get() {
return this.$store.getters.currentCameraIndex;
},
set(value) {
this.$store.commit('currentCameraIndex', value);
}
},
currentPipelineIndex: {
get() {
return this.$store.getters.currentPipelineIndex + (this.$store.getters.isDriverMode ? 1 : 0);
},
set(value) {
this.$store.commit('currentPipelineIndex', value - (this.$store.getters.isDriverMode ? 1 : 0));
}
}
},
methods: {
changeCameraName() {
this.newCameraName = this.$store.getters.cameraList[this.currentCameraIndex];
this.isCameraNameEdit = true;
},
saveCameraNameChange() {
if (this.checkCameraName === "") {
// this.handleInputWithIndex("changeCameraName", this.newCameraName);
this.axios.post('http://' + this.$address + '/api/setCameraNickname',
{name: this.newCameraName, cameraIndex: this.$store.getters.currentCameraIndex})
// eslint-disable-next-line
.then(r => {
this.$emit('camera-name-changed')
})
.catch(e => {
console.log("HTTP error while changing camera name " + e);
this.$emit('camera-name-changed')
})
this.discardCameraNameChange();
}
},
discardCameraNameChange() {
this.isCameraNameEdit = false;
this.newCameraName = "";
},
toPipelineNameChange() {
this.newPipelineName = this.$store.getters.pipelineList[this.currentPipelineIndex - 1];
this.isPipelineNameEdit = true;
this.namingDialog = true;
},
toCreatePipeline() {
this.newPipelineName = "New Pipeline";
this.isPipelineNameEdit = false;
this.namingDialog = true;
},
openDuplicateDialog() {
this.pipeIndexToDuplicate = this.currentPipelineIndex - 1;
this.duplicateDialog = true;
},
deleteCurrentPipeline() {
if (this.$store.getters.pipelineList.length > 1) {
this.handleInputWithIndex('deleteCurrentPipeline', {});
} else {
this.snackbar = true;
}
},
savePipelineNameChange() {
if (this.checkPipelineName === "") {
if (this.isPipelineNameEdit) {
this.handleInputWithIndex("changePipelineName", this.newPipelineName);
} else {
this.handleInputWithIndex("addNewPipeline", this.newPipelineName);
}
this.discardPipelineNameChange();
}
},
duplicatePipeline() {
// if (!this.anotherCamera) {
// this.pipelineDuplicate.camera = -1
// }
this.handleInputWithIndex("duplicatePipeline", this.pipeIndexToDuplicate);
// this.axios.post("http://" + this.$address + "/api/vision/duplicate", this.pipeIndexToDuplicate);
this.closeDuplicateDialog();
},
closeDuplicateDialog() {
this.duplicateDialog = false;
this.pipeIndexToDuplicate = undefined;
},
discardPipelineNameChange() {
this.namingDialog = false;
this.isPipelineNameEdit = false;
this.newPipelineName = "";
},
}
export default {
name: "CameraAndPipelineSelect",
components: {
CVicon,
CVselect,
CVinput
},
data: () => {
return {
re: RegExp("^[A-Za-z0-9 \\-)(]*[A-Za-z0-9][A-Za-z0-9 \\-)(.]*$"),
isCameraNameEdit: false,
newCameraName: "",
cameraNameError: "",
isPipelineNameEdit: false,
namingDialog: false,
newPipelineName: "",
duplicateDialog: false,
showPipeTypeDialog: false,
proposedPipelineType : 0,
pipeIndexToDuplicate: undefined
} }
},
computed: {
checkCameraName() {
if (this.newCameraName !== this.$store.getters.cameraList[this.currentCameraIndex]) {
if (this.re.test(this.newCameraName)) {
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"
}
}
}
} else {
return "A camera name can only contain letters, numbers and spaces"
}
}
return "";
},
checkPipelineName() {
if (this.newPipelineName !== this.$store.getters.pipelineList[this.currentPipelineIndex - 1] || this.isPipelineNameEdit === false) {
if (this.re.test(this.newPipelineName)) {
for (let pipe in this.$store.getters.pipelineList) {
if (this.$store.getters.pipelineList.hasOwnProperty(pipe)) {
if (this.newPipelineName === this.$store.getters.pipelineList[pipe]) {
return "A pipeline with this name already exists"
}
}
}
} else {
return "A pipeline name can only contain letters, numbers, and spaces"
}
}
return ""
},
currentCameraIndex: {
get() {
return this.$store.getters.currentCameraIndex;
},
set(value) {
this.$store.commit('currentCameraIndex', value);
}
},
currentPipelineIndex: {
get() {
return this.$store.getters.currentPipelineIndex + (this.$store.getters.isDriverMode ? 1 : 0);
},
set(value) {
this.$store.commit('currentPipelineIndex', value - (this.$store.getters.isDriverMode ? 1 : 0));
}
},
currentPipelineType: {
get() {
return this.$store.getters.currentPipelineSettings.pipelineType - 2;
},
set(value) {
value; // nop, since we have the dialog for this
}
}
},
methods: {
showTypeDialog(idx) {
// Only show the dialog if it's a new type
this.showPipeTypeDialog = idx !== this.currentPipelineType;
this.proposedPipelineType = idx;
},
changePipeType(actuallyChange) {
const newIdx = actuallyChange ? this.proposedPipelineType : this.currentPipelineType
this.handleInputWithIndex('pipelineType', newIdx);
this.showPipeTypeDialog = false;
},
changeCameraName() {
this.newCameraName = this.$store.getters.cameraList[this.currentCameraIndex];
this.isCameraNameEdit = true;
},
saveCameraNameChange() {
if (this.checkCameraName === "") {
// this.handleInputWithIndex("changeCameraName", this.newCameraName);
this.axios.post('http://' + this.$address + '/api/setCameraNickname',
{name: this.newCameraName, cameraIndex: this.$store.getters.currentCameraIndex})
// eslint-disable-next-line
.then(r => {
this.$emit('camera-name-changed')
})
.catch(e => {
console.log("HTTP error while changing camera name " + e);
this.$emit('camera-name-changed')
})
this.discardCameraNameChange();
}
},
discardCameraNameChange() {
this.isCameraNameEdit = false;
this.newCameraName = "";
},
toPipelineNameChange() {
this.newPipelineName = this.$store.getters.pipelineList[this.currentPipelineIndex - 1];
this.isPipelineNameEdit = true;
this.namingDialog = true;
},
toCreatePipeline() {
this.newPipelineName = "New Pipeline";
this.isPipelineNameEdit = false;
this.namingDialog = true;
},
deleteCurrentPipeline() {
if (this.$store.getters.pipelineList.length > 1) {
this.handleInputWithIndex('deleteCurrentPipeline', {});
} else {
this.snackbar = true;
}
},
savePipelineNameChange() {
if (this.checkPipelineName === "") {
if (this.isPipelineNameEdit) {
this.handleInputWithIndex("changePipelineName", this.newPipelineName);
} else {
this.handleInputWithIndex("addNewPipeline", [this.newPipelineName, this.newPipelineType]); // 0 for reflective, 1 for colored shpae
}
this.discardPipelineNameChange();
}
},
duplicatePipeline() {
this.handleInputWithIndex("duplicatePipeline", this.currentPipelineIndex);
},
discardPipelineNameChange() {
this.namingDialog = false;
this.isPipelineNameEdit = false;
this.newPipelineName = "";
},
}
}
</script> </script>
<style scoped> <style scoped>
</style> </style>

View File

@@ -60,4 +60,4 @@
<style scoped> <style scoped>
</style> </style>

View File

@@ -45,4 +45,4 @@
<style scoped> <style scoped>
</style> </style>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,7 @@
# JSPDF Fonts
These are .js interpretations of the .tff files in the branding folder. They are used by jspdf to apply branding-approprate fonts to any .pdf file generation (ex: calibration targets)
https://peckconsulting.s3.amazonaws.com/fontconverter/fontconverter.html is the converter used to generate them.
https://www.devlinpeck.com/tutorials/jspdf-custom-font has more info creating/using them.

View File

@@ -29,9 +29,11 @@ Vue.use(VueAxios, axios);
Vue.prototype.$msgPack = msgPack(true); Vue.prototype.$msgPack = msgPack(true);
import {dataHandleMixin} from './mixins/global/dataHandleMixin' import {dataHandleMixin} from './mixins/global/dataHandleMixin'
Vue.mixin(dataHandleMixin); Vue.mixin(dataHandleMixin);
import {stateMixin} from './mixins/global/stateMixin'
Vue.mixin(stateMixin);
new Vue({ new Vue({
router, router,
store, store,

View File

@@ -0,0 +1,10 @@
export const stateMixin = {
methods: {
currentPipelineType() {
return this.$store.getters.pipelineType
},
currentPipelineSettings() {
return this.$store.getters.currentPipelineSettings
},
}
};

View File

@@ -122,4 +122,4 @@ function shrinkRange(range, color) {
} }
export default {initColorPicker, colorPickerClick, eyeDrop, expand, shrink} export default {initColorPicker, colorPickerClick, eyeDrop, expand, shrink}

View File

@@ -5,4 +5,4 @@ $body-font-family: $default-font;
$heading-font-family: $default-font; $heading-font-family: $default-font;
.v-application { .v-application {
font-family: $default-font !important; font-family: $default-font !important;
} }

View File

@@ -15,6 +15,13 @@ export default new Vuex.Store({
}, },
state: { state: {
backendConnected: false, backendConnected: false,
ntConnectionInfo: {
connected: false,
address: "",
clients: 0,
possibleRios: ["Loading..."],
deviceips: ["Loading..."],
},
connectedCallbacks: [], connectedCallbacks: [],
colorPicking: false, colorPicking: false,
logsOverlay: false, logsOverlay: false,
@@ -42,7 +49,7 @@ export default new Vuex.Store({
isFovConfigurable: true, isFovConfigurable: true,
calibrated: false, calibrated: false,
currentPipelineSettings: { currentPipelineSettings: {
pipelineType: 2, // One of "driver", "reflective", "shape" pipelineType: 2, // One of "calib", "driver", "reflective", "shape"
// 2 is reflective // 2 is reflective
// Settings that apply to all pipeline types // Settings that apply to all pipeline types
@@ -143,6 +150,8 @@ export default new Vuex.Store({
settings: set('settings'), settings: set('settings'),
calibrationData: set('calibrationData'), calibrationData: set('calibrationData'),
metrics: set('metrics'), metrics: set('metrics'),
ntConnectionInfo: set('ntConnectionInfo'),
backendConnected: set('backendConnected'),
logString: (state, newStr) => { logString: (state, newStr) => {
const str = state.logMessages; const str = state.logMessages;
str.push(newStr); str.push(newStr);
@@ -245,5 +254,6 @@ export default new Vuex.Store({
}, },
pipelineList: state => state.cameraSettings[state.currentCameraIndex].pipelineNicknames, pipelineList: state => state.cameraSettings[state.currentCameraIndex].pipelineNicknames,
calibrationList: state => state.cameraSettings[state.currentCameraIndex].calibrations, calibrationList: state => state.cameraSettings[state.currentCameraIndex].calibrations,
pipelineType: state => state.cameraSettings[state.currentCameraIndex].currentPipelineSettings.pipelineType
} }
}) })

View File

@@ -69,4 +69,4 @@ export default {
} }
} }
}; };

View File

@@ -5,4 +5,4 @@ const theme = Object.freeze({
background: "#232C37", background: "#232C37",
}); });
export default theme; export default theme;

View File

@@ -19,15 +19,16 @@
<CVselect <CVselect
v-model="currentCameraIndex" v-model="currentCameraIndex"
name="Camera" name="Camera"
select-cols="10"
:list="$store.getters.cameraList" :list="$store.getters.cameraList"
@input="handleInput('currentCamera',currentCameraIndex)" @input="handleInput('currentCamera',currentCameraIndex)"
:select-cols="$vuetify.breakpoint.mdAndUp ? 10 : 7"
/> />
<CVnumberinput <CVnumberinput
v-model="cameraSettings.fov" v-model="cameraSettings.fov"
: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'" :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" name="Maximum diagonal FOV"
:disabled="!cameraSettings.isFovConfigurable" :disabled="!cameraSettings.isFovConfigurable"
:label-cols="$vuetify.breakpoint.mdAndUp ? undefined : 7"
/> />
<br> <br>
<CVnumberinput <CVnumberinput
@@ -35,6 +36,7 @@
name="Camera pitch" name="Camera pitch"
tooltip="How many degrees above the horizontal the physical camera is tilted" tooltip="How many degrees above the horizontal the physical camera is tilted"
:step="0.01" :step="0.01"
:label-cols="$vuetify.breakpoint.mdAndUp ? undefined : 7"
/> />
<br> <br>
<v-btn <v-btn
@@ -66,43 +68,51 @@
cols="12" cols="12"
md="6" md="6"
> >
<CVselect <v-form
v-model="selectedFilteredResIndex" ref="form"
name="Resolution" v-model="settingsValid"
select-cols="7" >
:list="stringResolutionList" <CVselect
:disabled="isCalibrating" v-model="selectedFilteredResIndex"
tooltip="Resolution to calibrate at (you will have to calibrate every resolution you use 3D mode on)" name="Resolution"
/> select-cols="7"
<CVselect :list="stringResolutionList"
v-model="boardType" :disabled="isCalibrating"
name="Board Type" tooltip="Resolution to calibrate at (you will have to calibrate every resolution you use 3D mode on)"
select-cols="7" />
:list="['Chessboard', 'Dot Grid']" <CVselect
:disabled="isCalibrating" v-model="boardType"
tooltip="Calibration board pattern to use" name="Board Type"
/> select-cols="7"
<CVnumberinput :list="['Chessboard', 'Dot Grid']"
v-model="squareSizeIn" :disabled="isCalibrating"
name="Pattern Spacing (in)" tooltip="Calibration board pattern to use"
label-cols="5" />
tooltip="Spacing between pattern features in inches" <CVnumberinput
:disabled="isCalibrating" v-model="squareSizeIn"
/> name="Pattern Spacing (in)"
<CVnumberinput tooltip="Spacing between pattern features in inches"
v-model="boardWidth" :disabled="isCalibrating"
name="Board width" :rules="[v => (v > 0) || 'Size must be positive']"
label-cols="5" :label-cols="$vuetify.breakpoint.mdAndUp ? 5 : 7"
tooltip="Width of the board in dots or chessboard squares; with the standard chessboard, this is usually 8" />
:disabled="isCalibrating" <CVnumberinput
/> v-model="boardWidth"
<CVnumberinput name="Board width"
v-model="boardHeight" tooltip="Width of the board in dots or chessboard squares"
name="Board height" :disabled="isCalibrating"
label-cols="5" :rules="[v => (v >= 4) || 'Width must be at least 4']"
tooltip="Height of the board in dots or chessboard squares; with the standard chessboard, this is usually 8" :label-cols="$vuetify.breakpoint.mdAndUp ? 5 : 7"
:disabled="isCalibrating" />
/> <CVnumberinput
v-model="boardHeight"
name="Board height"
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> </v-col>
<!-- Calibrated table --> <!-- Calibrated table -->
@@ -227,19 +237,14 @@
small small
outlined outlined
style="width: 100%;" style="width: 100%;"
:disabled="!settingsValid"
@click="downloadBoard" @click="downloadBoard"
> >
<v-icon left> <v-icon left>
mdi-download mdi-download
</v-icon> </v-icon>
Download Chessboard Download Target
</v-btn> </v-btn>
<a
ref="calibrationFile"
style="color: black; text-decoration: none; display: none"
:href="require('../assets/chessboard.png')"
download="chessboard.png"
/>
</v-col> </v-col>
</v-row> </v-row>
</div> </div>
@@ -325,6 +330,8 @@ import CVnumberinput from '../components/common/cv-number-input';
import CVslider from '../components/common/cv-slider'; import CVslider from '../components/common/cv-slider';
import CVimage from "../components/common/cv-image"; import CVimage from "../components/common/cv-image";
import TooltippedLabel from "../components/common/cv-tooltipped-label"; import TooltippedLabel from "../components/common/cv-tooltipped-label";
import jsPDF from "jspdf";
import "../jsPDFFonts/Prompt-Regular-normal.js";
export default { export default {
name: 'Cameras', name: 'Cameras',
@@ -341,11 +348,12 @@ export default {
calibrationInProgress: false, calibrationInProgress: false,
calibrationFailed: false, calibrationFailed: false,
filteredVideomodeIndex: 0, filteredVideomodeIndex: 0,
settingsValid: true,
} }
}, },
computed: { computed: {
disallowCalibration() { disallowCalibration() {
return !(this.calibrationData.boardType === 0 || this.calibrationData.boardType === 1); return !(this.calibrationData.boardType === 0 || this.calibrationData.boardType === 1) || !this.settingsValid;
}, },
checkCancellation() { checkCancellation() {
if (this.isCalibrating) { if (this.isCalibrating) {
@@ -486,9 +494,97 @@ export default {
return ret; return ret;
}, },
downloadBoard() { downloadBoard() {
this.axios.get("http://" + this.$address + require('../assets/chessboard.png'), {responseType: 'blob'}).then((response) => { // Generates a .pdf of a board for calibration and downloads it
require('downloadjs')(response.data, "Calibration Board", "image/png");
}); //Murica paper.
var doc = new jsPDF({unit: 'in', format:'letter'});
var paper_x = 8.5;
var paper_y = 11.0;
//Load in custom fonts
console.log(doc.getFontList());
doc.setFont('Prompt-Regular');
doc.setFontSize(12);
// Common Parameters
var num_x = this.boardWidth;
var num_y = this.boardHeight;
var patternSize = this.squareSizeIn;
var isCheckerboard = (this.boardType==0);
var x_coord = 0.0;
var y_coord = 0.0;
var x_idx = 0;
var y_idx = 0;
var start_x = 0;
var start_y = 0;
var annotation = num_x + " x " + num_y + " | " + patternSize + "in "
if(isCheckerboard){
///////////////////////////////////////////
// Checkerboard Pattern
start_x = paper_x/2.0 - (num_x * patternSize)/2.0;
start_y = paper_y/2.0 - (num_y * patternSize)/2.0;
for(y_idx = 0; y_idx < num_y; y_idx++){
for(x_idx = 0; x_idx < num_x; x_idx++){
x_coord = start_x + x_idx * patternSize;
y_coord = start_y + y_idx * patternSize;
if((x_idx + y_idx) % 2 == 0){
doc.rect(x_coord, y_coord, patternSize, patternSize, "F");
}
}
}
} else {
///////////////////////////////////////////
// Assymetric Dot-Grid Pattern
// see https://github.com/opencv/opencv/blob/b450dd7a87bc69997a8417d94bdfb87427a9fe62/modules/calib3d/src/circlesgrid.cpp#L437
// as well as FindBoardCornersPipe.java's Dotboard implementation
start_x = paper_x/2.0 - ((2*(num_x-1) + (num_y-1) % 2) * patternSize)/2.0;
start_y = paper_y/2.0 - (num_y-1 * patternSize)/2.0;
// Dot Grid Pattern
for(y_idx = 0; y_idx < num_y; y_idx++){
for(x_idx = 0; x_idx < num_x; x_idx++){
x_coord = start_x + (2*x_idx + y_idx % 2) * patternSize;
y_coord = start_y + y_idx * patternSize;
doc.circle(x_coord, y_coord, patternSize/4.0, "F");
}
}
}
///////////////////////////////////////////
// Draw a fixed size inch ruler pattern to
// help users debug their printers
var lineStartX = 1.0;
var lineEndX = paper_x - lineStartX;
var lineY = paper_y - 1.0;
doc.setFont('Prompt-Regular');
doc.setLineWidth(0.01);
doc.line(lineStartX, lineY, lineEndX, lineY);
var segIdx = 0;
for(var tickX = lineStartX; tickX <= lineEndX; tickX += 1.0){
doc.line(tickX, lineY, tickX, lineY + 0.25);
doc.text(String(segIdx) + (segIdx == 0 ? " in" : ""), tickX + 0.1, lineY + 0.25);
segIdx++;
}
///////////////////////////////////////////
// Annotate what was drawn + branding
var img = new Image();
img.src = require('@/assets/logoMono.png');
doc.addImage(img, 'PNG', 1.0, 0.75, 1.4, 0.5 );
doc.setFont('Prompt-Regular');
doc.text(annotation, paper_x-1.0, 1.0, {maxWidth:(paper_x - 2.0)/2, align:"right"});
doc.save("calibrationTarget.pdf");
}, },
sendCameraSettings() { sendCameraSettings() {
this.axios.post("http://" + this.$address + "/api/settings/camera", { this.axios.post("http://" + this.$address + "/api/settings/camera", {
@@ -563,4 +659,4 @@ export default {
.v-data-table th, td { .v-data-table th, td {
font-size: 1rem !important; font-size: 1rem !important;
} }
</style> </style>

View File

@@ -34,10 +34,17 @@
:text-color="fpsTooLow ? 'white' : 'grey'" :text-color="fpsTooLow ? 'white' : 'grey'"
> >
<span class="pr-1">{{ Math.round($store.state.pipelineResults.fps) }}&nbsp;FPS &ndash;</span> <span class="pr-1">{{ Math.round($store.state.pipelineResults.fps) }}&nbsp;FPS &ndash;</span>
<span v-if="!fpsTooLow">{{ Math.round($store.state.pipelineResults.latency) }} ms latency</span> <span v-if="!fpsTooLow">{{ Math.min(Math.round($store.state.pipelineResults.latency), 100) }} ms latency</span>
<span v-else-if="!$store.getters.currentPipelineSettings.inputShouldShow">HSV thresholds are too broad; narrow them for better performance</span> <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> <span v-else>stop viewing the color stream for better performance</span>
</v-chip> </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-switch
v-model="driverMode" v-model="driverMode"
label="Driver Mode" label="Driver Mode"
@@ -83,6 +90,7 @@
> >
<v-card <v-card
color="primary" color="primary"
class="mt-3"
> >
<!-- <v-btn @click="onCamNameChange">--> <!-- <v-btn @click="onCamNameChange">-->
<!-- Reload--> <!-- Reload-->
@@ -91,7 +99,7 @@
</v-card> </v-card>
<v-card <v-card
:disabled="$store.getters.isDriverMode || $store.state.colorPicking" :disabled="$store.getters.isDriverMode || $store.state.colorPicking"
class="mt-3" class="mt-6 mb-3"
color="primary" color="primary"
> >
<v-row <v-row
@@ -421,6 +429,12 @@ export default {
.some(e => e.width === resolution.width && e.height === resolution.height) .some(e => e.width === resolution.width && e.height === resolution.height)
} }
}, },
isRobotConnected: {
get() {
// return this.$store.state.ntConnectionInfo.connected && this.$store.state.backendConnected;
return true;
}
}
}, },
created() { created() {
this.$store.state.connectedCallbacks.push(this.reloadStreams) this.$store.state.connectedCallbacks.push(this.reloadStreams)
@@ -469,4 +483,4 @@ th {
width: 80px; width: 80px;
text-align: center; text-align: center;
} }
</style> </style>

View File

@@ -1,162 +1,288 @@
<template> <template>
<div> <div>
<CVrangeSlider <CVrangeSlider
v-model="contourArea" v-model="contourArea"
name="Area" name="Area"
min="0" min="0"
max="100" max="100"
step="0.1" step="0.1"
@input="handlePipelineData('contourArea')" @input="handlePipelineData('contourArea')"
@rollback="e=> rollback('contourArea',e)"
/> />
<CVrangeSlider <CVrangeSlider
v-model="contourRatio" v-model="contourRatio"
name="Ratio (W/H)" v-if="currentPipelineType() !== 3"
tooltip="Min and max ratio between the width and height of a contour's bounding rectangle" name="Ratio (W/H)"
min="0" tooltip="Min and max ratio between the width and height of a contour's bounding rectangle"
max="100" min="0"
step="0.1" max="100"
@input="handlePipelineData('contourRatio')" step="0.1"
@rollback="e=> rollback('contourRatio',e)" @input="handlePipelineData('contourRatio')"
/> />
<CVrangeSlider <CVrangeSlider
v-model="contourFullness" v-model="contourFullness"
name="Fullness" v-if="currentPipelineType() !== 3"
tooltip="Min and max ratio between a contour's area and its bounding rectangle" name="Fullness"
min="0" tooltip="Min and max ratio between a contour's area and its bounding rectangle"
max="100" min="0"
@input="handlePipelineData('contourFullness')" max="100"
@rollback="e=> rollback('contourFullness',e)" @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')"
/> />
<CVslider <CVslider
v-model="contourSpecklePercentage" v-model="contourSpecklePercentage"
name="Speckle Rejection" name="Speckle Rejection"
tooltip="Rejects contours whose average area is less than the given percentage of the average area of all the other contours" tooltip="Rejects contours whose average area is less than the given percentage of the average area of all the other contours"
min="0" min="0"
max="100" max="100"
:slider-cols="largeBox" :slider-cols="largeBox"
@input="handlePipelineData('contourSpecklePercentage')" @input="handlePipelineData('contourSpecklePercentage')"
@rollback="e=> rollback('contourSpecklePercentage',e)"
/> />
<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','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"/>
<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')"
/>
<!-- 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')"
/>
<!-- 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')"
/>
<CVrangeSlider
v-model="contourRadius"
:disabled="currentPipelineSettings().contourShape !== 0"
name="Radius"
min="0"
max="100"
step="1"
@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')"
/>
<CVslider
v-model="circleAccuracy"
:disabled="currentPipelineSettings().contourShape !== 0"
name="Circle Accuracy"
min="0"
max="100"
:slider-cols="largeBox"
@input="handlePipelineData('circleAccuracy')"
/>
<v-divider class="mt-3"/>
</template>
<CVselect <CVselect
v-model="contourGroupingMode" v-model="contourSortMode"
name="Target Grouping" name="Target Sort"
tooltip="Whether or not every two targets are paired with each other (good for e.g. 2019 targets)" tooltip="Chooses the sorting mode used to determine the 'best' targets to provide to user code"
:select-cols="largeBox" :select-cols="largeBox"
:list="['Single','Dual']" :list="['Largest','Smallest','Highest','Lowest','Rightmost','Leftmost','Centermost']"
@input="handlePipelineData('contourGroupingMode')" @input="handlePipelineData('contourSortMode')"
@rollback="e=> rollback('contourGroupingMode',e)" @rollback="e => rollback('contourSortMode', e)"
/>
<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')"
@rollback="e=> rollback('contourIntersection',e)"
/>
<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)"
/> />
</div> </div>
</template> </template>
<script> <script>
import CVrangeSlider from '../../components/common/cv-range-slider' import CVrangeSlider from '../../components/common/cv-range-slider'
import CVselect from '../../components/common/cv-select' import CVselect from '../../components/common/cv-select'
import CVslider from '../../components/common/cv-slider' import CVslider from '../../components/common/cv-slider'
export default { export default {
name: 'Contours', name: 'Contours',
components: { components: {
CVrangeSlider, CVrangeSlider,
CVselect, CVselect,
CVslider CVslider
}, },
// eslint-disable-next-line vue/require-prop-types // eslint-disable-next-line vue/require-prop-types
props: ['value'], props: ['value'],
data() { data() {
return {} return {}
}, },
computed: { computed: {
largeBox: { largeBox: {
get() { get() {
// Sliders and selectors should be fuller width if we're on screen size medium and // Sliders and selectors should be fuller width if we're on screen size medium and
// up and either not in compact mode (because the tab will be 100% screen width), // up and either not in compact mode (because the tab will be 100% screen width),
// or in driver mode (where the card will also be 100% screen width). // or in driver mode (where the card will also be 100% screen width).
return this.$vuetify.breakpoint.mdAndUp && (!this.$store.state.compactMode || this.$store.getters.isDriverMode) ? 10 : 8; return this.$vuetify.breakpoint.mdAndUp && (!this.$store.state.compactMode || this.$store.getters.isDriverMode) ? 10 : 8;
} }
}, },
contourArea: { contourArea: {
get() { get() {
return this.$store.getters.currentPipelineSettings.contourArea return this.$store.getters.currentPipelineSettings.contourArea
}, },
set(val) { set(val) {
this.$store.commit("mutatePipeline", {"contourArea": val}); this.$store.commit("mutatePipeline", {"contourArea": val});
} }
}, },
contourRatio: { contourRatio: {
get() { get() {
return this.$store.getters.currentPipelineSettings.contourRatio return this.$store.getters.currentPipelineSettings.contourRatio
}, },
set(val) { set(val) {
this.$store.commit("mutatePipeline", {"contourRatio": val}); this.$store.commit("mutatePipeline", {"contourRatio": val});
} }
}, },
contourFullness: { contourFullness: {
get() { get() {
return this.$store.getters.currentPipelineSettings.contourFullness return this.$store.getters.currentPipelineSettings.contourFullness
}, },
set(val) { set(val) {
this.$store.commit("mutatePipeline", {"contourFullness": val}); this.$store.commit("mutatePipeline", {"contourFullness": val});
} }
}, },
contourSpecklePercentage: { contourPerimeter: {
get() { get() {
return this.$store.getters.currentPipelineSettings.contourSpecklePercentage return this.$store.getters.currentPipelineSettings.contourPerimeter
}, },
set(val) { set(val) {
this.$store.commit("mutatePipeline", {"contourSpecklePercentage": val}); this.$store.commit("mutatePipeline", {"contourPerimeter": val});
} }
}, },
contourGroupingMode: { contourSpecklePercentage: {
get() { get() {
return this.$store.getters.currentPipelineSettings.contourGroupingMode return this.$store.getters.currentPipelineSettings.contourSpecklePercentage
}, },
set(val) { set(val) {
this.$store.commit("mutatePipeline", {"contourGroupingMode": val}); this.$store.commit("mutatePipeline", {"contourSpecklePercentage": val});
} }
}, },
contourSortMode: { contourGroupingMode: {
get() { get() {
return this.$store.getters.currentPipelineSettings.contourSortMode return this.$store.getters.currentPipelineSettings.contourGroupingMode
}, },
set(val) { set(val) {
this.$store.commit("mutatePipeline", {"contourSortMode": val}); this.$store.commit("mutatePipeline", {"contourGroupingMode": val});
} }
}, },
contourIntersection: { contourSortMode: {
get() { get() {
return this.$store.getters.currentPipelineSettings.contourIntersection return this.$store.getters.currentPipelineSettings.contourSortMode
}, },
set(val) { set(val) {
this.$store.commit("mutatePipeline", {"contourIntersection": val}); this.$store.commit("mutatePipeline", {"contourSortMode": val});
} }
} },
}, contourShape: {
methods: {}, get() {
} return this.$store.getters.currentPipelineSettings.contourShape
},
set(val) {
this.$store.commit("mutatePipeline", {"contourShape": val});
}
},
contourIntersection: {
get() {
return this.$store.getters.currentPipelineSettings.contourIntersection
},
set(val) {
this.$store.commit("mutatePipeline", {"contourIntersection": val});
}
},
accuracyPercentage: {
get() {
return this.$store.getters.currentPipelineSettings.accuracyPercentage
},
set(val) {
this.$store.commit("mutatePipeline", {"accuracyPercentage": val});
}
},
contourRadius: {
get() {
return this.$store.getters.currentPipelineSettings.contourRadius
},
set(val) {
this.$store.commit("mutatePipeline", {"contourRadius": val});
}
},
circleDetectThreshold: {
get() {
return this.$store.getters.currentPipelineSettings.circleDetectThreshold
},
set(val) {
this.$store.commit("mutatePipeline", {"circleDetectThreshold": val});
}
},
maxCannyThresh: {
get() {
return this.$store.getters.currentPipelineSettings.maxCannyThresh
},
set(val) {
this.$store.commit("mutatePipeline", {"maxCannyThresh": val});
}
},
circleAccuracy: {
get() {
return this.$store.getters.currentPipelineSettings.circleAccuracy
},
set(val) {
this.$store.commit("mutatePipeline", {"circleAccuracy": val});
}
},
},
methods: {},
}
</script> </script>
<style lang="" scoped> <style lang="" scoped>
</style> </style>

View File

@@ -170,7 +170,11 @@
// It would probably be cleaner if this checked that we're on the Raspi 3 instead of checking for GPU accel status // 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[ const width = this.$store.getters.videoFormatList[
this.$store.getters.currentCameraSettings.currentPipelineSettings.cameraVideoModeIndex]['width']; 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() { getNumSkippedStreamDivisors() {
return unfilteredStreamDivisors.length - this.getRawStreamDivisors().length; return unfilteredStreamDivisors.length - this.getRawStreamDivisors().length;

View File

@@ -156,4 +156,4 @@
</script> </script>
<style scoped> <style scoped>
</style> </style>

View File

@@ -60,7 +60,7 @@
}, },
data() { data() {
return { 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', 'Power Cell (7in)', '2016 High Goal'], //Keep in sync with TargetModel.java
snackbar: { snackbar: {
color: "Success", color: "Success",
text: "" text: ""
@@ -155,4 +155,4 @@
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }
</style> </style>

View File

@@ -97,4 +97,4 @@
.v-data-table td { .v-data-table td {
font-family: monospace !important; font-family: monospace !important;
} }
</style> </style>

View File

@@ -1,48 +1,66 @@
<template> <template>
<div> <div>
<CVrangeSlider <CVrangeSlider
v-model="hsvHue" v-model="hsvHue"
name="Hue" name="Hue"
tooltip="Describes color" tooltip="Describes color"
:min="0" :min="0"
:max="180" :max="180"
@input="handlePipelineData('hsvHue')" @input="handlePipelineData('hsvHue')"
@rollback="e => rollback('hue',e)" @rollback="e => rollback('hue',e)"
/> />
<CVrangeSlider <CVrangeSlider
v-model="hsvSaturation" v-model="hsvSaturation"
name="Saturation" name="Saturation"
tooltip="Describes colorfulness; the smaller this value the 'whiter' the color becomes" tooltip="Describes colorfulness; the smaller this value the 'whiter' the color becomes"
:min="0" :min="0"
:max="255" :max="255"
@input="handlePipelineData('hsvSaturation')" @input="handlePipelineData('hsvSaturation')"
@rollback="e => rollback('saturation',e)" @rollback="e => rollback('saturation',e)"
/> />
<CVrangeSlider <CVrangeSlider
v-model="hsvValue" v-model="hsvValue"
name="Value" name="Value"
tooltip="Describes lightness; the smaller this value the 'blacker' the color becomes" tooltip="Describes lightness; the smaller this value the 'blacker' the color becomes"
:min="0" :min="0"
:max="255" :max="255"
@input="handlePipelineData('hsvValue')" @input="handlePipelineData('hsvValue')"
@rollback="e => rollback('value',e)" @rollback="e => rollback('value',e)"
/> />
<template v-if="this.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)"
/>
<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)"
/>
</template>
<v-divider class="mb-3 mt-3"/>
<div class="pt-3 white--text"> <div class="pt-3 white--text">
Color Picker Color Picker
</div> </div>
<v-divider <v-divider
class="mt-3" class="mt-3"
/> />
<v-row <v-row
justify="center" justify="center"
class="mt-3 mb-3" class="mt-3 mb-3"
> >
<template v-if="!$store.state.colorPicking"> <template v-if="!$store.state.colorPicking">
<v-btn <v-btn
color="accent" color="accent"
class="ma-2 black--text" class="ma-2 black--text"
small small
@click="setFunction(3)" @click="setFunction(3)"
> >
<v-icon left> <v-icon left>
mdi-minus mdi-minus
@@ -50,10 +68,10 @@
Shrink Range Shrink Range
</v-btn> </v-btn>
<v-btn <v-btn
color="accent" color="accent"
class="ma-2 black--text" class="ma-2 black--text"
small small
@click="setFunction(1)" @click="setFunction(1)"
> >
<v-icon left> <v-icon left>
mdi-plus-minus mdi-plus-minus
@@ -61,10 +79,10 @@
Set To Average Set To Average
</v-btn> </v-btn>
<v-btn <v-btn
color="accent" color="accent"
class="ma-2 black--text" class="ma-2 black--text"
small small
@click="setFunction(2)" @click="setFunction(2)"
> >
<v-icon left> <v-icon left>
mdi-plus mdi-plus
@@ -74,11 +92,11 @@
</template> </template>
<template v-else> <template v-else>
<v-btn <v-btn
color="accent" color="accent"
class="ma-2 black--text" class="ma-2 black--text"
style="width: 30%;" style="width: 30%;"
small small
@click="setFunction(0)" @click="setFunction(0)"
> >
Cancel Cancel
</v-btn> </v-btn>
@@ -88,112 +106,130 @@
</template> </template>
<script> <script>
import CVrangeSlider from '../../components/common/cv-range-slider' import CVrangeSlider from '../../components/common/cv-range-slider'
import CVSwitch from "@/components/common/cv-switch";
export default {
name: 'Threshold',
components: {
CVrangeSlider
},
// eslint-disable-next-line vue/require-prop-types
props: ['value'],
data() {
return {
currentFunction: undefined,
colorPicker: undefined,
showThresholdState: 0
}
},
computed: {
hsvHue: {
get() {
return this.$store.getters.currentPipelineSettings.hsvHue
},
set(val) {
this.$store.commit("mutatePipeline", {"hsvHue": val})
}
},
hsvSaturation: {
get() {
return this.$store.getters.currentPipelineSettings.hsvSaturation
},
set(val) {
this.$store.commit("mutatePipeline", {"hsvSaturation": val})
}
},
hsvValue: {
get() {
return this.$store.getters.currentPipelineSettings.hsvValue
},
set(val) {
this.$store.commit("mutatePipeline", {"hsvValue": val})
}
}
},
mounted: function () {
const self = this;
this.colorPicker = require('../../plugins/ColorPicker').default;
this.$nextTick(() => {
self.colorPicker.initColorPicker();
});
},
methods: {
onClick(event) {
if (this.currentFunction !== undefined) {
this.colorPicker.initColorPicker();
let s = this.$store.getters.currentPipelineSettings; export default {
let hsvArray = this.colorPicker.colorPickerClick(event, this.currentFunction, name: 'Threshold',
[ components: {
[s.hsvHue[0], s.hsvSaturation[0], s.hsvValue[0]], CVSwitch,
[s.hsvHue[1], s.hsvSaturation[1], s.hsvValue[1]] CVrangeSlider
].map(hsv => hsv.map(it => it || 0))); },
// That `map` calls are to make sure that we don't let any undefined/null values slip in // eslint-disable-next-line vue/require-prop-types
this.currentFunction = undefined; props: ['value'],
this.$store.state.colorPicking = false; data() {
this.handlePipelineUpdate("outputShouldDraw", true); return {
currentFunction: undefined,
s.hsvHue = [hsvArray[0][0], hsvArray[1][0]]; colorPicker: undefined,
s.hsvSaturation = [hsvArray[0][1], hsvArray[1][1]]; showThresholdState: 0
s.hsvValue = [hsvArray[0][2], hsvArray[1][2]];
let msg = this.$msgPack.encode({
"changePipelineSetting": {
'hsvHue': s.hsvHue,
'hsvSaturation': s.hsvSaturation,
'hsvValue': s.hsvValue,
'cameraIndex': this.$store.state.currentCameraIndex
}
});
this.$socket.send(msg);
this.$emit('update');
}
},
setFunction(index) {
switch (index) {
case 0:
this.currentFunction = undefined;
this.$store.state.colorPicking = false;
this.handlePipelineUpdate("outputShouldDraw", true);
return;
case 1:
this.currentFunction = this.colorPicker.eyeDrop;
break;
case 2:
this.currentFunction = this.colorPicker.expand;
break;
case 3:
this.currentFunction = this.colorPicker.shrink;
break;
}
this.$store.state.colorPicking = true;
this.handlePipelineUpdate("outputShouldDraw", false);
this.$store.commit("mutatePipeline", {"inputShouldShow": true});
this.handlePipelineUpdate("inputShouldShow", true);
}
}
} }
},
computed: {
hsvHue: {
get() {
return this.$store.getters.currentPipelineSettings.hsvHue
},
set(val) {
this.$store.commit("mutatePipeline", {"hsvHue": val})
}
},
hsvSaturation: {
get() {
return this.$store.getters.currentPipelineSettings.hsvSaturation
},
set(val) {
this.$store.commit("mutatePipeline", {"hsvSaturation": val})
}
},
hsvValue: {
get() {
return this.$store.getters.currentPipelineSettings.hsvValue
},
set(val) {
this.$store.commit("mutatePipeline", {"hsvValue": val})
}
},
erode: {
get() {
return this.$store.getters.currentPipelineSettings.erode
},
set(val) {
this.$store.commit("mutatePipeline", {"erode": val});
}
},
dilate: {
get() {
return this.$store.getters.currentPipelineSettings.dilate
},
set(val) {
this.$store.commit("mutatePipeline", {"dilate": val});
}
},
},
mounted: function () {
const self = this;
this.colorPicker = require('../../plugins/ColorPicker').default;
this.$nextTick(() => {
self.colorPicker.initColorPicker();
});
},
methods: {
onClick(event) {
if (this.currentFunction !== undefined) {
this.colorPicker.initColorPicker();
</script> let s = this.$store.getters.currentPipelineSettings;
let hsvArray = this.colorPicker.colorPickerClick(event, this.currentFunction,
[
[s.hsvHue[0], s.hsvSaturation[0], s.hsvValue[0]],
[s.hsvHue[1], s.hsvSaturation[1], s.hsvValue[1]]
].map(hsv => hsv.map(it => it || 0)));
// That `map` calls are to make sure that we don't let any undefined/null values slip in
this.currentFunction = undefined;
this.$store.state.colorPicking = false;
this.handlePipelineUpdate("outputShouldDraw", true);
s.hsvHue = [hsvArray[0][0], hsvArray[1][0]];
s.hsvSaturation = [hsvArray[0][1], hsvArray[1][1]];
s.hsvValue = [hsvArray[0][2], hsvArray[1][2]];
let msg = this.$msgPack.encode({
"changePipelineSetting": {
'hsvHue': s.hsvHue,
'hsvSaturation': s.hsvSaturation,
'hsvValue': s.hsvValue,
'cameraIndex': this.$store.state.currentCameraIndex
}
});
this.$socket.send(msg);
this.$emit('update');
}
},
setFunction(index) {
switch (index) {
case 0:
this.currentFunction = undefined;
this.$store.state.colorPicking = false;
this.handlePipelineUpdate("outputShouldDraw", true);
return;
case 1:
this.currentFunction = this.colorPicker.eyeDrop;
break;
case 2:
this.currentFunction = this.colorPicker.expand;
break;
case 3:
this.currentFunction = this.colorPicker.shrink;
break;
}
this.$store.state.colorPicking = true;
this.handlePipelineUpdate("outputShouldDraw", false);
this.$store.commit("mutatePipeline", {"inputShouldShow": true});
this.handlePipelineUpdate("inputShouldShow", true);
}
}
}
</script>

View File

@@ -49,6 +49,7 @@
return { return {
selectedTab: 0, selectedTab: 0,
snack: false, snack: false,
calibrationInProgress: false,
snackbar: { snackbar: {
color: "accent", color: "accent",
text: "" text: ""
@@ -85,4 +86,4 @@
height: auto !important; height: auto !important;
vertical-align: middle; vertical-align: middle;
} }
</style> </style>

View File

@@ -91,7 +91,7 @@
<v-col <v-col
cols="12" cols="12"
sm="6" sm="6"
lg="3" md="4"
> >
<v-btn <v-btn
color="secondary" color="secondary"
@@ -106,7 +106,7 @@
<v-col <v-col
cols="12" cols="12"
sm="6" sm="6"
lg="3" md="4"
> >
<v-btn <v-btn
color="secondary" color="secondary"
@@ -120,7 +120,21 @@
</v-col> </v-col>
<v-col <v-col
cols="12" 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 <v-btn
color="red" color="red"
@@ -134,7 +148,7 @@
</v-col> </v-col>
<v-col <v-col
cols="12" cols="12"
lg="3" lg="6"
> >
<v-btn <v-btn
color="red" color="red"
@@ -172,6 +186,15 @@
:href="'http://' + this.$address + '/api/settings/photonvision_config.zip'" :href="'http://' + this.$address + '/api/settings/photonvision_config.zip'"
download="photonvision-settings.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> </div>
</template> </template>
@@ -181,6 +204,7 @@ export default {
data() { data() {
return { return {
snack: false, snack: false,
uploadPercentage: 0.0,
snackbar: { snackbar: {
color: "success", color: "success",
text: "" text: ""
@@ -226,7 +250,7 @@ export default {
{headers: {"Content-Type": "multipart/form-data"}}).then(() => { {headers: {"Content-Type": "multipart/form-data"}}).then(() => {
this.snackbar = { this.snackbar = {
color: "success", color: "success",
text: "Settings imported successfully! Program will now exit...", text: "Settings imported successfully! PhotonVision will restart in the background...",
}; };
this.snack = true; this.snack = true;
}).catch(err => { }).catch(err => {
@@ -234,7 +258,7 @@ export default {
this.snackbar = { this.snackbar = {
color: "error", color: "error",
text: "Error while uploading settings file! Could not process provided file.", text: "Error while uploading settings file! Could not process provided file.",
}; };
} else if (err.request) { } else if (err.request) {
this.snackbar = { this.snackbar = {
color: "error", color: "error",
@@ -249,6 +273,52 @@ export default {
this.snack = true; 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> </script>
@@ -266,6 +336,8 @@ export default {
text-align: left; text-align: left;
margin-bottom: 10px; margin-bottom: 10px;
width: 100%; width: 100%;
display: block;
overflow-x: auto;
} }
.infoElem { .infoElem {
@@ -276,4 +348,4 @@ export default {
border-right: 1px solid; border-right: 1px solid;
} }
</style> </style>

View File

@@ -41,4 +41,4 @@
<style lang="" scoped> <style lang="" scoped>
</style> </style>

View File

@@ -4,20 +4,21 @@
ref="form" ref="form"
v-model="valid" v-model="valid"
> >
<CVSwitch
v-model="runNTServer"
name="Run NetworkTables Server"
tooltip="If enabled, this device will create a NT server. This is useful for home debugging, but should be disabled on-robot."
/>
<CVnumberinput <CVnumberinput
v-model="teamNumber" v-model="teamNumber"
:disabled="settings.runNTServer" :disabled="settings.runNTServer"
name="Team Number" 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']" :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" class="mb-4"
:label-cols="$vuetify.breakpoint.mdAndUp ? undefined : 7"
/> />
<v-chip label color="red" v-bind:style="$vuetify.breakpoint.xsOnly ? 'height: auto;' : ''" text-color="white" v-if="parseInt(teamNumber) < 1 && !runNTServer">
<span class="text-wrap">
Team number not set! NetworkTables cannot connect.
</span>
</v-chip>
<CVradio <CVradio
v-model="connectionType" v-model="connectionType"
:list="['DHCP','Static']" :list="['DHCP','Static']"
:disabled="!$store.state.settings.networkSettings.supported" :disabled="!$store.state.settings.networkSettings.supported"
/> />
@@ -35,15 +36,84 @@
:rules="[v => isHostname(v) || 'Invalid hostname']" :rules="[v => isHostname(v) || 'Invalid hostname']"
name="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"
:text-cols="$vuetify.breakpoint.mdAndUp ? undefined : 7"
/>
<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-form> </v-form>
<v-btn <v-btn
color="accent" color="accent"
:class="runNTServer ? 'mt-3' : ''"
style="color: black; width: 100%;" style="color: black; width: 100%;"
:disabled="!valid && !runNTServer" :disabled="!valid && !runNTServer"
@click="sendGeneralSettings()" @click="sendGeneralSettings()"
> >
Save Save
</v-btn> </v-btn>
<v-divider class="mt-4 mb-4"/>
<v-row>
<v-col cols="12" sm="6">
<v-simple-table
fixed-header
height="100%"
dense
>
<template v-slot:default>
<thead style="font-size: 1.25rem;">
<tr>
<th>
Device IPs
</th>
</tr>
</thead>
<tbody>
<tr
v-for="(value, index) in $store.state.ntConnectionInfo.deviceips"
:key="index"
>
<td>{{ value }}</td>
</tr>
</tbody>
</template>
</v-simple-table>
</v-col>
<v-col cols="12" sm="6">
<v-simple-table
fixed-header
height="100%"
dense
>
<template v-slot:default>
<thead style="font-size: 1.25rem;">
<tr>
<th>
Possible RoboRIOs
</th>
</tr>
</thead>
<tbody>
<tr
v-for="(value, index) in $store.state.ntConnectionInfo.possibleRios"
:key="index"
>
<td>{{ value }}</td>
</tr>
</tbody>
</template>
</v-simple-table>
</v-col>
</v-row>
</div> </div>
</template> </template>
@@ -179,6 +249,24 @@ export default {
} }
</script> </script>
<style lang="" scoped> <style scoped>
.v-data-table {
/*text-align: center;*/
background-color: transparent !important;
width: 100%;
height: 100%;
overflow-y: auto;
}
.v-data-table th {
background-color: #006492 !important;
}
.v-data-table th, td {
font-size: 1rem !important;
}
.v-data-table td {
font-family: monospace !important;
}
</style> </style>

13
photon-core/.gitignore vendored Normal file
View File

@@ -0,0 +1,13 @@
bin/*
.settings/*
.project
.classpath
*.prefs
.gradle
.gradle/*
build
build/*
photonvision/*
photonvision_config/*
src/main/java/org/photonvision/PhotonVersion.java

32
photon-core/build.gradle Normal file
View File

@@ -0,0 +1,32 @@
import java.nio.file.Path
apply from: "${rootDir}/shared/common.gradle"
dependencies {
implementation project(':photon-targeting')
implementation 'io.javalin:javalin:4.2.0'
implementation 'org.msgpack:msgpack-core:0.9.0'
implementation 'org.msgpack:jackson-dataformat-msgpack:0.9.0'
// wpiutil
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"
implementation "org.jogamp.jogl:jogl-all:$joglVersion"
implementation "org.jogamp.gluegen:gluegen-rt:$joglVersion:natives-linux-aarch64"
implementation "org.jogamp.jogl:jogl-all:$joglVersion:natives-linux-aarch64"
// Zip
implementation 'org.zeroturnaround:zt-zip:1.14'
}
task writeCurrentVersionJava {
writePhotonVersionFile(Path.of("$projectDir", "src", "main", "java", "org", "photonvision", "PhotonVersion.java"),
versionString)
}
build.dependsOn writeCurrentVersionJava

View File

@@ -0,0 +1 @@
rootProject.name = 'photon-core'

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common; package org.photonvision.common;
public enum ProgramStatus { public enum ProgramStatus {

View File

@@ -14,13 +14,12 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.configuration; package org.photonvision.common.configuration;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import edu.wpi.first.wpilibj.geometry.Rotation2d; import edu.wpi.first.math.geometry.Rotation2d;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.LogGroup;

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.configuration; package org.photonvision.common.configuration;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
@@ -332,8 +331,8 @@ public class ConfigManager {
return loadedConfigurations; return loadedConfigurations;
} }
public void addCameraConfigurations(HashMap<VisionSource, CameraConfiguration> sources) { public void addCameraConfigurations(List<VisionSource> sources) {
getConfig().addCameraConfigs(sources.values()); getConfig().addCameraConfigs(sources);
requestSave(); requestSave();
} }

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.configuration; package org.photonvision.common.configuration;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@@ -24,7 +23,6 @@ import java.util.List;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class HardwareConfig { public class HardwareConfig {
public final String deviceName; public final String deviceName;
public final String deviceLogoPath; public final String deviceLogoPath;
public final String supportURL; public final String supportURL;

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.configuration; package org.photonvision.common.configuration;
public class HardwareSettings { public class HardwareSettings {

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.configuration; package org.photonvision.common.configuration;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.configuration; package org.photonvision.common.configuration;
import java.util.Collection; import java.util.Collection;
@@ -28,10 +27,10 @@ import org.photonvision.common.util.SerializationUtils;
import org.photonvision.raspi.PicamJNI; import org.photonvision.raspi.PicamJNI;
import org.photonvision.vision.processes.VisionModule; import org.photonvision.vision.processes.VisionModule;
import org.photonvision.vision.processes.VisionModuleManager; import org.photonvision.vision.processes.VisionModuleManager;
import org.photonvision.vision.processes.VisionSource;
// TODO rename this class // TODO rename this class
public class PhotonConfiguration { public class PhotonConfiguration {
private HardwareConfig hardwareConfig; private HardwareConfig hardwareConfig;
private HardwareSettings hardwareSettings; private HardwareSettings hardwareSettings;
private NetworkConfig networkConfig; private NetworkConfig networkConfig;
@@ -75,9 +74,9 @@ public class PhotonConfiguration {
return cameraConfigurations; return cameraConfigurations;
} }
public void addCameraConfigs(Collection<CameraConfiguration> config) { public void addCameraConfigs(Collection<VisionSource> sources) {
for (var c : config) { for (var s : sources) {
addCameraConfig(c); addCameraConfig(s.getCameraConfiguration());
} }
} }

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.dataflow; package org.photonvision.common.dataflow;
import java.util.function.Consumer; import java.util.function.Consumer;

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.dataflow; package org.photonvision.common.dataflow;
import java.util.ArrayList; import java.util.ArrayList;

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.dataflow; package org.photonvision.common.dataflow;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
@@ -27,7 +26,6 @@ import org.photonvision.common.logging.Logger;
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public class DataChangeService { public class DataChangeService {
private static final Logger logger = new Logger(DataChangeService.class, LogGroup.WebServer); private static final Logger logger = new Logger(DataChangeService.class, LogGroup.WebServer);
private static class ThreadSafeSingleton { private static class ThreadSafeSingleton {

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.dataflow; package org.photonvision.common.dataflow;
import java.util.ArrayList; import java.util.ArrayList;

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.dataflow; package org.photonvision.common.dataflow;
import java.util.List; import java.util.List;

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.dataflow.events; package org.photonvision.common.dataflow.events;
import org.photonvision.common.dataflow.DataChangeDestination; import org.photonvision.common.dataflow.DataChangeDestination;

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.dataflow.events; package org.photonvision.common.dataflow.events;
import org.photonvision.common.dataflow.DataChangeDestination; import org.photonvision.common.dataflow.DataChangeDestination;

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.dataflow.events; package org.photonvision.common.dataflow.events;
import io.javalin.websocket.WsContext; import io.javalin.websocket.WsContext;
@@ -23,7 +22,6 @@ import org.photonvision.common.dataflow.DataChangeDestination;
import org.photonvision.common.dataflow.DataChangeSource; import org.photonvision.common.dataflow.DataChangeSource;
public class IncomingWebSocketEvent<T> extends DataChangeEvent<T> { public class IncomingWebSocketEvent<T> extends DataChangeEvent<T> {
public final Integer cameraIndex; public final Integer cameraIndex;
public final WsContext originContext; public final WsContext originContext;

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.dataflow.events; package org.photonvision.common.dataflow.events;
import io.javalin.websocket.WsContext; import io.javalin.websocket.WsContext;
@@ -34,6 +33,13 @@ public class OutgoingUIEvent<T> extends DataChangeEvent<T> {
this.originContext = originContext; this.originContext = originContext;
} }
public static OutgoingUIEvent<HashMap<String, Object>> wrappedOf(
String commandName, Object value) {
HashMap<String, Object> data = new HashMap<>();
data.put(commandName, value);
return new OutgoingUIEvent<>(commandName, data);
}
public static OutgoingUIEvent<HashMap<String, Object>> wrappedOf( public static OutgoingUIEvent<HashMap<String, Object>> wrappedOf(
String commandName, String propertyName, Object value, WsContext originContext) { String commandName, String propertyName, Object value, WsContext originContext) {
HashMap<String, Object> data = new HashMap<>(); HashMap<String, Object> data = new HashMap<>();

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.dataflow.networktables; package org.photonvision.common.dataflow.networktables;
import edu.wpi.first.networktables.EntryListenerFlags; import edu.wpi.first.networktables.EntryListenerFlags;
@@ -23,7 +22,6 @@ import edu.wpi.first.networktables.NetworkTableEntry;
import java.util.function.Consumer; import java.util.function.Consumer;
public class NTDataChangeListener { public class NTDataChangeListener {
private final NetworkTableEntry watchedEntry; private final NetworkTableEntry watchedEntry;
private final int listenerID; private final int listenerID;

View File

@@ -14,22 +14,26 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.dataflow.networktables; package org.photonvision.common.dataflow.networktables;
import edu.wpi.first.networktables.EntryNotification; import edu.wpi.first.networktables.EntryNotification;
import edu.wpi.first.networktables.NetworkTable; import edu.wpi.first.networktables.NetworkTable;
import edu.wpi.first.networktables.NetworkTableEntry; import edu.wpi.first.networktables.NetworkTableEntry;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.opencv.core.Point;
import org.photonvision.common.dataflow.CVPipelineResultConsumer; import org.photonvision.common.dataflow.CVPipelineResultConsumer;
import org.photonvision.common.dataflow.structures.Packet; 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.pipeline.result.CVPipelineResult;
import org.photonvision.vision.pipeline.result.SimplePipelineResult; import org.photonvision.vision.target.TrackedTarget;
public class NTDataPublisher implements CVPipelineResultConsumer { public class NTDataPublisher implements CVPipelineResultConsumer {
private final NetworkTable rootTable = NetworkTablesManager.getInstance().kRootTable; private final NetworkTable rootTable = NetworkTablesManager.getInstance().kRootTable;
private NetworkTable subTable; private NetworkTable subTable;
private NetworkTableEntry rawBytesEntry; private NetworkTableEntry rawBytesEntry;
@@ -163,7 +167,9 @@ public class NTDataPublisher implements CVPipelineResultConsumer {
@Override @Override
public void accept(CVPipelineResult result) { public void accept(CVPipelineResult result) {
var simplified = new SimplePipelineResult(result); var simplified =
new PhotonPipelineResult(
result.getLatencyMillis(), simpleFromTrackedTargets(result.targets));
Packet packet = new Packet(simplified.getPacketSize()); Packet packet = new Packet(simplified.getPacketSize());
simplified.populatePacket(packet); simplified.populatePacket(packet);
@@ -201,4 +207,25 @@ public class NTDataPublisher implements CVPipelineResultConsumer {
} }
rootTable.getInstance().flush(); rootTable.getInstance().flush();
} }
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(),
cornerList));
}
return ret;
}
} }

View File

@@ -0,0 +1,205 @@
/*
* 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.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;
import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
import org.photonvision.common.scripting.ScriptEventType;
import org.photonvision.common.scripting.ScriptManager;
import org.photonvision.common.util.TimedTaskManager;
public class NetworkTablesManager {
private final NetworkTableInstance ntInstance = NetworkTableInstance.getDefault();
private final String kRootTableName = "/photonvision";
public final NetworkTable kRootTable = ntInstance.getTable(kRootTableName);
private NetworkTablesManager() {
ntInstance.addLogger(new NTLogger(), 0, 255); // to hide error messages
}
private static NetworkTablesManager INSTANCE;
public static NetworkTablesManager getInstance() {
if (INSTANCE == null) INSTANCE = new NetworkTablesManager();
return INSTANCE;
}
private static final Logger logger = new Logger(NetworkTablesManager.class, LogGroup.General);
private static class NTLogger implements Consumer<LogMessage> {
private boolean hasReportedConnectionFailure = false;
private long lastConnectMessageMillis = 0;
@Override
public void accept(LogMessage logMessage) {
if (!hasReportedConnectionFailure && logMessage.message.contains("timed out")) {
logger.error("NT Connection has failed! Will retry in background.");
hasReportedConnectionFailure = true;
getInstance().broadcastConnectedStatus();
} else if (logMessage.message.contains("connected")
&& System.currentTimeMillis() - lastConnectMessageMillis > 125) {
logger.info("NT Connected!");
hasReportedConnectionFailure = false;
lastConnectMessageMillis = System.currentTimeMillis();
ScriptManager.queueEvent(ScriptEventType.kNTConnected);
getInstance().broadcastVersion();
getInstance().broadcastConnectedStatus();
}
}
}
public void broadcastConnectedStatus() {
TimedTaskManager.getInstance().addOneShotTask(this::broadcastConnectedStatusImpl, 1000L);
}
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();
if (connections.length > 0) {
subMap.put("address", connections[0].remote_ip + ":" + connections[0].remote_port);
}
subMap.put("clients", connections.length);
}
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() {
kRootTable.getEntry("version").setString(PhotonVersion.versionString);
kRootTable.getEntry("buildDate").setString(PhotonVersion.buildDate);
}
public void setConfig(NetworkConfig config) {
if (config.runNTServer) {
setServerMode();
} else {
setClientMode(config.teamNumber);
}
broadcastVersion();
}
private void setClientMode(int teamNumber) {
logger.info("Starting NT Client");
ntInstance.stopServer();
ntInstance.startClientTeam(teamNumber);
ntInstance.startDSClient();
if (ntInstance.isConnected()) {
logger.info("[NetworkTablesManager] Connected to the robot!");
} else {
logger.error(
"[NetworkTablesManager] Could not connect to the robot! Will retry in the background...");
}
broadcastVersion();
}
private void setServerMode() {
logger.info("Starting NT Server");
ntInstance.stopClient();
ntInstance.startServer();
broadcastVersion();
}
}

View File

@@ -14,17 +14,15 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.dataflow.websocket; package org.photonvision.common.dataflow.websocket;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import org.photonvision.common.dataflow.CVPipelineResultConsumer; import org.photonvision.common.dataflow.CVPipelineResultConsumer;
import org.photonvision.common.dataflow.DataChangeService;
import org.photonvision.common.dataflow.events.OutgoingUIEvent;
import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger; import org.photonvision.common.logging.Logger;
import org.photonvision.server.SocketHandler;
import org.photonvision.vision.pipeline.result.CVPipelineResult; import org.photonvision.vision.pipeline.result.CVPipelineResult;
public class UIDataPublisher implements CVPipelineResultConsumer { public class UIDataPublisher implements CVPipelineResultConsumer {
@@ -39,16 +37,17 @@ public class UIDataPublisher implements CVPipelineResultConsumer {
@Override @Override
public void accept(CVPipelineResult result) { public void accept(CVPipelineResult result) {
var now = System.currentTimeMillis(); long now = System.currentTimeMillis();
var dataMap = new HashMap<String, Object>();
dataMap.put("latency", result.getLatencyMillis());
// only update the UI at 15hz // only update the UI at 15hz
if (lastUIResultUpdateTime + 1000.0 / 10.0 > now) return; if (lastUIResultUpdateTime + 1000.0 / 10.0 > now) return;
var uiMap = new HashMap<Integer, HashMap<String, Object>>(); var uiMap = new HashMap<Integer, HashMap<String, Object>>();
var dataMap = new HashMap<String, Object>();
dataMap.put("fps", result.fps); dataMap.put("fps", result.fps);
dataMap.put("latency", result.getLatencyMillis());
var targets = result.targets; var targets = result.targets;
@@ -57,18 +56,10 @@ public class UIDataPublisher implements CVPipelineResultConsumer {
uiTargets.add(t.toHashMap()); uiTargets.add(t.toHashMap());
} }
dataMap.put("targets", uiTargets); dataMap.put("targets", uiTargets);
uiMap.put(index, dataMap); uiMap.put(index, dataMap);
var retMap = new HashMap<String, Object>();
retMap.put("updatePipelineResult", uiMap);
try {
SocketHandler.getInstance().broadcastMessage(retMap, null);
} catch (JsonProcessingException e) {
logger.error(e.getMessage());
logger.error(Arrays.toString(e.getStackTrace()));
}
DataChangeService.getInstance()
.publishEvent(OutgoingUIEvent.wrappedOf("updatePipelineResult", uiMap));
lastUIResultUpdateTime = now; lastUIResultUpdateTime = now;
} }
} }

View File

@@ -14,14 +14,12 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.hardware.GPIO; package org.photonvision.common.hardware.GPIO;
import org.photonvision.common.configuration.HardwareConfig; import org.photonvision.common.configuration.HardwareConfig;
import org.photonvision.common.hardware.Platform; import org.photonvision.common.hardware.Platform;
public class CustomGPIO extends GPIOBase { public class CustomGPIO extends GPIOBase {
private boolean currentState; private boolean currentState;
private final int port; private final int port;

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.hardware.GPIO; package org.photonvision.common.hardware.GPIO;
import java.util.Arrays; import java.util.Arrays;

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.hardware.GPIO.pi; package org.photonvision.common.hardware.GPIO.pi;
@SuppressWarnings("SpellCheckingInspection") @SuppressWarnings("SpellCheckingInspection")

View File

@@ -14,20 +14,18 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.hardware.GPIO.pi; package org.photonvision.common.hardware.GPIO.pi;
import java.util.HashMap; import java.util.HashMap;
/** /**
* A class that defines the exceptions that can be thrown by Pigpio. * A class that defines the exceptions that can be thrown by Pigpio.
* *
* <p>Credit to nkolban * <p>Credit to nkolban
* https://github.com/nkolban/jpigpio/blob/master/JPigpio/src/jpigpio/PigpioException.java * https://github.com/nkolban/jpigpio/blob/master/JPigpio/src/jpigpio/PigpioException.java
*/ */
@SuppressWarnings({"SpellCheckingInspection", "unused", "RedundantSuppression"}) @SuppressWarnings({"SpellCheckingInspection", "unused", "RedundantSuppression"})
public class PigpioException extends Exception { public class PigpioException extends Exception {
private int rc = -99999999; private int rc = -99999999;
private static final long serialVersionUID = 443595760654129068L; private static final long serialVersionUID = 443595760654129068L;
@@ -67,10 +65,10 @@ public class PigpioException extends Exception {
} }
/** /**
* Retrieve 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. * @return The error code that was returned by the underlying Pigpio call.
*/ */
public int getErrorCode() { public int getErrorCode() {
return rc; return rc;
} // End of getErrorCode } // End of getErrorCode

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.hardware.GPIO.pi; package org.photonvision.common.hardware.GPIO.pi;
import static org.photonvision.common.hardware.GPIO.pi.PigpioException.*; import static org.photonvision.common.hardware.GPIO.pi.PigpioException.*;
@@ -24,7 +23,6 @@ import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger; import org.photonvision.common.logging.Logger;
public class PigpioPin extends GPIOBase { public class PigpioPin extends GPIOBase {
public static final Logger logger = new Logger(PigpioPin.class, LogGroup.General); public static final Logger logger = new Logger(PigpioPin.class, LogGroup.General);
private static final PigpioSocket piSocket = new PigpioSocket(); private static final PigpioSocket piSocket = new PigpioSocket();

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.hardware.GPIO.pi; package org.photonvision.common.hardware.GPIO.pi;
public class PigpioPulse { public class PigpioPulse {
@@ -23,14 +22,14 @@ public class PigpioPulse {
int delayMicros; int delayMicros;
/** /**
* Initialises a pulse. * Initialises a pulse.
* *
* @param gpioOn GPIO number to switch on at the start of the pulse. If zero, then no GPIO will be * @param gpioOn GPIO number to switch on at the start of the pulse. If zero, then no GPIO will be
* switched on. * switched on.
* @param gpioOff GPIO number to switch off at the start of the pulse. If zero, then no GPIO will * @param gpioOff GPIO number to switch off at the start of the pulse. If zero, then no GPIO will
* be switched off. * be switched off.
* @param delayMicros the delay in microseconds before the next pulse. * @param delayMicros the delay in microseconds before the next pulse.
*/ */
public PigpioPulse(int gpioOn, int gpioOff, int delayMicros) { public PigpioPulse(int gpioOn, int gpioOff, int delayMicros) {
this.gpioOn = gpioOn != 0 ? 1 << gpioOn : 0; this.gpioOn = gpioOn != 0 ? 1 << gpioOn : 0;
this.gpioOff = gpioOff != 0 ? 1 << gpioOff : 0; this.gpioOff = gpioOff != 0 ? 1 << gpioOff : 0;

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.hardware.GPIO.pi; package org.photonvision.common.hardware.GPIO.pi;
import static org.photonvision.common.hardware.GPIO.pi.PigpioException.*; import static org.photonvision.common.hardware.GPIO.pi.PigpioException.*;
@@ -41,12 +40,12 @@ public class PigpioSocket {
} }
/** /**
* Creates and starts a socket connection to a pigpio daemon on a remote host with the specified * Creates and starts a socket connection to a pigpio daemon on a remote host with the specified
* address and port * address and port
* *
* @param addr Address of remote pigpio daemon * @param addr Address of remote pigpio daemon
* @param port Port of remote pigpio daemon * @param port Port of remote pigpio daemon
*/ */
public PigpioSocket(String addr, int port) { public PigpioSocket(String addr, int port) {
try { try {
commandSocket = new PigpioSocketLock(addr, port); commandSocket = new PigpioSocketLock(addr, port);
@@ -56,10 +55,10 @@ public class PigpioSocket {
} }
/** /**
* Reconnects to the pigpio daemon * Reconnects to the pigpio daemon
* *
* @throws PigpioException on failure * @throws PigpioException on failure
*/ */
public void reconnect() throws PigpioException { public void reconnect() throws PigpioException {
try { try {
commandSocket.reconnect(); commandSocket.reconnect();
@@ -70,10 +69,10 @@ public class PigpioSocket {
} }
/** /**
* Terminates the connection to the pigpio daemon * Terminates the connection to the pigpio daemon
* *
* @throws PigpioException on failure * @throws PigpioException on failure
*/ */
public void gpioTerminate() throws PigpioException { public void gpioTerminate() throws PigpioException {
try { try {
commandSocket.terminate(); commandSocket.terminate();
@@ -84,12 +83,12 @@ public class PigpioSocket {
} }
/** /**
* Read the GPIO level * Read the GPIO level
* *
* @param pin Pin to read from * @param pin Pin to read from
* @return Value of the pin * @return Value of the pin
* @throws PigpioException on failure * @throws PigpioException on failure
*/ */
public boolean gpioRead(int pin) throws PigpioException { public boolean gpioRead(int pin) throws PigpioException {
try { try {
int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_READ.value, pin); int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_READ.value, pin);
@@ -102,12 +101,12 @@ public class PigpioSocket {
} }
/** /**
* Write the GPIO level * Write the GPIO level
* *
* @param pin Pin to write to * @param pin Pin to write to
* @param value Value to write * @param value Value to write
* @throws PigpioException on failure * @throws PigpioException on failure
*/ */
public void gpioWrite(int pin, boolean value) throws PigpioException { public void gpioWrite(int pin, boolean value) throws PigpioException {
try { try {
int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_WRITE.value, pin, value ? 1 : 0); int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_WRITE.value, pin, value ? 1 : 0);
@@ -119,10 +118,10 @@ public class PigpioSocket {
} }
/** /**
* Clears all waveforms and any data added by calls to {@link #waveAddGeneric(ArrayList)} * Clears all waveforms and any data added by calls to {@link #waveAddGeneric(ArrayList)}
* *
* @throws PigpioException on failure * @throws PigpioException on failure
*/ */
public void waveClear() throws PigpioException { public void waveClear() throws PigpioException {
try { try {
int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_WVCLR.value); int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_WVCLR.value);
@@ -134,12 +133,12 @@ public class PigpioSocket {
} }
/** /**
* Adds a number of pulses to the current waveform * Adds a number of pulses to the current waveform
* *
* @param pulses ArrayList of pulses to add * @param pulses ArrayList of pulses to add
* @return the new total number of pulses in the current waveform * @return the new total number of pulses in the current waveform
* @throws PigpioException on failure * @throws PigpioException on failure
*/ */
private int waveAddGeneric(ArrayList<PigpioPulse> pulses) throws PigpioException { private int waveAddGeneric(ArrayList<PigpioPulse> pulses) throws PigpioException {
// pigpio wave message format // pigpio wave message format
@@ -175,12 +174,12 @@ public class PigpioSocket {
} }
/** /**
* Creates pulses and adds them to the current waveform * Creates pulses and adds them to the current waveform
* *
* @param pulseTimeMillis Pulse length in milliseconds * @param pulseTimeMillis Pulse length in milliseconds
* @param blinks Number of times to pulse. -1 for repeat * @param blinks Number of times to pulse. -1 for repeat
* @param pinNo Pin to pulse * @param pinNo Pin to pulse
*/ */
private void addBlinkPulsesToWaveform(int pulseTimeMillis, int blinks, int pinNo) { private void addBlinkPulsesToWaveform(int pulseTimeMillis, int blinks, int pinNo) {
boolean repeat = blinks == -1; boolean repeat = blinks == -1;
@@ -208,13 +207,13 @@ public class PigpioSocket {
} }
/** /**
* Generates and sends a waveform to the given pins with the specified parameters. * Generates and sends a waveform to the given pins with the specified parameters.
* *
* @param pulseTimeMillis Pulse length in milliseconds * @param pulseTimeMillis Pulse length in milliseconds
* @param blinks Number of times to pulse. -1 for repeat * @param blinks Number of times to pulse. -1 for repeat
* @param pins Pins to pulse * @param pins Pins to pulse
* @throws PigpioException on failure * @throws PigpioException on failure
*/ */
public void generateAndSendWaveform(int pulseTimeMillis, int blinks, int... pins) public void generateAndSendWaveform(int pulseTimeMillis, int blinks, int... pins)
throws PigpioException { throws PigpioException {
if (pins.length == 0) return; if (pins.length == 0) return;
@@ -263,11 +262,11 @@ public class PigpioSocket {
} }
/** /**
* Stops the transmission of the current waveform * Stops the transmission of the current waveform
* *
* @return success * @return success
* @throws PigpioException on failure * @throws PigpioException on failure
*/ */
public boolean waveTxStop() throws PigpioException { public boolean waveTxStop() throws PigpioException {
try { try {
int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_WVHLT.value); int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_WVHLT.value);
@@ -280,12 +279,12 @@ public class PigpioSocket {
} }
/** /**
* Creates a waveform from the data provided by the prior calls to {@link * 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 * #waveAddGeneric(ArrayList)} Upon success a wave ID greater than or equal to 0 is returned
* *
* @return ID of the created waveform * @return ID of the created waveform
* @throws PigpioException on failure * @throws PigpioException on failure
*/ */
public int waveCreate() throws PigpioException { public int waveCreate() throws PigpioException {
try { try {
int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_WVCRE.value); int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_WVCRE.value);
@@ -298,11 +297,11 @@ public class PigpioSocket {
} }
/** /**
* Deletes the waveform with specified wave ID * Deletes the waveform with specified wave ID
* *
* @param waveId ID of the waveform to delete * @param waveId ID of the waveform to delete
* @throws PigpioException on failure * @throws PigpioException on failure
*/ */
public void waveDelete(int waveId) throws PigpioException { public void waveDelete(int waveId) throws PigpioException {
try { try {
int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_WVDEL.value, waveId); int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_WVDEL.value, waveId);
@@ -314,12 +313,12 @@ public class PigpioSocket {
} }
/** /**
* Transmits the waveform with specified wave ID. The waveform is sent once * Transmits the waveform with specified wave ID. The waveform is sent once
* *
* @param waveId ID of the waveform to transmit * @param waveId ID of the waveform to transmit
* @return The number of DMA control blocks in the waveform * @return The number of DMA control blocks in the waveform
* @throws PigpioException on failure * @throws PigpioException on failure
*/ */
public int waveSendOnce(int waveId) throws PigpioException { public int waveSendOnce(int waveId) throws PigpioException {
try { try {
int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_WVTX.value, waveId); int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_WVTX.value, waveId);
@@ -331,13 +330,13 @@ public class PigpioSocket {
} }
/** /**
* Transmits the waveform with specified wave ID. The waveform cycles until cancelled (either by * Transmits the waveform with specified wave ID. The waveform cycles until cancelled (either by
* the sending of a new waveform or {@link #waveTxStop()} * the sending of a new waveform or {@link #waveTxStop()}
* *
* @param waveId ID of the waveform to transmit * @param waveId ID of the waveform to transmit
* @return The number of DMA control blocks in the waveform * @return The number of DMA control blocks in the waveform
* @throws PigpioException on failure * @throws PigpioException on failure
*/ */
public int waveSendRepeat(int waveId) throws PigpioException { public int waveSendRepeat(int waveId) throws PigpioException {
try { try {
int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_WVTXR.value, waveId); int retCode = commandSocket.sendCmd(PigpioCommand.PCMD_WVTXR.value, waveId);
@@ -349,14 +348,14 @@ public class PigpioSocket {
} }
/** /**
* Starts hardware PWM on a GPIO at the specified frequency and dutycycle * Starts hardware PWM on a GPIO at the specified frequency and dutycycle
* *
* @param pin GPIO pin to start PWM on * @param pin GPIO pin to start PWM on
* @param pwmFrequency Frequency to run at (1Hz-125MHz). Frequencies above 30MHz are unlikely to * @param pwmFrequency Frequency to run at (1Hz-125MHz). Frequencies above 30MHz are unlikely to
* work * work
* @param pwmDuty Duty cycle to run at (0-1,000,000) * @param pwmDuty Duty cycle to run at (0-1,000,000)
* @throws PigpioException on failure * @throws PigpioException on failure
*/ */
public void hardwarePWM(int pin, int pwmFrequency, int pwmDuty) throws PigpioException { public void hardwarePWM(int pin, int pwmFrequency, int pwmDuty) throws PigpioException {
try { try {
ByteBuffer bb = ByteBuffer.allocate(4); ByteBuffer bb = ByteBuffer.allocate(4);

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.hardware.GPIO.pi; package org.photonvision.common.hardware.GPIO.pi;
import java.io.DataInputStream; import java.io.DataInputStream;
@@ -24,9 +23,9 @@ import java.net.Socket;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
/** /**
* Credit to nkolban * Credit to nkolban
* https://github.com/nkolban/jpigpio/blob/master/JPigpio/src/jpigpio/SocketLock.java * https://github.com/nkolban/jpigpio/blob/master/JPigpio/src/jpigpio/SocketLock.java
*/ */
final class PigpioSocketLock { final class PigpioSocketLock {
private static final int replyTimeoutMillis = 1000; private static final int replyTimeoutMillis = 1000;
@@ -82,16 +81,16 @@ final class PigpioSocketLock {
} }
/** /**
* Send extended command to pigpiod and return result code * Send extended command to pigpiod and return result code
* *
* @param cmd Command to send * @param cmd Command to send
* @param p1 Command parameter 1 * @param p1 Command parameter 1
* @param p2 Command parameter 2 * @param p2 Command parameter 2
* @param p3 Command parameter 3 (usually length of extended data - see paramater ext) * @param p3 Command parameter 3 (usually length of extended data - see paramater ext)
* @param ext Array of bytes containing extended data * @param ext Array of bytes containing extended data
* @return Command result code * @return Command result code
* @throws IOException in case of network connection error * @throws IOException in case of network connection error
*/ */
@SuppressWarnings("UnusedAssignment") @SuppressWarnings("UnusedAssignment")
public synchronized int sendCmd(int cmd, int p1, int p2, int p3, byte[] ext) throws IOException { public synchronized int sendCmd(int cmd, int p1, int p2, int p3, byte[] ext) throws IOException {
ByteBuffer bb = ByteBuffer.allocate(16 + ext.length); ByteBuffer bb = ByteBuffer.allocate(16 + ext.length);
@@ -136,11 +135,11 @@ final class PigpioSocketLock {
} }
/** /**
* Read all remaining bytes coming from pigpiod * Read all remaining bytes coming from pigpiod
* *
* @param data Array to store read bytes. * @param data Array to store read bytes.
* @throws IOException if unable to read from network * @throws IOException if unable to read from network
*/ */
public void readBytes(byte[] data) throws IOException { public void readBytes(byte[] data) throws IOException {
in.readFully(data); in.readFully(data);
} }

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.hardware; package org.photonvision.common.hardware;
import edu.wpi.first.networktables.NetworkTableEntry; import edu.wpi.first.networktables.NetworkTableEntry;
@@ -27,7 +26,6 @@ import org.photonvision.common.dataflow.networktables.NTDataChangeListener;
import org.photonvision.common.dataflow.networktables.NetworkTablesManager; import org.photonvision.common.dataflow.networktables.NetworkTablesManager;
import org.photonvision.common.hardware.GPIO.CustomGPIO; import org.photonvision.common.hardware.GPIO.CustomGPIO;
import org.photonvision.common.hardware.GPIO.pi.PigpioSocket; import org.photonvision.common.hardware.GPIO.pi.PigpioSocket;
import org.photonvision.common.hardware.VisionLED.VisionLEDMode;
import org.photonvision.common.hardware.metrics.MetricsBase; import org.photonvision.common.hardware.metrics.MetricsBase;
import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger; import org.photonvision.common.logging.Logger;
@@ -91,7 +89,7 @@ public class HardwareManager {
pigpioSocket); pigpioSocket);
ledModeEntry = NetworkTablesManager.getInstance().kRootTable.getEntry("ledMode"); ledModeEntry = NetworkTablesManager.getInstance().kRootTable.getEntry("ledMode");
ledModeEntry.setNumber(VisionLEDMode.VLM_DEFAULT.value); ledModeEntry.setNumber(VisionLEDMode.kDefault.value);
ledModeListener = ledModeListener =
visionLED == null visionLED == null
? null ? null

View File

@@ -0,0 +1,43 @@
/*
* 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 W"),
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;
}
}

View File

@@ -14,10 +14,9 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.hardware; package org.photonvision.common.hardware;
import edu.wpi.first.wpiutil.RuntimeDetector; import edu.wpi.first.util.RuntimeDetector;
import java.io.IOException; import java.io.IOException;
import org.photonvision.common.util.ShellExec; import org.photonvision.common.util.ShellExec;
@@ -46,7 +45,11 @@ public enum Platform {
private static final String OS_NAME = System.getProperty("os.name"); private static final String OS_NAME = System.getProperty("os.name");
private static final String OS_ARCH = System.getProperty("os.arch"); 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 = private static String UnknownPlatformString =
String.format("Unknown Platform. OS: %s, Architecture: %s", OS_NAME, OS_ARCH); String.format("Unknown Platform. OS: %s, Architecture: %s", OS_NAME, OS_ARCH);
@@ -62,7 +65,7 @@ public enum Platform {
} }
public static boolean isRaspberryPi() { public static boolean isRaspberryPi() {
return CurrentPlatform.equals(LINUX_RASPBIAN); return currentPlatform.equals(LINUX_RASPBIAN);
} }
@SuppressWarnings("StatementWithEmptyBody") @SuppressWarnings("StatementWithEmptyBody")
@@ -115,4 +118,22 @@ public enum Platform {
return this.value; 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 "";
}
} }

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.hardware; package org.photonvision.common.hardware;
import java.util.List; import java.util.List;

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.hardware; package org.photonvision.common.hardware;
import edu.wpi.first.networktables.EntryNotification; import edu.wpi.first.networktables.EntryNotification;
@@ -40,7 +39,7 @@ public class VisionLED {
private final int brightnessMax; private final int brightnessMax;
private final PigpioSocket pigpioSocket; private final PigpioSocket pigpioSocket;
private VisionLEDMode currentLedMode = VisionLEDMode.VLM_DEFAULT; private VisionLEDMode currentLedMode = VisionLEDMode.kDefault;
private BooleanSupplier pipelineModeSupplier; private BooleanSupplier pipelineModeSupplier;
private int mappedBrightnessPercentage; private int mappedBrightnessPercentage;
@@ -111,7 +110,7 @@ public class VisionLED {
} }
public void setState(boolean on) { public void setState(boolean on) {
setInternal(on ? VisionLEDMode.VLM_ON : VisionLEDMode.VLM_OFF, false); setInternal(on ? VisionLEDMode.kOn : VisionLEDMode.kOff, false);
} }
void onLedModeChange(EntryNotification entryNotification) { void onLedModeChange(EntryNotification entryNotification) {
@@ -120,20 +119,20 @@ public class VisionLED {
VisionLEDMode newLedMode; VisionLEDMode newLedMode;
switch (newLedModeRaw) { switch (newLedModeRaw) {
case -1: case -1:
newLedMode = VisionLEDMode.VLM_DEFAULT; newLedMode = VisionLEDMode.kDefault;
break; break;
case 0: case 0:
newLedMode = VisionLEDMode.VLM_OFF; newLedMode = VisionLEDMode.kOff;
break; break;
case 1: case 1:
newLedMode = VisionLEDMode.VLM_ON; newLedMode = VisionLEDMode.kOn;
break; break;
case 2: case 2:
newLedMode = VisionLEDMode.VLM_BLINK; newLedMode = VisionLEDMode.kBlink;
break; break;
default: default:
logger.warn("User supplied invalid LED mode, falling back to Default"); logger.warn("User supplied invalid LED mode, falling back to Default");
newLedMode = VisionLEDMode.VLM_DEFAULT; newLedMode = VisionLEDMode.kDefault;
break; break;
} }
setInternal(newLedMode, true); setInternal(newLedMode, true);
@@ -145,16 +144,16 @@ public class VisionLED {
if (fromNT) { if (fromNT) {
switch (newLedMode) { switch (newLedMode) {
case VLM_DEFAULT: case kDefault:
setStateImpl(pipelineModeSupplier.getAsBoolean()); setStateImpl(pipelineModeSupplier.getAsBoolean());
break; break;
case VLM_OFF: case kOff:
setStateImpl(false); setStateImpl(false);
break; break;
case VLM_ON: case kOn:
setStateImpl(true); setStateImpl(true);
break; break;
case VLM_BLINK: case kBlink:
blinkImpl(85, -1); blinkImpl(85, -1);
break; break;
} }
@@ -166,15 +165,15 @@ public class VisionLED {
+ newLedMode.toString() + newLedMode.toString()
+ "\""); + "\"");
} else { } else {
if (currentLedMode == VisionLEDMode.VLM_DEFAULT) { if (currentLedMode == VisionLEDMode.kDefault) {
switch (newLedMode) { switch (newLedMode) {
case VLM_DEFAULT: case kDefault:
setStateImpl(pipelineModeSupplier.getAsBoolean()); setStateImpl(pipelineModeSupplier.getAsBoolean());
break; break;
case VLM_OFF: case kOff:
setStateImpl(false); setStateImpl(false);
break; break;
case VLM_ON: case kOn:
setStateImpl(true); setStateImpl(true);
break; break;
} }
@@ -182,32 +181,4 @@ public class VisionLED {
logger.info("Changing LED internal state to " + newLedMode.toString()); logger.info("Changing LED internal state to " + newLedMode.toString());
} }
} }
public enum VisionLEDMode {
VLM_DEFAULT(-1),
VLM_OFF(0),
VLM_ON(1),
VLM_BLINK(2);
public final int value;
VisionLEDMode(int value) {
this.value = value;
}
@Override
public String toString() {
switch (this) {
case VLM_DEFAULT:
return "Default";
case VLM_OFF:
return "Off";
case VLM_ON:
return "On";
case VLM_BLINK:
return "Blink";
}
return "";
}
}
} }

View File

@@ -14,11 +14,9 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.hardware.metrics; package org.photonvision.common.hardware.metrics;
public class CPUMetrics extends MetricsBase { public class CPUMetrics extends MetricsBase {
private String cpuMemSplit = null; private String cpuMemSplit = null;
public String getMemory() { public String getMemory() {

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.hardware.metrics; package org.photonvision.common.hardware.metrics;
public class DiskMetrics extends MetricsBase { public class DiskMetrics extends MetricsBase {

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.hardware.metrics; package org.photonvision.common.hardware.metrics;
public class GPUMetrics extends MetricsBase { public class GPUMetrics extends MetricsBase {

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.hardware.metrics; package org.photonvision.common.hardware.metrics;
import java.io.PrintWriter; import java.io.PrintWriter;

View File

@@ -14,16 +14,15 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.hardware.metrics; package org.photonvision.common.hardware.metrics;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.util.HashMap; import java.util.HashMap;
import org.photonvision.common.dataflow.DataChangeService;
import org.photonvision.common.dataflow.events.OutgoingUIEvent;
import org.photonvision.common.hardware.Platform; import org.photonvision.common.hardware.Platform;
import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger; import org.photonvision.common.logging.Logger;
import org.photonvision.common.util.TimedTaskManager; import org.photonvision.common.util.TimedTaskManager;
import org.photonvision.server.SocketHandler;
public class MetricsPublisher { public class MetricsPublisher {
private static final Logger logger = new Logger(MetricsPublisher.class, LogGroup.General); private static final Logger logger = new Logger(MetricsPublisher.class, LogGroup.General);
@@ -65,14 +64,7 @@ public class MetricsPublisher {
metrics.put("gpuMemUtil", gpuMetrics.getMallocedMemory()); metrics.put("gpuMemUtil", gpuMetrics.getMallocedMemory());
metrics.put("diskUtilPct", diskMetrics.getUsedDiskPct()); metrics.put("diskUtilPct", diskMetrics.getUsedDiskPct());
var retMap = new HashMap<String, Object>(); DataChangeService.getInstance().publishEvent(OutgoingUIEvent.wrappedOf("metrics", metrics));
retMap.put("metrics", metrics);
try {
SocketHandler.getInstance().broadcastMessage(retMap, null);
} catch (JsonProcessingException e) {
logger.error("Exception while sending metrics!", e);
}
} }
private static class Singleton { private static class Singleton {

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.hardware.metrics; package org.photonvision.common.hardware.metrics;
public class RAMMetrics extends MetricsBase { public class RAMMetrics extends MetricsBase {

View File

@@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.photonvision.common.logging; package org.photonvision.common.logging;
public enum LogGroup { public enum LogGroup {

Some files were not shown because too many files have changed in this diff Show More