mirror of
https://github.com/PhotonVision/photonvision
synced 2026-07-01 02:41:42 +00:00
Initial 3.0 Commit
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -121,3 +121,4 @@ New client/chameleon-client/*
|
||||
*.jfr
|
||||
.DS_Store
|
||||
# *.iml
|
||||
chameleon-server/build
|
||||
|
||||
86
chameleon-server/build.gradle
Normal file
86
chameleon-server/build.gradle
Normal file
@@ -0,0 +1,86 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'com.github.johnrengelman.shadow' version '5.2.0'
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
baseName = 'shadow'
|
||||
configurations = [project.configurations.compile]
|
||||
classifier = ''
|
||||
archiveVersion = ''
|
||||
}
|
||||
|
||||
group 'com.chameleonvision'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
sourceCompatibility = 11
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = 'https://frcmaven.wpi.edu:443/artifactory/release'
|
||||
}
|
||||
}
|
||||
ext {
|
||||
wpilibVersion = '2020.2.2'
|
||||
openCVVersion = '3.4.7-2'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "io.javalin:javalin:3.7.0"
|
||||
|
||||
implementation "com.fasterxml.jackson.core:jackson-annotations:2.10.0"
|
||||
implementation "com.fasterxml.jackson.core:jackson-core:2.10.0"
|
||||
implementation "com.fasterxml.jackson.core:jackson-databind:2.10.0"
|
||||
|
||||
implementation "org.msgpack:msgpack-core:0.8.20"
|
||||
implementation "org.msgpack:jackson-dataformat-msgpack:0.8.20"
|
||||
|
||||
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.9'
|
||||
implementation "org.apache.commons:commons-math3:3.6.1"
|
||||
implementation group: 'commons-io', name: 'commons-io', version: '2.6'
|
||||
implementation "org.apache.commons:commons-collections4:4.4"
|
||||
implementation "org.apache.commons:commons-exec:1.3"
|
||||
|
||||
implementation "com.moandjiezana.toml:toml4j:0.7.2"
|
||||
|
||||
// javacv
|
||||
compile group: 'org.bytedeco', name: 'javacv-platform', version: '1.5.2'
|
||||
|
||||
// wpilib stuff
|
||||
implementation "edu.wpi.first.wpiutil:wpiutil-java:$wpilibVersion"
|
||||
implementation "edu.wpi.first.cameraserver:cameraserver-java:$wpilibVersion"
|
||||
|
||||
implementation "edu.wpi.first.cscore:cscore-java:$wpilibVersion"
|
||||
compile "edu.wpi.first.cscore:cscore-jni:$wpilibVersion:linuxaarch64bionic"
|
||||
compile "edu.wpi.first.cscore:cscore-jni:$wpilibVersion:linuxraspbian"
|
||||
compile "edu.wpi.first.cscore:cscore-jni:$wpilibVersion:linuxx86-64"
|
||||
compile "edu.wpi.first.cscore:cscore-jni:$wpilibVersion:osxx86-64"
|
||||
compile "edu.wpi.first.cscore:cscore-jni:$wpilibVersion:windowsx86-64"
|
||||
|
||||
implementation "edu.wpi.first.ntcore:ntcore-java:$wpilibVersion"
|
||||
compile "edu.wpi.first.ntcore:ntcore-jni:$wpilibVersion:linuxaarch64bionic"
|
||||
compile "edu.wpi.first.ntcore:ntcore-jni:$wpilibVersion:linuxraspbian"
|
||||
compile "edu.wpi.first.ntcore:ntcore-jni:$wpilibVersion:linuxx86-64"
|
||||
compile "edu.wpi.first.ntcore:ntcore-jni:$wpilibVersion:osxx86-64"
|
||||
compile "edu.wpi.first.ntcore:ntcore-jni:$wpilibVersion:windowsx86-64"
|
||||
|
||||
implementation "edu.wpi.first.thirdparty.frc2020.opencv:opencv-java:$openCVVersion"
|
||||
compile "edu.wpi.first.thirdparty.frc2020.opencv:opencv-jni:$openCVVersion:linuxaarch64bionic"
|
||||
compile "edu.wpi.first.thirdparty.frc2020.opencv:opencv-jni:$openCVVersion:linuxraspbian"
|
||||
compile "edu.wpi.first.thirdparty.frc2020.opencv:opencv-jni:$openCVVersion:linuxx86-64"
|
||||
compile "edu.wpi.first.thirdparty.frc2020.opencv:opencv-jni:$openCVVersion:osxx86-64"
|
||||
compile "edu.wpi.first.thirdparty.frc2020.opencv:opencv-jni:$openCVVersion:windowsx86-64"
|
||||
|
||||
// test stuff
|
||||
testImplementation('org.junit.jupiter:junit-jupiter:5.6.0')
|
||||
}
|
||||
|
||||
test {
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
events "passed", "skipped", "failed"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_12">
|
||||
<output url="file://$MODULE_DIR$/target/classes" />
|
||||
<output-test url="file://$MODULE_DIR$/target/test-classes" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Maven: io.javalin:javalin:3.7.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.61" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.jetbrains.kotlin:kotlin-stdlib:1.3.61" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.jetbrains:annotations:13.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.28" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-server:9.4.25.v20191220" level="project" />
|
||||
<orderEntry type="library" name="Maven: javax.servlet:javax.servlet-api:3.1.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-http:9.4.25.v20191220" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-util:9.4.25.v20191220" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-io:9.4.25.v20191220" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-webapp:9.4.25.v20191220" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-xml:9.4.25.v20191220" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-servlet:9.4.25.v20191220" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-security:9.4.25.v20191220" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-server:9.4.25.v20191220" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-common:9.4.25.v20191220" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-api:9.4.25.v20191220" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-client:9.4.25.v20191220" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-client:9.4.25.v20191220" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-servlet:9.4.25.v20191220" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.json:json:20190722" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.slf4j:slf4j-nop:1.7.26" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.commons:commons-math3:3.6.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: commons-io:commons-io:2.6" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.msgpack:msgpack-core:0.8.18" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.msgpack:jackson-dataformat-msgpack:0.8.18" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.9" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.10.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.10.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.10.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.junit.jupiter:junit-jupiter-engine:5.5.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apiguardian:apiguardian-api:1.1.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.junit.platform:junit-platform-engine:1.5.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.opentest4j:opentest4j:1.2.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.junit.platform:junit-platform-commons:1.5.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.junit.jupiter:junit-jupiter-api:5.5.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: edu.wpi.first.cscore:cscore-java:2020.2.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: edu.wpi.first.cscore:cscore-jni:linuxaarch64bionic:2020.2.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: edu.wpi.first.cscore:cscore-jni:linuxraspbian:2020.2.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: edu.wpi.first.cscore:cscore-jni:linuxx86-64:2020.2.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: edu.wpi.first.cscore:cscore-jni:osxx86-64:2020.2.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: edu.wpi.first.cscore:cscore-jni:windowsx86-64:2020.2.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: edu.wpi.first.cameraserver:cameraserver-java:2020.2.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: edu.wpi.first.ntcore:ntcore-java:2020.2.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: edu.wpi.first.ntcore:ntcore-jni:osxx86-64:2020.2.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: edu.wpi.first.ntcore:ntcore-jni:linuxraspbian:2020.2.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: edu.wpi.first.ntcore:ntcore-jni:linuxx86-64:2020.2.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: edu.wpi.first.ntcore:ntcore-jni:linuxaarch64bionic:2020.2.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: edu.wpi.first.ntcore:ntcore-jni:windowsx86-64:2020.2.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: edu.wpi.first.wpiutil:wpiutil-java:2020.2.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: edu.wpi.first.thirdparty.frc2020.opencv:opencv-java:3.4.7-2" level="project" />
|
||||
<orderEntry type="library" name="Maven: edu.wpi.first.thirdparty.frc2020.opencv:opencv-jni:linuxaarch64bionic:3.4.7-2" level="project" />
|
||||
<orderEntry type="library" name="Maven: edu.wpi.first.thirdparty.frc2020.opencv:opencv-jni:linuxraspbian:3.4.7-2" level="project" />
|
||||
<orderEntry type="library" name="Maven: edu.wpi.first.thirdparty.frc2020.opencv:opencv-jni:linuxx86-64:3.4.7-2" level="project" />
|
||||
<orderEntry type="library" name="Maven: edu.wpi.first.thirdparty.frc2020.opencv:opencv-jni:osxx86-64:3.4.7-2" level="project" />
|
||||
<orderEntry type="library" name="Maven: edu.wpi.first.thirdparty.frc2020.opencv:opencv-jni:windowsx86-64:3.4.7-2" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
6
chameleon-server/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
chameleon-server/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#Thu Feb 20 23:05:11 EST 2020
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
172
chameleon-server/gradlew
vendored
Normal file
172
chameleon-server/gradlew
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
84
chameleon-server/gradlew.bat
vendored
Normal file
84
chameleon-server/gradlew.bat
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
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.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
: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
|
||||
@@ -1,269 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.chameleon-vision.main</groupId>
|
||||
<artifactId>chameleon-vision</artifactId>
|
||||
|
||||
<version>2.3.2</version>
|
||||
<build>
|
||||
<plugins>
|
||||
<!--setup for java jdk 12-->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
<configuration>
|
||||
<source>12</source>
|
||||
<target>12</target>
|
||||
<verbose>true</verbose>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>com.chameleonvision.Main</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<wpilib.version>2020.2.2</wpilib.version>
|
||||
<opencv.version>3.4.7-2</opencv.version>
|
||||
<!-- <maven.compiler.source>12.0.2</maven.compiler.source>-->
|
||||
<!-- <maven.compiler.target>12.0.2</maven.compiler.target>-->
|
||||
<maven.compiler.release>12</maven.compiler.release>
|
||||
</properties>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>WPI</id>
|
||||
<name>WPILib Artifactory Server-releases</name>
|
||||
<url>https://frcmaven.wpi.edu:443/artifactory/release</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<dependencies>
|
||||
<!--javalin micro webservices framework-->
|
||||
<dependency>
|
||||
<groupId>io.javalin</groupId>
|
||||
<artifactId>javalin</artifactId>
|
||||
<version>RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<!--org.json from saving and loading data-->
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20190722</version>
|
||||
</dependency>
|
||||
|
||||
<!--slf4j for javalin -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-nop</artifactId>
|
||||
<version>1.7.26</version>
|
||||
</dependency>
|
||||
|
||||
<!--apache common classes libs-->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-math3</artifactId>
|
||||
<version>3.6.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.msgpack</groupId>
|
||||
<artifactId>msgpack-core</artifactId>
|
||||
<version>0.8.18</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.msgpack</groupId>
|
||||
<artifactId>jackson-dataformat-msgpack</artifactId>
|
||||
<version>0.8.18</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.9</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>2.10.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<version>2.10.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.10.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>5.5.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- supported platforms for wpilib JNI classifiers
|
||||
linuxaarch64bionic
|
||||
linuxraspbian
|
||||
linuxx86-64
|
||||
osxx86-64
|
||||
windowsx86-64
|
||||
-->
|
||||
<!--frc cscore java libs-->
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.cscore</groupId>
|
||||
<artifactId>cscore-java</artifactId>
|
||||
<version>${wpilib.version}</version>
|
||||
</dependency>
|
||||
<!--frc cscore interface libs-->
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.cscore</groupId>
|
||||
<artifactId>cscore-jni</artifactId>
|
||||
<version>${wpilib.version}</version>
|
||||
<classifier>linuxaarch64bionic</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.cscore</groupId>
|
||||
<artifactId>cscore-jni</artifactId>
|
||||
<version>${wpilib.version}</version>
|
||||
<classifier>linuxraspbian</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.cscore</groupId>
|
||||
<artifactId>cscore-jni</artifactId>
|
||||
<version>${wpilib.version}</version>
|
||||
<classifier>linuxx86-64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.cscore</groupId>
|
||||
<artifactId>cscore-jni</artifactId>
|
||||
<version>${wpilib.version}</version>
|
||||
<classifier>osxx86-64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.cscore</groupId>
|
||||
<artifactId>cscore-jni</artifactId>
|
||||
<version>${wpilib.version}</version>
|
||||
<classifier>windowsx86-64</classifier>
|
||||
</dependency>
|
||||
|
||||
<!--frc camera server libs-->
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.cameraserver</groupId>
|
||||
<artifactId>cameraserver-java</artifactId>
|
||||
<version>${wpilib.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--frc network table java libs-->
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.ntcore</groupId>
|
||||
<artifactId>ntcore-java</artifactId>
|
||||
<version>${wpilib.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--frc network tables interface libs-->
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.ntcore</groupId>
|
||||
<artifactId>ntcore-jni</artifactId>
|
||||
<version>${wpilib.version}</version>
|
||||
<classifier>osxx86-64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.ntcore</groupId>
|
||||
<artifactId>ntcore-jni</artifactId>
|
||||
<version>${wpilib.version}</version>
|
||||
<classifier>linuxraspbian</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.ntcore</groupId>
|
||||
<artifactId>ntcore-jni</artifactId>
|
||||
<version>${wpilib.version}</version>
|
||||
<classifier>linuxx86-64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.ntcore</groupId>
|
||||
<artifactId>ntcore-jni</artifactId>
|
||||
<version>${wpilib.version}</version>
|
||||
<classifier>linuxaarch64bionic</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.ntcore</groupId>
|
||||
<artifactId>ntcore-jni</artifactId>
|
||||
<version>${wpilib.version}</version>
|
||||
<classifier>windowsx86-64</classifier>
|
||||
</dependency>
|
||||
|
||||
<!--frc java libs-->
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.wpiutil</groupId>
|
||||
<artifactId>wpiutil-java</artifactId>
|
||||
<version>${wpilib.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- WPI OpenCV for all supported platforms -->
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.thirdparty.frc2020.opencv</groupId>
|
||||
<artifactId>opencv-java</artifactId>
|
||||
<version>${opencv.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.thirdparty.frc2020.opencv</groupId>
|
||||
<artifactId>opencv-jni</artifactId>
|
||||
<version>${opencv.version}</version>
|
||||
<classifier>linuxaarch64bionic</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.thirdparty.frc2020.opencv</groupId>
|
||||
<artifactId>opencv-jni</artifactId>
|
||||
<version>${opencv.version}</version>
|
||||
<classifier>linuxraspbian</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.thirdparty.frc2020.opencv</groupId>
|
||||
<artifactId>opencv-jni</artifactId>
|
||||
<version>${opencv.version}</version>
|
||||
<classifier>linuxx86-64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.thirdparty.frc2020.opencv</groupId>
|
||||
<artifactId>opencv-jni</artifactId>
|
||||
<version>${opencv.version}</version>
|
||||
<classifier>osxx86-64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.thirdparty.frc2020.opencv</groupId>
|
||||
<artifactId>opencv-jni</artifactId>
|
||||
<version>${opencv.version}</version>
|
||||
<classifier>windowsx86-64</classifier>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
2
chameleon-server/settings.gradle
Normal file
2
chameleon-server/settings.gradle
Normal file
@@ -0,0 +1,2 @@
|
||||
rootProject.name = 'ChameleonVision'
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
package com.chameleonvision;
|
||||
|
||||
public class Debug {
|
||||
private Debug() {}
|
||||
|
||||
private static boolean isTestMode() {
|
||||
return Main.testMode;
|
||||
}
|
||||
|
||||
public static void printInfo(String infoMessage) {
|
||||
if (isTestMode()) {
|
||||
System.out.println(infoMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public static void printInfo(String smallInfo, String largeInfo) {
|
||||
System.out.println(isTestMode() ? String.format("%s - %s" , smallInfo, largeInfo) : smallInfo);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.chameleonvision.Exceptions;
|
||||
|
||||
public class DuplicatedKeyException extends Exception{
|
||||
public DuplicatedKeyException(String message){
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
package com.chameleonvision;
|
||||
|
||||
import com.chameleonvision.config.ConfigManager;
|
||||
import com.chameleonvision.network.NetworkManager;
|
||||
import com.chameleonvision.networktables.NetworkTablesManager;
|
||||
import com.chameleonvision.scripting.ScriptEventType;
|
||||
import com.chameleonvision.scripting.ScriptManager;
|
||||
import com.chameleonvision.util.Platform;
|
||||
import com.chameleonvision.util.Utilities;
|
||||
import com.chameleonvision.vision.VisionManager;
|
||||
import com.chameleonvision.web.Server;
|
||||
import edu.wpi.cscore.CameraServerCvJNI;
|
||||
import edu.wpi.cscore.CameraServerJNI;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static com.chameleonvision.util.Platform.CurrentPlatform;
|
||||
|
||||
public class Main {
|
||||
|
||||
private static final String NT_SERVERMODE_KEY = "--nt-servermode"; // no args for this setting
|
||||
private static final String NT_CLIENTMODESERVER_KEY = "--nt-client-server"; // expects String representing an IP address (hostnames will be rejected!)
|
||||
private static final String NETWORK_MANAGE_KEY = "--unmanage-network"; // no args for this setting
|
||||
private static final String IGNORE_ROOT_KEY = "--ignore-root"; // no args for this setting
|
||||
private static final String TEST_MODE_KEY = "--cv-development";
|
||||
private static final String UI_PORT_KEY = "--ui-port";
|
||||
|
||||
private static final int DEFAULT_PORT = 5800;
|
||||
|
||||
private static boolean ntServerMode = false;
|
||||
private static boolean manageNetwork = true;
|
||||
private static boolean ignoreRoot = false;
|
||||
private static String ntClientModeServer = null;
|
||||
public static boolean testMode = false;
|
||||
public static int uiPort = DEFAULT_PORT;
|
||||
|
||||
private static void handleArgs(String[] args) {
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
var key = args[i].toLowerCase();
|
||||
String value = null;
|
||||
|
||||
// this switch handles arguments with a value. Add any settings with a value here.
|
||||
switch (key) {
|
||||
case NT_CLIENTMODESERVER_KEY:
|
||||
var potentialValue = args[i + 1];
|
||||
// ensures this "value" isnt null, blank, nor another argument
|
||||
if (potentialValue != null && !potentialValue.isBlank() && !potentialValue.startsWith("-") & !potentialValue.startsWith("--")) {
|
||||
value = potentialValue.toLowerCase();
|
||||
}
|
||||
i++; // increment to skip an 'arg' next go-around of for loop, as that would be this value
|
||||
break;
|
||||
case UI_PORT_KEY:
|
||||
var potentialPort = args[i + 1];
|
||||
if (potentialPort != null && !potentialPort.isBlank() && !potentialPort.startsWith("-") & !potentialPort.startsWith("--")) {
|
||||
value = potentialPort;
|
||||
}
|
||||
i++;
|
||||
break;
|
||||
case NT_SERVERMODE_KEY:
|
||||
case NETWORK_MANAGE_KEY:
|
||||
case IGNORE_ROOT_KEY:
|
||||
case TEST_MODE_KEY:
|
||||
// nothing
|
||||
break;
|
||||
}
|
||||
|
||||
// this switch actually handles the arguments.
|
||||
switch (key) {
|
||||
case NT_SERVERMODE_KEY:
|
||||
ntServerMode = true;
|
||||
break;
|
||||
case NT_CLIENTMODESERVER_KEY:
|
||||
if (value != null) {
|
||||
if (value.equals("localhost")) {
|
||||
ntClientModeServer = "127.0.0.1";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Utilities.isValidIPV4(value)) {
|
||||
ntClientModeServer = value;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
System.err.println("Argument for NT Server Host was invalid, defaulting to team number host");
|
||||
break;
|
||||
case NETWORK_MANAGE_KEY:
|
||||
manageNetwork = false;
|
||||
break;
|
||||
case IGNORE_ROOT_KEY:
|
||||
ignoreRoot = true;
|
||||
break;
|
||||
case TEST_MODE_KEY:
|
||||
testMode = true;
|
||||
break;
|
||||
case UI_PORT_KEY:
|
||||
if (value != null) {
|
||||
try {
|
||||
uiPort = Integer.parseInt(value);
|
||||
} catch (NumberFormatException e){
|
||||
System.err.println("ui Port was not a valid number using port 5800");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> ScriptManager.queueEvent(ScriptEventType.kProgramExit)));
|
||||
|
||||
if (CurrentPlatform.equals(Platform.UNSUPPORTED)) {
|
||||
System.err.printf("Sorry, this platform is not supported. Give these details to the developers.\n%s\n", CurrentPlatform.toString());
|
||||
return;
|
||||
} else {
|
||||
System.out.printf("Starting Chameleon Vision on platform %s\n", CurrentPlatform.toString());
|
||||
}
|
||||
|
||||
handleArgs(args);
|
||||
|
||||
if (!CurrentPlatform.isRoot()) {
|
||||
if (ignoreRoot) {
|
||||
// manageNetwork = false;
|
||||
System.out.println("Ignoring root, network will not be managed!");
|
||||
} else {
|
||||
System.err.println("This program must be run as root!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to load the JNI Libraries
|
||||
System.out.println("Loading CameraServer...");
|
||||
try {
|
||||
CameraServerJNI.forceLoad();
|
||||
CameraServerCvJNI.forceLoad();
|
||||
} catch (UnsatisfiedLinkError | IOException e) {
|
||||
if (CurrentPlatform.isWindows()) {
|
||||
System.err.println("Try to download the VC++ Redistributable, https://aka.ms/vs/16/release/vc_redist.x64.exe");
|
||||
}
|
||||
throw new RuntimeException("Failed to load JNI Libraries!");
|
||||
}
|
||||
|
||||
System.out.println("Checking Settings...");
|
||||
ConfigManager.initializeSettings();
|
||||
|
||||
if (!CurrentPlatform.isWindows()) {
|
||||
System.out.println("Initializing Script Manager...");
|
||||
ScriptManager.initialize();
|
||||
} else {
|
||||
System.out.println("Scripts not yet supported on Windows. ScriptEvents will be ignored.");
|
||||
}
|
||||
|
||||
NetworkManager.initialize(manageNetwork);
|
||||
|
||||
if (ntServerMode) {
|
||||
NetworkTablesManager.setServerMode();
|
||||
} else {
|
||||
NetworkTablesManager.setClientMode(ntClientModeServer);
|
||||
}
|
||||
|
||||
ScriptManager.queueEvent(ScriptEventType.kProgramInit);
|
||||
|
||||
boolean visionSourcesOk = VisionManager.initializeSources();
|
||||
if (!visionSourcesOk) {
|
||||
System.err.println("No cameras connected!");
|
||||
return;
|
||||
}
|
||||
|
||||
boolean visionProcessesOk = VisionManager.initializeProcesses();
|
||||
if (!visionProcessesOk) {
|
||||
System.err.println("Failed to initialize vision processes!");
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println("Starting vision processes...");
|
||||
VisionManager.startProcesses();
|
||||
|
||||
System.out.printf("Starting Web server at port %d\n", uiPort);
|
||||
Server.main(uiPort);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.chameleonvision.common.configuration;
|
||||
|
||||
public class ConfigFile {
|
||||
|
||||
/**
|
||||
* Represents a config file at a fixed path
|
||||
* @param path Path to config file
|
||||
*/
|
||||
public ConfigFile(String path) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.chameleonvision.common.configuration;
|
||||
|
||||
public class ConfigFolder {
|
||||
|
||||
/**
|
||||
* Represents a folder of config files
|
||||
* @param path path to config file
|
||||
*/
|
||||
public ConfigFolder(String path) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.chameleonvision.common.configuration;
|
||||
|
||||
import com.chameleonvision.common.server.configuration.MainConfig;
|
||||
|
||||
public class ConfigManager {
|
||||
|
||||
private final ConfigFolder rootFolder;
|
||||
final MainConfig mainConfig;
|
||||
|
||||
protected ConfigManager() {
|
||||
|
||||
rootFolder = new ConfigFolder("");
|
||||
|
||||
mainConfig = MainConfig.getInstance();
|
||||
}
|
||||
|
||||
private static class SingletonHolder {
|
||||
private static final ConfigManager INSTANCE = new ConfigManager();
|
||||
}
|
||||
|
||||
public static ConfigManager getInstance() {
|
||||
return SingletonHolder.INSTANCE;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.chameleonvision.network;
|
||||
package com.chameleonvision.common.network;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
@@ -44,7 +44,7 @@ public class LinuxNetworking extends SysNetworking {
|
||||
}
|
||||
|
||||
} else {
|
||||
System.err.println("dhcpcd5 is not installed cant set ip");
|
||||
System.err.println("dhcpcd5 is not installed, unable to set IP.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -52,9 +52,8 @@ public class LinuxNetworking extends SysNetworking {
|
||||
|
||||
@Override
|
||||
public boolean setHostname(String newHostname) {
|
||||
String[] setHostnameArgs = {"set-hostname", newHostname};
|
||||
try {
|
||||
var setHostnameRetCode = shell.execute("hostnamectl", setHostnameArgs);
|
||||
var setHostnameRetCode = shell.execute("hostnamectl", "set-hostname", newHostname);
|
||||
return setHostnameRetCode == 0;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@@ -70,7 +69,7 @@ public class LinuxNetworking extends SysNetworking {
|
||||
List<String> lines = FileUtils.readLines(dhcpConf, StandardCharsets.UTF_8);
|
||||
lines.add("interface " + networkInterface.name);
|
||||
InetAddress iNetMask = InetAddress.getByName(netmask);
|
||||
int prefix = NetmaskToCIDR.convertNetmaskToCIDR(iNetMask);
|
||||
int prefix = convertNetmaskToCIDR(iNetMask);
|
||||
lines.add("static ip_address=" + ipAddress + "/" + prefix);
|
||||
lines.add("static routers=" + gateway);
|
||||
FileUtils.writeLines(dhcpConf, lines);
|
||||
@@ -81,7 +80,6 @@ public class LinuxNetworking extends SysNetworking {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<java.net.NetworkInterface> getNetworkInterfaces() throws SocketException {
|
||||
List<java.net.NetworkInterface> netInterfaces;
|
||||
@@ -1,22 +1,22 @@
|
||||
package com.chameleonvision.network;
|
||||
package com.chameleonvision.common.network;
|
||||
|
||||
import java.net.InterfaceAddress;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public class NetworkInterface {
|
||||
public final String name;
|
||||
public final String displayName;
|
||||
public final String IPAddress;
|
||||
public final String Netmask;
|
||||
public final String Gateway;
|
||||
public final String Broadcast;
|
||||
public final String name;
|
||||
public final String displayName;
|
||||
public final String IPAddress;
|
||||
public final String Netmask;
|
||||
public final String Gateway;
|
||||
public final String Broadcast;
|
||||
|
||||
public NetworkInterface(java.net.NetworkInterface inetface, InterfaceAddress ifaceAddress) {
|
||||
name = inetface.getName();
|
||||
displayName = inetface.getDisplayName();
|
||||
public NetworkInterface(java.net.NetworkInterface inetface, InterfaceAddress ifaceAddress) {
|
||||
name = inetface.getName();
|
||||
displayName = inetface.getDisplayName();
|
||||
|
||||
var inetAddress = ifaceAddress.getAddress();
|
||||
IPAddress = inetAddress.getHostAddress();
|
||||
var inetAddress = ifaceAddress.getAddress();
|
||||
IPAddress = inetAddress.getHostAddress();
|
||||
Netmask = getIPv4LocalNetMask(ifaceAddress);
|
||||
|
||||
// TODO: (low) hack to "get" gateway, this is gross and bad, pls fix
|
||||
@@ -25,10 +25,10 @@ public class NetworkInterface {
|
||||
Gateway = String.join(".", splitIPAddr);
|
||||
splitIPAddr[3] = "255";
|
||||
Broadcast = String.join(".", splitIPAddr);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getIPv4LocalNetMask(InterfaceAddress interfaceAddress) {
|
||||
var netPrefix = interfaceAddress.getNetworkPrefixLength();
|
||||
var netPrefix = interfaceAddress.getNetworkPrefixLength();
|
||||
try {
|
||||
// Since this is for IPv4, it's 32 bits, so set the sign value of
|
||||
// the int to "negative"...
|
||||
@@ -41,7 +41,7 @@ public class NetworkInterface {
|
||||
}
|
||||
// Transform the resulting value in xxx.xxx.xxx.xxx format, like if
|
||||
/// it was a standard address...
|
||||
// Return the address thus created...
|
||||
// Return the address thus created...
|
||||
return ((shiftby >> 24) & 255) + "." + ((shiftby >> 16) & 255) + "." + ((shiftby >> 8) & 255) + "." + (shiftby & 255);
|
||||
// return InetAddress.getByName(maskString);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.chameleonvision.common.network;
|
||||
|
||||
public class NetworkManager {
|
||||
private NetworkManager() {}
|
||||
|
||||
private static class SingletonHolder {
|
||||
private static final NetworkManager INSTANCE = new NetworkManager();
|
||||
}
|
||||
|
||||
public static NetworkManager getInstance() {
|
||||
return SingletonHolder.INSTANCE;
|
||||
}
|
||||
|
||||
private boolean isManaged = false;
|
||||
|
||||
public void initialize(boolean shouldManage) {
|
||||
isManaged = shouldManage;
|
||||
if (!isManaged) {
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.chameleonvision.common.network;
|
||||
|
||||
public enum NetworkMode {
|
||||
DHCP,
|
||||
STATIC
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.chameleonvision.common.network;
|
||||
|
||||
import com.chameleonvision.common.server.util.ShellExec;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.SocketException;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class SysNetworking {
|
||||
NetworkInterface networkInterface;
|
||||
ShellExec shell = new ShellExec(true, true);
|
||||
|
||||
private String hostname = getHostname();
|
||||
|
||||
public String getHostname() {
|
||||
if (hostname == null) {
|
||||
try {
|
||||
var retCode = shell.execute("hostname", null, true);
|
||||
if (retCode == 0) {
|
||||
while(!shell.isOutputCompleted()) {}
|
||||
return shell.getOutput();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
} else return hostname;
|
||||
}
|
||||
|
||||
//code belongs to https://stackoverflow.com/questions/19531411/calculate-cidr-from-a-given-netmask-java
|
||||
public static int convertNetmaskToCIDR(InetAddress netmask) {
|
||||
|
||||
byte[] netmaskBytes = netmask.getAddress();
|
||||
int cidr = 0;
|
||||
boolean zero = false;
|
||||
for (byte b : netmaskBytes) {
|
||||
int mask = 0x80;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int result = b & mask;
|
||||
if (result == 0) {
|
||||
zero = true;
|
||||
} else if (zero) {
|
||||
throw new IllegalArgumentException("Invalid netmask.");
|
||||
} else {
|
||||
cidr++;
|
||||
}
|
||||
mask >>>= 1;
|
||||
}
|
||||
}
|
||||
return cidr;
|
||||
}
|
||||
|
||||
public void setNetworkInterface(NetworkInterface networkInterface) {
|
||||
this.networkInterface = networkInterface;
|
||||
}
|
||||
public abstract boolean setDHCP();
|
||||
public abstract boolean setHostname(String hostname);
|
||||
public abstract boolean setStatic(String ipAddress, String netmask, String gateway);
|
||||
public abstract List<java.net.NetworkInterface> getNetworkInterfaces() throws SocketException;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.chameleonvision.common.server.configuration;
|
||||
|
||||
import com.chameleonvision.common.configuration.ConfigFile;
|
||||
|
||||
public class MainConfig extends ConfigFile {
|
||||
|
||||
public int teamNumber = 0;
|
||||
public boolean ntServer = false;
|
||||
|
||||
private MainConfig() {
|
||||
super("general");
|
||||
}
|
||||
|
||||
private static class SingletonHolder {
|
||||
private static final MainConfig INSTANCE = new MainConfig();
|
||||
}
|
||||
|
||||
public static MainConfig getInstance() {
|
||||
return SingletonHolder.INSTANCE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.chameleonvision.common.server.configuration;
|
||||
|
||||
import com.chameleonvision.common.configuration.ConfigFile;
|
||||
import com.chameleonvision.common.network.NetworkMode;
|
||||
|
||||
public class NetworkConfig extends ConfigFile {
|
||||
|
||||
public NetworkMode networkMode = NetworkMode.DHCP;
|
||||
public String ip = "";
|
||||
public String hostname = "chameleon-vision";
|
||||
|
||||
private NetworkConfig() {
|
||||
super("network");
|
||||
}
|
||||
|
||||
private static class SingletonHolder {
|
||||
private static final NetworkConfig INSTANCE = new NetworkConfig();
|
||||
}
|
||||
|
||||
public static NetworkConfig getInstance() {
|
||||
return SingletonHolder.INSTANCE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.chameleonvision.common.server.util;
|
||||
|
||||
import edu.wpi.first.wpiutil.RuntimeDetector;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public enum Platform {
|
||||
// WPILib Supported (JNI)
|
||||
WINDOWS_32("Windows x32"),
|
||||
WINDOWS_64("Windows x64"),
|
||||
LINUX_64("Linux x64"),
|
||||
LINUX_RASPBIAN("Linux Raspbian"), // Raspberry Pi 3/4
|
||||
LINUX_AARCH64BIONIC("Linux AARCH64 Bionic"), // Jetson Nano, Jetson TX2
|
||||
MACOS_64("Mac OS x64"),
|
||||
|
||||
// ChameleonVision Supported (Manual install)
|
||||
LINUX_ARM32("Linux ARM32"), // ODROID XU4, C1+
|
||||
LINUX_ARM64("Linux ARM64"), // ODROID C2, N2
|
||||
|
||||
// Completely unsupported
|
||||
UNSUPPORTED("Unsupported Platform");
|
||||
|
||||
public final String value;
|
||||
public final boolean isRoot = checkForRoot();
|
||||
|
||||
Platform(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
private static final String OS_NAME = System.getProperty("os.name");
|
||||
private static final String OS_ARCH = System.getProperty("os.arch");
|
||||
public static final Platform CurrentPlatform = getCurrentPlatform();
|
||||
|
||||
private static String UnknownPlatformString = String.format("Unknown Platform. OS: %s, Architecture: %s", OS_NAME, OS_ARCH);
|
||||
|
||||
public boolean isWindows() {
|
||||
return this == WINDOWS_64 || this == WINDOWS_32;
|
||||
}
|
||||
|
||||
public boolean isLinux() {
|
||||
return this == LINUX_64 || this == LINUX_RASPBIAN || this == LINUX_ARM64;
|
||||
}
|
||||
|
||||
public boolean isMac() {
|
||||
return this == MACOS_64;
|
||||
}
|
||||
|
||||
public static boolean isRaspberryPi() {
|
||||
return CurrentPlatform.equals(LINUX_RASPBIAN);
|
||||
}
|
||||
|
||||
private static ShellExec shell = new ShellExec(true, false);
|
||||
|
||||
@SuppressWarnings("StatementWithEmptyBody")
|
||||
private boolean checkForRoot() {
|
||||
if (isLinux() || isMac()) {
|
||||
try {
|
||||
shell.execute("id", null, true, "-u");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// TODO: better way to do this?
|
||||
while (!shell.isOutputCompleted()) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
if (shell.getExitCode() == 0) {
|
||||
return shell.getOutput().split("\n")[0].equals("0");
|
||||
}
|
||||
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Platform getCurrentPlatform() {
|
||||
if (RuntimeDetector.isWindows()) {
|
||||
if (RuntimeDetector.is32BitIntel()) return WINDOWS_32;
|
||||
if (RuntimeDetector.is64BitIntel()) return WINDOWS_64;
|
||||
}
|
||||
|
||||
if (RuntimeDetector.isMac()) {
|
||||
if (RuntimeDetector.is32BitIntel()) return UNSUPPORTED;
|
||||
if (RuntimeDetector.is64BitIntel()) return MACOS_64;
|
||||
}
|
||||
|
||||
if (RuntimeDetector.isLinux()) {
|
||||
if (RuntimeDetector.is32BitIntel()) return UNSUPPORTED;
|
||||
if (RuntimeDetector.is64BitIntel()) return LINUX_64;
|
||||
if (RuntimeDetector.isRaspbian()) return LINUX_RASPBIAN;
|
||||
|
||||
}
|
||||
|
||||
System.out.println(UnknownPlatformString);
|
||||
return Platform.UNSUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
public String toString() {
|
||||
if (this.equals(UNSUPPORTED)) {
|
||||
return UnknownPlatformString;
|
||||
} else {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
package com.chameleonvision.util;
|
||||
package com.chameleonvision.common.server.util;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* Execute external process and optionally read output buffer.
|
||||
*/
|
||||
@SuppressWarnings({"unused", "ConstantConditions"})
|
||||
public class ShellExec {
|
||||
private int exitCode;
|
||||
private boolean readOutput, readError;
|
||||
@@ -23,7 +24,7 @@ public class ShellExec {
|
||||
* Execute a bash command. We can handle complex bash commands including
|
||||
* multiple executions (; | && ||), quotes, expansions ($), escapes (\), e.g.:
|
||||
* "cd /abc/def; mv ghi 'older ghi '$(whoami)"
|
||||
* @param command
|
||||
* @param command Bash command to execute
|
||||
* @return true if bash got started, but your command may have failed.
|
||||
*/
|
||||
public int executeBashCommand(String command) throws IOException {
|
||||
@@ -41,19 +42,7 @@ public class ShellExec {
|
||||
|
||||
// Consume streams, older jvm's had a memory leak if streams were not read,
|
||||
// some other jvm+OS combinations may block unless streams are consumed.
|
||||
errorGobbler = new StreamGobbler(process.getErrorStream(), readError);
|
||||
outputGobbler = new StreamGobbler(process.getInputStream(), readOutput);
|
||||
errorGobbler.start();
|
||||
outputGobbler.start();
|
||||
|
||||
exitCode = 0;
|
||||
if (wait) {
|
||||
try {
|
||||
process.waitFor();
|
||||
exitCode = process.exitValue();
|
||||
} catch (InterruptedException ignored) { }
|
||||
}
|
||||
return exitCode;
|
||||
return doProcess(wait, process);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,7 +81,11 @@ public class ShellExec {
|
||||
|
||||
// Consume streams, older jvm's had a memory leak if streams were not read,
|
||||
// some other jvm+OS combinations may block unless streams are consumed.
|
||||
errorGobbler = new StreamGobbler(process.getErrorStream(), readError);
|
||||
return doProcess(wait, process);
|
||||
}
|
||||
|
||||
private int doProcess(boolean wait, Process process) {
|
||||
errorGobbler = new StreamGobbler(process.getErrorStream(), readError);
|
||||
outputGobbler = new StreamGobbler(process.getInputStream(), readOutput);
|
||||
errorGobbler.start();
|
||||
outputGobbler.start();
|
||||
@@ -169,7 +162,7 @@ public class ShellExec {
|
||||
/**
|
||||
* Get inputstream buffer or null if stream
|
||||
* was not consumed.
|
||||
* @return
|
||||
* @return Output stream
|
||||
*/
|
||||
public String getOutput() {
|
||||
return (output != null ? output.toString() : null);
|
||||
@@ -177,7 +170,7 @@ public class ShellExec {
|
||||
|
||||
/**
|
||||
* Is input stream completed.
|
||||
* @return
|
||||
* @return if input stream is completed
|
||||
*/
|
||||
public boolean isCompleted() {
|
||||
return completed;
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.chameleonvision.common.server.util;
|
||||
|
||||
import org.apache.commons.exec.*;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
// TODO: Finish me!
|
||||
@SuppressWarnings({"FieldCanBeLocal", "unused"})
|
||||
public class ShellExecutor {
|
||||
|
||||
private final Executor executor;
|
||||
private final ExecuteWatchdog watchdog;
|
||||
private final DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
|
||||
private final OutputStream stdOutStream = new ByteArrayOutputStream();
|
||||
private final OutputStream stdErrStream = new ByteArrayOutputStream();
|
||||
private final boolean block;
|
||||
|
||||
public ShellExecutor(String command, boolean block, int timeoutMillis, String... args) {
|
||||
this.block = block;
|
||||
|
||||
CommandLine cmdLine = new CommandLine(command);
|
||||
cmdLine.addArguments(args);
|
||||
|
||||
watchdog = new ExecuteWatchdog(timeoutMillis);
|
||||
executor = new DefaultExecutor();
|
||||
executor.setWatchdog(watchdog);
|
||||
executor.setStreamHandler(new PumpStreamHandler(stdOutStream, stdErrStream));
|
||||
}
|
||||
|
||||
// public int execute() {
|
||||
// if ()
|
||||
// }
|
||||
|
||||
public String getStdOut() {
|
||||
if (!watchdog.isWatching()) {
|
||||
return executor.toString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getStdErr() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.chameleonvision.common.vision.base.camera;
|
||||
|
||||
import com.chameleonvision.common.vision.base.capture.USBFrameProvider;
|
||||
|
||||
public class USBCamera extends USBFrameProvider {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.chameleonvision.common.vision.base.capture;
|
||||
|
||||
import com.chameleonvision.common.vision.base.frame.Frame;
|
||||
import com.chameleonvision.common.vision.base.frame.FrameProvider;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
|
||||
public class FileFrameProvider implements FrameProvider {
|
||||
@Override
|
||||
public Frame getFrame() {
|
||||
throw new NotImplementedException("");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.chameleonvision.common.vision.base.capture;
|
||||
|
||||
import com.chameleonvision.common.vision.base.frame.Frame;
|
||||
import com.chameleonvision.common.vision.base.frame.FrameProvider;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
|
||||
public class NetworkFrameProvider implements FrameProvider {
|
||||
@Override
|
||||
public Frame getFrame() {
|
||||
throw new NotImplementedException("");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.chameleonvision.common.vision.base.capture;
|
||||
|
||||
import com.chameleonvision.common.vision.base.frame.Frame;
|
||||
import com.chameleonvision.common.vision.base.frame.FrameProvider;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
|
||||
public class USBFrameProvider implements FrameProvider {
|
||||
@Override
|
||||
public Frame getFrame() {
|
||||
throw new NotImplementedException("");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.chameleonvision.common.vision.base.frame;
|
||||
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Size;
|
||||
|
||||
public class Frame {
|
||||
public long timestamp;
|
||||
public Mat image;
|
||||
public Size imageSize;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.chameleonvision.common.vision.base.frame;
|
||||
|
||||
public interface FrameConsumer {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.chameleonvision.common.vision.base.frame;
|
||||
|
||||
public interface FrameProvider {
|
||||
Frame getFrame();
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.chameleonvision.common.vision.base.pipeline;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Defines a pipe. A pipe is a single step in a pipeline.
|
||||
* This class is to be extended, never used on its own.
|
||||
*
|
||||
* @param <I> Input type for the pipe
|
||||
* @param <O> Output type for the pipe
|
||||
*/
|
||||
public abstract class Pipe<I, O> implements Function<I, PipeResult<O>> {
|
||||
|
||||
private PipeResult<O> result = new PipeResult<>();
|
||||
|
||||
/**
|
||||
* Runs the process for the pipe.
|
||||
*
|
||||
* @param in Input for pipe processing
|
||||
* @return Result of processing
|
||||
*/
|
||||
protected abstract O process(I in);
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param in Input for pipe processing
|
||||
* @return Result of processing
|
||||
*/
|
||||
@Override
|
||||
public PipeResult<O> apply(I in) {
|
||||
long pipeStartNanos = System.nanoTime();
|
||||
result.result = process(in);
|
||||
result.nanosElapsed = System.nanoTime() - pipeStartNanos;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.chameleonvision.common.vision.base.pipeline;
|
||||
|
||||
public class PipeResult<O> {
|
||||
O result;
|
||||
long nanosElapsed;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.chameleonvision.common.vision.base.pipeline.pipe;
|
||||
|
||||
import com.chameleonvision.common.vision.base.pipeline.Pipe;
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
public class ResizeImagePipe extends Pipe<Mat, Mat> {
|
||||
|
||||
public ResizeImagePipe() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mat process(Mat in) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.chameleonvision.common.vision.base.pipeline.pipe.javacv;
|
||||
|
||||
import com.chameleonvision.common.vision.base.pipeline.Pipe;
|
||||
import org.bytedeco.opencv.opencv_core.GpuMat;
|
||||
|
||||
public class GPUResizeImagePipe extends Pipe<GpuMat, GpuMat> {
|
||||
|
||||
public GPUResizeImagePipe() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GpuMat process(GpuMat in) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.chameleonvision.common.vision.base.stream;
|
||||
|
||||
public class AsyncMjpgStreamer extends MjpgStreamer {
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.chameleonvision.common.vision.base.stream;
|
||||
|
||||
public class MjpgStreamer {
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.chameleonvision.common.vision.base.stream;
|
||||
|
||||
public interface Streamer {
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.chameleonvision.common.vision.opencv;
|
||||
|
||||
public class OpenCVWrapper {
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.chameleonvision.common.vision.target;
|
||||
|
||||
public class KnownTarget {
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.chameleonvision.common.vision.target;
|
||||
|
||||
public class TrackedTarget {
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package com.chameleonvision.config;
|
||||
|
||||
import com.chameleonvision.vision.pipeline.CVPipelineSettings;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface CVPipelineSettingsList extends List<CVPipelineSettings> {
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package com.chameleonvision.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreType;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.MatOfDouble;
|
||||
import org.opencv.core.Size;
|
||||
|
||||
/**
|
||||
* A class that holds a camera matrix and distortion coefficients for a given resolution
|
||||
*/
|
||||
public class CameraCalibrationConfig {
|
||||
@JsonProperty("resolution") public final Size resolution;
|
||||
@JsonProperty("cameraMatrix") public final JsonMat cameraMatrix;
|
||||
@JsonProperty("distortionCoeffs") public final JsonMat distortionCoeffs;
|
||||
@JsonProperty("squareSize") public final double squareSize;
|
||||
|
||||
@JsonCreator
|
||||
public CameraCalibrationConfig(
|
||||
@JsonProperty("resolution") Size resolution,
|
||||
@JsonProperty("cameraMatrix") JsonMat cameraMatrix,
|
||||
@JsonProperty("distortionCoeffs") JsonMat distortionCoeffs,
|
||||
@JsonProperty("squareSize") double squareSize) {
|
||||
this.resolution = resolution;
|
||||
this.cameraMatrix = cameraMatrix;
|
||||
this.distortionCoeffs = distortionCoeffs;
|
||||
this.squareSize = squareSize;
|
||||
}
|
||||
|
||||
public CameraCalibrationConfig(Size resolution, Mat cameraMatrix, Mat distortionCoeffs, double squareSize) {
|
||||
this.resolution = resolution;
|
||||
this.cameraMatrix = JsonMat.fromMat(cameraMatrix);
|
||||
this.distortionCoeffs = JsonMat.fromMat(distortionCoeffs);
|
||||
this.squareSize = squareSize;
|
||||
}
|
||||
|
||||
@JsonIgnoreType
|
||||
public static class UICameraCalibrationConfig {
|
||||
public final int width;
|
||||
public final int height;
|
||||
public final double[] cameraMatrix;
|
||||
public final double[] distortionCoeffs;
|
||||
|
||||
public UICameraCalibrationConfig(CameraCalibrationConfig config) {
|
||||
width = (int) config.resolution.width;
|
||||
height = (int) config.resolution.height;
|
||||
cameraMatrix = config.cameraMatrix.data;
|
||||
distortionCoeffs = config.distortionCoeffs.data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public Mat getCameraMatrixAsMat() {
|
||||
return cameraMatrix.toMat();
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public MatOfDouble getDistortionCoeffsAsMat() {
|
||||
return new MatOfDouble(distortionCoeffs.toMat());
|
||||
}
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
package com.chameleonvision.config;
|
||||
|
||||
import com.chameleonvision.util.FileHelper;
|
||||
import com.chameleonvision.util.JacksonHelper;
|
||||
import com.chameleonvision.vision.pipeline.CVPipelineSettings;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class CameraConfig {
|
||||
|
||||
private static final Path camerasConfigFolderPath = Path.of(ConfigManager.SettingsPath.toString(), "cameras");
|
||||
|
||||
private final CameraJsonConfig preliminaryConfig;
|
||||
private final Path configFolderPath;
|
||||
private final Path configPath;
|
||||
private final Path driverModePath;
|
||||
private final Path calibrationPath;
|
||||
final Path pipelineFolderPath;
|
||||
|
||||
public final PipelineConfig pipelineConfig;
|
||||
|
||||
CameraConfig(CameraJsonConfig config) {
|
||||
preliminaryConfig = config;
|
||||
String cameraConfigName = preliminaryConfig.name.replace(' ', '_');
|
||||
pipelineConfig = new PipelineConfig(this);
|
||||
|
||||
configFolderPath = Path.of(camerasConfigFolderPath.toString(), cameraConfigName);
|
||||
configPath = Path.of(configFolderPath.toString(), "camera.json");
|
||||
driverModePath = Path.of(configFolderPath.toString(), "drivermode.json");
|
||||
calibrationPath = Path.of(configFolderPath.toString(), "calibration.json");
|
||||
pipelineFolderPath = Paths.get(configFolderPath.toString(), "pipelines");
|
||||
}
|
||||
|
||||
public FullCameraConfiguration load() {
|
||||
checkFolder();
|
||||
checkConfig();
|
||||
checkDriverMode();
|
||||
checkCalibration();
|
||||
pipelineConfig.check();
|
||||
|
||||
return new FullCameraConfiguration(loadConfig(), pipelineConfig.load(), loadDriverMode(), loadCalibration(), this);
|
||||
}
|
||||
|
||||
private CameraJsonConfig loadConfig() {
|
||||
CameraJsonConfig config = preliminaryConfig;
|
||||
try {
|
||||
config = JacksonHelper.deserialize(configPath, CameraJsonConfig.class);
|
||||
} catch (IOException e) {
|
||||
System.err.printf("Failed to load camera config: %s - using default.\n", configPath.toString());
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
private CVPipelineSettings loadDriverMode() {
|
||||
CVPipelineSettings driverMode = new CVPipelineSettings();
|
||||
try {
|
||||
driverMode = JacksonHelper.deserialize(driverModePath, CVPipelineSettings.class);
|
||||
} catch (IOException e) {
|
||||
System.err.println("Failed to load camera drivermode: " + driverModePath.toString());
|
||||
}
|
||||
if (driverMode != null) {
|
||||
driverMode.nickname = "DRIVERMODE";
|
||||
driverMode.index = -1;
|
||||
}
|
||||
return driverMode;
|
||||
}
|
||||
|
||||
private List<CameraCalibrationConfig> loadCalibration() {
|
||||
List<CameraCalibrationConfig> calibrations = new ArrayList<>();
|
||||
try {
|
||||
calibrations = List.of(Objects.requireNonNull(JacksonHelper.deserialize(calibrationPath, CameraCalibrationConfig[].class)));
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to load camera calibration: " + driverModePath.toString());
|
||||
}
|
||||
return calibrations;
|
||||
}
|
||||
|
||||
void saveConfig(CameraJsonConfig config) {
|
||||
try {
|
||||
JacksonHelper.serializer(configPath, config, true);
|
||||
FileHelper.setFilePerms(configPath);
|
||||
} catch (IOException e) {
|
||||
System.err.println("Failed to save camera config file: " + configPath.toString());
|
||||
}
|
||||
}
|
||||
|
||||
void savePipelines(List<CVPipelineSettings> pipelines) {
|
||||
pipelineConfig.save(pipelines);
|
||||
}
|
||||
|
||||
public void saveDriverMode(CVPipelineSettings driverMode) {
|
||||
try {
|
||||
JacksonHelper.serializer(driverModePath, driverMode, true);
|
||||
FileHelper.setFilePerms(driverModePath);
|
||||
} catch (IOException e) {
|
||||
System.err.println("Failed to save camera drivermode file: " + driverModePath.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void saveCalibration(List<CameraCalibrationConfig> cal) {
|
||||
CameraCalibrationConfig[] configs = cal.toArray(new CameraCalibrationConfig[0]);
|
||||
try {
|
||||
JacksonHelper.serializer(calibrationPath, configs, true);
|
||||
FileHelper.setFilePerms(calibrationPath);
|
||||
} catch (IOException e) {
|
||||
System.err.println("Failed to save camera calibration file: " + calibrationPath.toString());
|
||||
}
|
||||
}
|
||||
|
||||
void checkFolder() {
|
||||
if (!configFolderExists()) {
|
||||
try {
|
||||
if (!(new File(configFolderPath.toUri()).mkdirs())) {
|
||||
System.err.println("Failed to create camera config folder: " + configFolderPath.toString());
|
||||
}
|
||||
FileHelper.setFilePerms(configFolderPath);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to create camera config folder: " + configFolderPath.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkConfig() {
|
||||
if (!configExists()) {
|
||||
try {
|
||||
JacksonHelper.serializer(configPath, preliminaryConfig, true);
|
||||
FileHelper.setFilePerms(configPath);
|
||||
} catch (IOException e) {
|
||||
System.err.println("Failed to create camera config file: " + configPath.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkDriverMode() {
|
||||
if (!driverModeExists()) {
|
||||
try {
|
||||
CVPipelineSettings newDriverModeSettings = new CVPipelineSettings();
|
||||
newDriverModeSettings.nickname = "DRIVERMODE";
|
||||
JacksonHelper.serializer(driverModePath, newDriverModeSettings, true);
|
||||
FileHelper.setFilePerms(driverModePath);
|
||||
} catch (IOException e) {
|
||||
System.err.println("Failed to create camera drivermode file: " + driverModePath.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkCalibration() {
|
||||
if (!calibrationExists()) {
|
||||
try {
|
||||
List<CameraCalibrationConfig> calibrations = new ArrayList<>();
|
||||
JacksonHelper.serializer(calibrationPath, calibrations.toArray(), true);
|
||||
} catch (IOException e) {
|
||||
System.err.println("Failed to create camera calibration file: " + calibrationPath.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean configFolderExists() {
|
||||
return Files.exists(configFolderPath);
|
||||
}
|
||||
|
||||
private boolean configExists() {
|
||||
return configFolderExists() && Files.exists(configPath);
|
||||
}
|
||||
|
||||
private boolean driverModeExists() {
|
||||
return configFolderExists() && Files.exists(driverModePath);
|
||||
}
|
||||
|
||||
private boolean calibrationExists() {
|
||||
return configFolderExists() && Files.exists(calibrationPath);
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package com.chameleonvision.config;
|
||||
|
||||
import com.chameleonvision.vision.VisionProcess;
|
||||
import com.chameleonvision.vision.camera.USBCaptureProperties;
|
||||
import com.chameleonvision.vision.enums.StreamDivisor;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class CameraJsonConfig {
|
||||
public final double fov;
|
||||
public final String path;
|
||||
public final String name;
|
||||
public final String nickname;
|
||||
public final int videomode;
|
||||
public final double tilt;
|
||||
public final StreamDivisor streamDivisor;
|
||||
|
||||
@JsonCreator
|
||||
public CameraJsonConfig(
|
||||
@JsonProperty("fov") double fov,
|
||||
@JsonProperty("path") String path,
|
||||
@JsonProperty("name") String name,
|
||||
@JsonProperty("nickname") String nickname,
|
||||
@JsonProperty("videomode") int videomode,
|
||||
@JsonProperty("streamDivisor") StreamDivisor streamDivisor,
|
||||
@JsonProperty("tilt") double tilt) {
|
||||
this.fov = fov;
|
||||
this.path = path;
|
||||
this.name = name;
|
||||
this.nickname = nickname;
|
||||
this.videomode = videomode;
|
||||
this.streamDivisor = streamDivisor;
|
||||
this.tilt = tilt;
|
||||
}
|
||||
|
||||
public CameraJsonConfig(String path, String name) {
|
||||
this.fov = USBCaptureProperties.DEFAULT_FOV;
|
||||
this.path = path;
|
||||
this.name = name;
|
||||
this.nickname = name;
|
||||
this.videomode = 0;
|
||||
this.streamDivisor = StreamDivisor.NONE;
|
||||
this.tilt = 0;
|
||||
}
|
||||
|
||||
public static CameraJsonConfig fromVisionProcess(VisionProcess process) {
|
||||
USBCaptureProperties camProps = process.getCamera().getProperties();
|
||||
int videomode = camProps.getCurrentVideoModeIndex();
|
||||
StreamDivisor streamDivisor = process.cameraStreamer.getDivisor();
|
||||
double tilt = process.getCamera().getProperties().getTilt().getDegrees();
|
||||
return new CameraJsonConfig(camProps.getFOV(), camProps.path, camProps.name, camProps.getNickname(), videomode, streamDivisor, tilt);
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
package com.chameleonvision.config;
|
||||
|
||||
import com.chameleonvision.util.*;
|
||||
import com.chameleonvision.vision.pipeline.CVPipelineSettings;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class ConfigManager {
|
||||
private ConfigManager() {
|
||||
}
|
||||
|
||||
public static final Path SettingsPath = Paths.get(ProgramDirectoryUtilities.getProgramDirectory(), "settings");
|
||||
private static final Path settingsFilePath = Paths.get(SettingsPath.toString(), "settings.json");
|
||||
|
||||
private static final LinkedHashMap<String, CameraConfig> cameraConfigs = new LinkedHashMap<>();
|
||||
|
||||
public static GeneralSettings settings = new GeneralSettings();
|
||||
|
||||
private static boolean settingsFolderExists() {
|
||||
return Files.exists(SettingsPath);
|
||||
}
|
||||
|
||||
private static boolean settingsFileExists() {
|
||||
return settingsFolderExists() && Files.exists(settingsFilePath);
|
||||
}
|
||||
|
||||
private static void checkSettingsFolder() {
|
||||
if (!settingsFolderExists()) {
|
||||
try {
|
||||
if (!(new File(SettingsPath.toUri()).mkdirs())) {
|
||||
System.err.println("Failed to create settings folder: " + SettingsPath.toString());
|
||||
}
|
||||
Files.createDirectory(SettingsPath);
|
||||
if (!Platform.CurrentPlatform.isWindows()) {
|
||||
new ShellExec().executeBashCommand("sudo chmod -R 0777 " + SettingsPath.toString());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (!(e instanceof java.nio.file.FileAlreadyExistsException))
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkSettingsFile() {
|
||||
boolean settingsFileEmpty = settingsFileExists() && new File(settingsFilePath.toString()).length() == 0;
|
||||
if (settingsFileEmpty || !settingsFileExists()) {
|
||||
try {
|
||||
JacksonHelper.serializer(settingsFilePath, settings, true);
|
||||
FileHelper.setFilePerms(settingsFilePath);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
settings = JacksonHelper.deserialize(settingsFilePath, GeneralSettings.class);
|
||||
} catch (IOException e) {
|
||||
System.err.println("Failed to load settings.json, using defaults.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void initializeSettings() {
|
||||
System.out.println("Settings folder: " + SettingsPath.toString());
|
||||
checkSettingsFolder();
|
||||
checkSettingsFile();
|
||||
FileHelper.setAllPerms(SettingsPath);
|
||||
}
|
||||
|
||||
private static void saveSettingsFile() {
|
||||
try {
|
||||
JacksonHelper.serializer(settingsFilePath, settings, true);
|
||||
FileHelper.setFilePerms(settingsFilePath);
|
||||
} catch (IOException e) {
|
||||
System.err.println("Failed to save settings.json!");
|
||||
}
|
||||
}
|
||||
|
||||
public static void saveGeneralSettings() {
|
||||
checkSettingsFolder();
|
||||
saveSettingsFile();
|
||||
}
|
||||
|
||||
public static List<FullCameraConfiguration> initializeCameras(List<CameraJsonConfig> preliminaryConfigs) {
|
||||
List<FullCameraConfiguration> configList = new ArrayList<>();
|
||||
|
||||
checkSettingsFolder();
|
||||
|
||||
// loop over all the camera names and try to create settings folders for it
|
||||
for (CameraJsonConfig preliminaryConfig : preliminaryConfigs) {
|
||||
CameraConfig cameraConfiguration = new CameraConfig(preliminaryConfig);
|
||||
cameraConfigs.put(preliminaryConfig.name, cameraConfiguration);
|
||||
|
||||
FullCameraConfiguration camJsonConfig = cameraConfiguration.load();
|
||||
|
||||
configList.add(camJsonConfig);
|
||||
}
|
||||
|
||||
return configList;
|
||||
}
|
||||
|
||||
public static void saveCameraConfig(String cameraName, CameraJsonConfig config) {
|
||||
var camConf = cameraConfigs.get(cameraName);
|
||||
camConf.saveConfig(config);
|
||||
}
|
||||
|
||||
public static void saveCameraPipelines(String cameraName, List<CVPipelineSettings> pipelines) {
|
||||
var camConf = cameraConfigs.get(cameraName);
|
||||
camConf.savePipelines(pipelines);
|
||||
}
|
||||
|
||||
public static void saveCameraDriverMode(String cameraName, CVPipelineSettings driverMode) {
|
||||
var camConf = cameraConfigs.get(cameraName);
|
||||
camConf.saveDriverMode(driverMode);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package com.chameleonvision.config;
|
||||
|
||||
import com.chameleonvision.vision.pipeline.CVPipelineSettings;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class FullCameraConfiguration {
|
||||
public final CameraJsonConfig cameraConfig;
|
||||
public final List<CVPipelineSettings> pipelines;
|
||||
public final CVPipelineSettings driverMode;
|
||||
public final List<CameraCalibrationConfig> calibration;
|
||||
public final CameraConfig fileConfig;
|
||||
|
||||
FullCameraConfiguration(CameraJsonConfig cameraConfig, List<CVPipelineSettings> pipelines, CVPipelineSettings driverMode, List<CameraCalibrationConfig> calibration, CameraConfig fileConfig) {
|
||||
this.cameraConfig = cameraConfig;
|
||||
this.pipelines = pipelines;
|
||||
this.driverMode = driverMode;
|
||||
this.calibration = calibration;
|
||||
this.fileConfig = fileConfig;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.chameleonvision.config;
|
||||
|
||||
import com.chameleonvision.network.NetworkIPMode;
|
||||
|
||||
public class GeneralSettings {
|
||||
public int teamNumber = 1577;
|
||||
public NetworkIPMode connectionType = NetworkIPMode.DHCP;
|
||||
public String ip = "";
|
||||
public String gateway = "";
|
||||
public String netmask = "";
|
||||
public String hostname = "Chameleon-vision";
|
||||
public String currentCamera = "";
|
||||
public Integer currentPipeline = null;
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package com.chameleonvision.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.opencv.core.CvType;
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
public class JsonMat {
|
||||
public final int rows;
|
||||
public final int cols;
|
||||
public final int type;
|
||||
public final double[] data;
|
||||
|
||||
public JsonMat(int rows, int cols, double[] data) {
|
||||
this(rows, cols, CvType.CV_64FC1, data);
|
||||
}
|
||||
|
||||
public JsonMat(
|
||||
@JsonProperty("rows") int rows,
|
||||
@JsonProperty("cols") int cols,
|
||||
@JsonProperty("type") int type,
|
||||
@JsonProperty("data") double[] data) {
|
||||
this.rows = rows;
|
||||
this.cols = cols;
|
||||
this.type = type;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public Mat toMat() {
|
||||
return toMat(this);
|
||||
}
|
||||
|
||||
private static boolean isCameraMatrixMat(Mat mat) {
|
||||
return mat.type() == CvType.CV_64FC1 && mat.cols() == 3 && mat.rows() == 3;
|
||||
}
|
||||
|
||||
private static boolean isDistortionCoeffsMat(Mat mat) {
|
||||
return mat.type() == CvType.CV_64FC1 && mat.cols() == 5 && mat.rows() == 1;
|
||||
}
|
||||
|
||||
private static boolean isCalibrationMat(Mat mat) {
|
||||
return isDistortionCoeffsMat(mat) || isCameraMatrixMat(mat);
|
||||
}
|
||||
|
||||
public static double[] getDataFromMat(Mat mat) {
|
||||
if (!isCalibrationMat(mat)) return null;
|
||||
|
||||
double[] data = new double[(int)(mat.total()*mat.elemSize())];
|
||||
mat.get(0, 0, data);
|
||||
|
||||
int dataLen = -1;
|
||||
|
||||
if (isCameraMatrixMat(mat)) dataLen = 9;
|
||||
if (isDistortionCoeffsMat(mat)) dataLen = 5;
|
||||
|
||||
// truncate Mat data to correct number data points.
|
||||
return Arrays.copyOfRange(data, 0, dataLen);
|
||||
}
|
||||
|
||||
public static JsonMat fromMat(Mat mat) {
|
||||
if (!isCalibrationMat(mat)) return null;
|
||||
return new JsonMat(mat.rows(), mat.cols(), getDataFromMat(mat));
|
||||
}
|
||||
|
||||
public static Mat toMat(JsonMat jsonMat) {
|
||||
if (jsonMat.type != CvType.CV_64FC1) return null;
|
||||
|
||||
Mat retMat = new Mat(jsonMat.rows, jsonMat.cols, jsonMat.type);
|
||||
retMat.put(0, 0, jsonMat.data);
|
||||
return retMat;
|
||||
}
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
package com.chameleonvision.config;
|
||||
|
||||
import com.chameleonvision.config.serializers.StandardCVPipelineSettingsDeserializer;
|
||||
import com.chameleonvision.config.serializers.StandardCVPipelineSettingsSerializer;
|
||||
import com.chameleonvision.util.FileHelper;
|
||||
import com.chameleonvision.util.JacksonHelper;
|
||||
import com.chameleonvision.vision.pipeline.*;
|
||||
import com.chameleonvision.vision.pipeline.impl.StandardCVPipelineSettings;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PipelineConfig {
|
||||
|
||||
private final CameraConfig cameraConfig;
|
||||
|
||||
/**
|
||||
* Construct a new PipelineConfig
|
||||
*
|
||||
* @param cameraConfig the CameraConfig (parent folder, kinda?)
|
||||
*/
|
||||
PipelineConfig(CameraConfig cameraConfig) {
|
||||
this.cameraConfig = cameraConfig;
|
||||
}
|
||||
|
||||
private void checkFolder() {
|
||||
if (!(new File(cameraConfig.pipelineFolderPath.toUri()).mkdirs())) {
|
||||
if (Files.notExists(cameraConfig.pipelineFolderPath)) {
|
||||
System.err.println("Failed to create pipelines folder.");
|
||||
}
|
||||
}
|
||||
try {
|
||||
FileHelper.setFilePerms(cameraConfig.pipelineFolderPath);
|
||||
} catch (IOException e) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private File[] getPipelineFiles() {
|
||||
return new File(cameraConfig.pipelineFolderPath.toUri()).listFiles();
|
||||
}
|
||||
|
||||
private boolean folderHasPipelines() {
|
||||
File[] folderContents = getPipelineFiles();
|
||||
if (folderContents == null) return false;
|
||||
return folderContents.length > 0;
|
||||
}
|
||||
|
||||
void check() {
|
||||
cameraConfig.checkFolder();
|
||||
checkFolder();
|
||||
// Check if there's at least one pipe
|
||||
if (!folderHasPipelines()) {
|
||||
save(new StandardCVPipelineSettings());
|
||||
}
|
||||
}
|
||||
|
||||
private Path getPipelinePath(CVPipelineSettings setting) {
|
||||
String pipelineName = setting.nickname.replace(' ', '_');
|
||||
String fullFileName = pipelineName + ".json";
|
||||
return Path.of(cameraConfig.pipelineFolderPath.toString(), fullFileName);
|
||||
}
|
||||
|
||||
private boolean pipelineExists(CVPipelineSettings setting) {
|
||||
return Files.exists(getPipelinePath(setting));
|
||||
}
|
||||
|
||||
public void save(CVPipelineSettings settings) {
|
||||
|
||||
var path = getPipelinePath(settings);
|
||||
|
||||
if (settings instanceof StandardCVPipelineSettings) {
|
||||
try {
|
||||
JacksonHelper.serialize(path, (StandardCVPipelineSettings) settings, StandardCVPipelineSettings.class, new StandardCVPipelineSettingsSerializer(), true);
|
||||
FileHelper.setFilePerms(path);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
JacksonHelper.serializer(path, settings, true);
|
||||
FileHelper.setFilePerms(path);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void save(List<CVPipelineSettings> settings) {
|
||||
for (CVPipelineSettings setting : settings) {
|
||||
save(setting);
|
||||
}
|
||||
}
|
||||
|
||||
public void delete(CVPipelineSettings setting) {
|
||||
if (pipelineExists(setting)) {
|
||||
try {
|
||||
Files.delete(getPipelinePath(setting));
|
||||
} catch (IOException e) {
|
||||
System.err.println("Failed to delete pipeline!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CVPipelineSettings rename(CVPipelineSettings setting, String newName) {
|
||||
if (pipelineExists(setting)) {
|
||||
delete(setting);
|
||||
setting.nickname = newName;
|
||||
save(setting);
|
||||
} else {
|
||||
setting.nickname = newName;
|
||||
save(setting);
|
||||
}
|
||||
return setting;
|
||||
}
|
||||
|
||||
public List<CVPipelineSettings> load() {
|
||||
check(); // TODO: this ensures there will be a default pipeline. is the check later necessary?
|
||||
|
||||
File[] pipelineFiles = getPipelineFiles();
|
||||
List<CVPipelineSettings> deserializedList = new ArrayList<>();
|
||||
|
||||
if (pipelineFiles == null || pipelineFiles.length < 1) {
|
||||
// TODO handle no pipelines to load
|
||||
System.err.println("no pipes to load! loading default");
|
||||
} else {
|
||||
for (File pipelineFile : pipelineFiles) {
|
||||
try {
|
||||
var pipe = JacksonHelper.deserialize(Paths.get(pipelineFile.getPath()), StandardCVPipelineSettings.class, new StandardCVPipelineSettingsDeserializer());
|
||||
deserializedList.add(pipe);
|
||||
} catch (IOException e) {
|
||||
System.err.println("couldn't load cvpipeline2d");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return deserializedList;
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
package com.chameleonvision.config.serializers;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||
import com.fasterxml.jackson.databind.type.CollectionType;
|
||||
import com.fasterxml.jackson.databind.type.TypeFactory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.opencv.core.MatOfPoint3f;
|
||||
import org.opencv.core.Point3;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class BaseDeserializer<T> extends StdDeserializer<T> {
|
||||
protected BaseDeserializer(Class<?> vc) {
|
||||
super(vc);
|
||||
}
|
||||
|
||||
JsonNode baseNode;
|
||||
|
||||
private static final CollectionType numberListColType = TypeFactory.defaultInstance().constructCollectionType(List.class, Number.class);
|
||||
private CollectionType pointListColType = TypeFactory.defaultInstance().constructCollectionType(List.class, Object.class);
|
||||
private static final ObjectMapper mapper = new ObjectMapper();
|
||||
private static boolean nodeGood(JsonNode node) {
|
||||
return node != null && !node.toString().equals("");
|
||||
}
|
||||
|
||||
List<Number> getNumberList(String name, List<Number> defaultValue) throws JsonProcessingException {
|
||||
JsonNode node = baseNode.get(name);
|
||||
|
||||
if (nodeGood(node)) {
|
||||
return mapper.readValue(node.toString(), numberListColType);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
boolean getBoolean(String name, boolean defaultValue) {
|
||||
JsonNode node = baseNode.get(name);
|
||||
|
||||
if (nodeGood(node)) {
|
||||
return node.booleanValue();
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
int getInt(String name, int defaultValue) {
|
||||
return (int) getDouble(name, defaultValue);
|
||||
}
|
||||
|
||||
double getDouble(String name, double defaultValue) {
|
||||
JsonNode node = baseNode.get(name);
|
||||
|
||||
if (nodeGood(node)) {
|
||||
return node.numberValue().doubleValue();
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
String getString(String name, String defaultValue) {
|
||||
JsonNode node = baseNode.get(name);
|
||||
|
||||
if (nodeGood(node)) {
|
||||
return node.asText();
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
<E extends Enum<E>> E getEnum(String name, Class<E> enumClass, E defaultValue) throws IOException {
|
||||
JsonNode node = baseNode.get(name);
|
||||
|
||||
if (nodeGood(node)) {
|
||||
E[] possibleVals = enumClass.getEnumConstants();
|
||||
String jsonVal = baseNode.get(name).asText();
|
||||
|
||||
for (E val : possibleVals) {
|
||||
if (val.name().equals(jsonVal)) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
MatOfPoint3f getMatOfPoint3f(String name, MatOfPoint3f defaultValue) throws JsonProcessingException {
|
||||
JsonNode node = baseNode.get(name);
|
||||
if (nodeGood(node)){
|
||||
List<List<Number>> numberList = mapper.readValue(node.toString(), pointListColType);
|
||||
List<Point3> point3List = new ArrayList<>();
|
||||
for (List<Number> tmp : numberList){
|
||||
Point3 p = new Point3();
|
||||
p.x = tmp.get(0).doubleValue();
|
||||
p.y = tmp.get(1).doubleValue();
|
||||
p.z = tmp.get(2).doubleValue();
|
||||
point3List.add(p);
|
||||
}
|
||||
MatOfPoint3f mat = new MatOfPoint3f();
|
||||
mat.fromList(point3List);
|
||||
return mat;
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package com.chameleonvision.config.serializers;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
import org.opencv.core.MatOfPoint3f;
|
||||
import org.opencv.core.Point;
|
||||
import org.opencv.core.Point3;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class BaseSerializer<T> extends StdSerializer<T> {
|
||||
protected BaseSerializer(Class<T> t) {
|
||||
super(t);
|
||||
}
|
||||
|
||||
JsonGenerator generator;
|
||||
|
||||
void writeNumberListAsNumberArray(String name, List<Number> list) throws IOException {
|
||||
generator.writeArrayFieldStart(name);
|
||||
for (Number i : list) {
|
||||
generator.writeObject(i);
|
||||
}
|
||||
generator.writeEndArray();
|
||||
}
|
||||
|
||||
<E extends Enum<E>> void writeEnum(String name, E num) throws IOException {
|
||||
generator.writeFieldName(name);
|
||||
generator.writeString(num.name());
|
||||
}
|
||||
|
||||
void writeMatOfPoint3f(String name, MatOfPoint3f mat) throws IOException {
|
||||
List<Point3> point3List = mat.toList();
|
||||
generator.writeArrayFieldStart(name);
|
||||
|
||||
for (Point3 point3 : point3List) {
|
||||
double[] tmp = {point3.x, point3.y, point3.z};
|
||||
generator.writeObject(tmp);
|
||||
}
|
||||
generator.writeEndArray();
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package com.chameleonvision.config.serializers;
|
||||
|
||||
import com.chameleonvision.vision.enums.*;
|
||||
import com.chameleonvision.vision.pipeline.impl.StandardCVPipelineSettings;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.IntNode;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class StandardCVPipelineSettingsDeserializer extends BaseDeserializer<StandardCVPipelineSettings> {
|
||||
public StandardCVPipelineSettingsDeserializer() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
private StandardCVPipelineSettingsDeserializer(Class<?> vc) {
|
||||
super(vc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StandardCVPipelineSettings deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
|
||||
// set BaseDeserializer parser reference.
|
||||
baseNode = jsonParser.getCodec().readTree(jsonParser);
|
||||
|
||||
StandardCVPipelineSettings pipeline = new StandardCVPipelineSettings();
|
||||
|
||||
pipeline.index = getInt("index", pipeline.index);
|
||||
|
||||
pipeline.flipMode = getEnum("flipMode", ImageFlipMode.class, pipeline.flipMode);
|
||||
pipeline.rotationMode = getEnum("rotationMode", ImageRotationMode.class, pipeline.rotationMode);
|
||||
|
||||
pipeline.nickname = getString("nickname", pipeline.nickname);
|
||||
|
||||
pipeline.exposure = getDouble("exposure", pipeline.exposure);
|
||||
pipeline.brightness = getDouble("brightness", pipeline.brightness);
|
||||
pipeline.gain = getDouble("gain", pipeline.gain);
|
||||
|
||||
pipeline.videoModeIndex = getInt("videoModeIndex", pipeline.videoModeIndex);
|
||||
|
||||
pipeline.streamDivisor = getEnum("streamDivisor", StreamDivisor.class, pipeline.streamDivisor);
|
||||
|
||||
pipeline.hue = getNumberList("hue", pipeline.hue);
|
||||
pipeline.saturation = getNumberList("saturation", pipeline.saturation);
|
||||
pipeline.value = getNumberList("value", pipeline.value);
|
||||
|
||||
pipeline.erode = getBoolean("erode", pipeline.erode);
|
||||
pipeline.dilate = getBoolean("dilate", pipeline.dilate);
|
||||
|
||||
pipeline.area = getNumberList("area", pipeline.area);
|
||||
pipeline.ratio = getNumberList("ratio", pipeline.ratio);
|
||||
pipeline.extent = getNumberList("extent", pipeline.extent);
|
||||
|
||||
pipeline.speckle = getInt("speckle", (Integer) pipeline.speckle);
|
||||
|
||||
pipeline.isBinary = getBoolean("isBinary", pipeline.isBinary);
|
||||
|
||||
pipeline.sortMode = getEnum("sortMode", SortMode.class, pipeline.sortMode);
|
||||
pipeline.targetRegion = getEnum("targetRegion", TargetRegion.class, pipeline.targetRegion);
|
||||
pipeline.targetOrientation = getEnum("targetOrientation", TargetOrientation.class, pipeline.targetOrientation);
|
||||
|
||||
pipeline.multiple = getBoolean("multiple", pipeline.multiple);
|
||||
|
||||
pipeline.targetGroup = getEnum("targetGroup", TargetGroup.class, pipeline.targetGroup);
|
||||
pipeline.targetIntersection = getEnum("targetIntersection", TargetIntersection.class, pipeline.targetIntersection);
|
||||
|
||||
pipeline.point = getNumberList("point", pipeline.point);
|
||||
|
||||
pipeline.calibrationMode = getEnum("calibrationMode", CalibrationMode.class, pipeline.calibrationMode);
|
||||
|
||||
pipeline.dualTargetCalibrationM = getDouble("dualTargetCalibrationM", pipeline.dualTargetCalibrationM);
|
||||
pipeline.dualTargetCalibrationB = getDouble("dualTargetCalibrationB", pipeline.dualTargetCalibrationB);
|
||||
|
||||
pipeline.is3D = getBoolean("is3D", pipeline.is3D);
|
||||
pipeline.targetCornerMat = getMatOfPoint3f("targetCornerMat", pipeline.targetCornerMat);
|
||||
pipeline.accuracy = getDouble("accuracy", pipeline.accuracy.doubleValue());
|
||||
|
||||
return pipeline;
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
package com.chameleonvision.config.serializers;
|
||||
|
||||
import com.chameleonvision.vision.pipeline.impl.StandardCVPipelineSettings;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class StandardCVPipelineSettingsSerializer extends BaseSerializer<StandardCVPipelineSettings> {
|
||||
public StandardCVPipelineSettingsSerializer() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
private StandardCVPipelineSettingsSerializer(Class<StandardCVPipelineSettings> t) {
|
||||
super(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(StandardCVPipelineSettings pipeline, JsonGenerator gen, SerializerProvider provider) throws IOException {
|
||||
// set BaseSerializer generator reference.
|
||||
generator = gen;
|
||||
|
||||
gen.writeStartObject();
|
||||
|
||||
gen.writeNumberField("index", pipeline.index);
|
||||
|
||||
writeEnum("flipMode", pipeline.flipMode);
|
||||
writeEnum("rotationMode", pipeline.rotationMode);
|
||||
|
||||
gen.writeStringField("nickname", pipeline.nickname);
|
||||
|
||||
gen.writeNumberField("exposure", pipeline.exposure);
|
||||
gen.writeNumberField("brightness", pipeline.brightness);
|
||||
gen.writeNumberField("gain", pipeline.gain);
|
||||
|
||||
gen.writeNumberField("videoModeIndex", pipeline.videoModeIndex);
|
||||
|
||||
writeEnum("streamDivisor", pipeline.streamDivisor);
|
||||
|
||||
writeNumberListAsNumberArray("hue", pipeline.hue);
|
||||
writeNumberListAsNumberArray("saturation", pipeline.saturation);
|
||||
writeNumberListAsNumberArray("value", pipeline.value);
|
||||
|
||||
gen.writeBooleanField("erode", pipeline.erode);
|
||||
gen.writeBooleanField("dilate", pipeline.dilate);
|
||||
|
||||
writeNumberListAsNumberArray("area", pipeline.area);
|
||||
writeNumberListAsNumberArray("ratio", pipeline.ratio);
|
||||
writeNumberListAsNumberArray("extent", pipeline.extent);
|
||||
|
||||
// speckle rejection
|
||||
gen.writeNumberField("speckle", (Integer) pipeline.speckle);
|
||||
|
||||
// stream output (camera feed, or thresholded feed)
|
||||
gen.writeBooleanField("isBinary", pipeline.isBinary);
|
||||
|
||||
writeEnum("sortMode", pipeline.sortMode);
|
||||
writeEnum("targetRegion", pipeline.targetRegion);
|
||||
writeEnum("targetOrientation", pipeline.targetOrientation);
|
||||
|
||||
// show multiple targets when drawing
|
||||
gen.writeBooleanField("multiple", pipeline.multiple);
|
||||
|
||||
writeEnum("targetGroup", pipeline.targetGroup);
|
||||
writeEnum("targetIntersection", pipeline.targetIntersection);
|
||||
|
||||
// single calibration point
|
||||
writeNumberListAsNumberArray("point", pipeline.point);
|
||||
|
||||
// target X/Y calibration
|
||||
writeEnum("calibrationMode", pipeline.calibrationMode);
|
||||
|
||||
// TODO: better names? or use an array?
|
||||
gen.writeNumberField("dualTargetCalibrationM", pipeline.dualTargetCalibrationM);
|
||||
gen.writeNumberField("dualTargetCalibrationB", pipeline.dualTargetCalibrationB);
|
||||
|
||||
|
||||
gen.writeBooleanField("is3D", pipeline.is3D);
|
||||
writeMatOfPoint3f("targetCornerMat", pipeline.targetCornerMat);
|
||||
gen.writeNumberField("accuracy", pipeline.accuracy.doubleValue());
|
||||
gen.writeEndObject();
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package com.chameleonvision.network;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
public class NetmaskToCIDR {
|
||||
//code belongs to https://stackoverflow.com/questions/19531411/calculate-cidr-from-a-given-netmask-java
|
||||
public static int convertNetmaskToCIDR(InetAddress netmask) {
|
||||
|
||||
byte[] netmaskBytes = netmask.getAddress();
|
||||
int cidr = 0;
|
||||
boolean zero = false;
|
||||
for (byte b : netmaskBytes) {
|
||||
int mask = 0x80;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int result = b & mask;
|
||||
if (result == 0) {
|
||||
zero = true;
|
||||
} else if (zero) {
|
||||
throw new IllegalArgumentException("Invalid netmask.");
|
||||
} else {
|
||||
cidr++;
|
||||
}
|
||||
mask >>>= 1;
|
||||
}
|
||||
}
|
||||
return cidr;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.chameleonvision.network;
|
||||
|
||||
public enum NetworkIPMode {
|
||||
DHCP,
|
||||
STATIC,
|
||||
UNKNOWN
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
package com.chameleonvision.network;
|
||||
|
||||
|
||||
import com.chameleonvision.config.ConfigManager;
|
||||
import com.chameleonvision.util.Platform;
|
||||
|
||||
import java.net.SocketException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class NetworkManager {
|
||||
private NetworkManager() {
|
||||
}
|
||||
|
||||
private static SysNetworking networking;
|
||||
private static boolean isManaged = false;
|
||||
|
||||
public static void initialize(boolean manage) {
|
||||
isManaged = manage;
|
||||
if (!isManaged) {
|
||||
return;
|
||||
}
|
||||
|
||||
Platform platform = Platform.CurrentPlatform;
|
||||
|
||||
if (platform.isLinux()) {
|
||||
networking = new LinuxNetworking();
|
||||
} else if (platform.isWindows()) {
|
||||
// networking = new WindowsNetworking();
|
||||
System.out.println("Windows networking is not yet supported. Running unmanaged.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (networking == null) {
|
||||
throw new RuntimeException("Failed to detect platform!");
|
||||
}
|
||||
|
||||
List<java.net.NetworkInterface> interfaces = new ArrayList<>();
|
||||
List<NetworkInterface> goodInterfaces = new ArrayList<>();
|
||||
|
||||
try {
|
||||
interfaces = networking.getNetworkInterfaces();
|
||||
} catch (SocketException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
var teamBytes = NetworkManager.GetTeamNumberIPBytes(ConfigManager.settings.teamNumber);
|
||||
|
||||
if (interfaces.size() > 0) {
|
||||
for (var inetface : interfaces) {
|
||||
for (var inetfaceAddr : inetface.getInterfaceAddresses()) {
|
||||
var rawAddr = inetfaceAddr.getAddress().getAddress();
|
||||
if (rawAddr.length > 4) continue;
|
||||
if (rawAddr[1] == teamBytes[0] && rawAddr[2] == teamBytes[1]) {
|
||||
goodInterfaces.add(new NetworkInterface(inetface, inetfaceAddr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (goodInterfaces.size() == 0) {
|
||||
isManaged = false;
|
||||
System.err.println("No valid network interfaces found! Staying unmanaged.");
|
||||
return;
|
||||
}
|
||||
|
||||
NetworkInterface botInterface = goodInterfaces.get(0);
|
||||
networking.setNetworkInterface(botInterface);
|
||||
} else {
|
||||
isManaged = false;
|
||||
System.err.println("No valid network interfaces found! Staying unmanaged.");
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] GetTeamNumberIPBytes(int teamNumber) {
|
||||
return new byte[]{(byte) (teamNumber / 100), (byte) (teamNumber % 100)};
|
||||
}
|
||||
|
||||
|
||||
private static boolean setDHCP() {
|
||||
if (!isManaged) {
|
||||
return true;
|
||||
}
|
||||
return networking.setDHCP();
|
||||
}
|
||||
|
||||
private static boolean setStatic(String ipAddress, String netmask, String gateway) {
|
||||
if (!isManaged) {
|
||||
return true;
|
||||
}
|
||||
return networking.setStatic(ipAddress, netmask, gateway);
|
||||
}
|
||||
|
||||
public static boolean setHostname(String hostname) {
|
||||
if (!isManaged) {
|
||||
return true;
|
||||
}
|
||||
return networking.setHostname(hostname);
|
||||
}
|
||||
|
||||
public static boolean setNetwork(boolean isStatic, String ip, String netmask, String gateway) {
|
||||
if (isStatic) {
|
||||
return setStatic(ip, netmask, gateway);
|
||||
} else {
|
||||
return setDHCP();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package com.chameleonvision.network;
|
||||
|
||||
import com.chameleonvision.util.ShellExec;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketException;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class SysNetworking {
|
||||
|
||||
NetworkInterface networkInterface;
|
||||
ShellExec shell = new ShellExec(true, true);
|
||||
|
||||
public String getHostname() {
|
||||
try {
|
||||
var retCode = shell.execute("hostname", null, true);
|
||||
if (retCode == 0) {
|
||||
while(!shell.isOutputCompleted()) {}
|
||||
return shell.getOutput();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setNetworkInterface(NetworkInterface networkInterface) {
|
||||
this.networkInterface = networkInterface;
|
||||
}
|
||||
public abstract boolean setDHCP();
|
||||
public abstract boolean setHostname(String hostname);
|
||||
public abstract boolean setStatic(String ipAddress, String netmask, String gateway);
|
||||
public abstract List<java.net.NetworkInterface> getNetworkInterfaces() throws SocketException;
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package com.chameleonvision.network;
|
||||
|
||||
import java.net.SocketException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class WindowsNetworking extends SysNetworking {
|
||||
|
||||
@Override
|
||||
public boolean setDHCP() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setHostname(String newHostname) {
|
||||
var currentHostname = getHostname();
|
||||
|
||||
if (getHostname() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String command = String.format("wmic computersystem where name=\"%s\" call rename name=\"%s\"", currentHostname, newHostname);
|
||||
|
||||
try {
|
||||
var process = Runtime.getRuntime().exec(command);
|
||||
var returnCode = process.waitFor();
|
||||
return returnCode == 0;
|
||||
} catch(Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setStatic(String ipAddress, String netmask, String gateway) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<java.net.NetworkInterface> getNetworkInterfaces() throws SocketException {
|
||||
var netInterfaces = Collections.list(java.net.NetworkInterface.getNetworkInterfaces());
|
||||
|
||||
List<java.net.NetworkInterface> goodInterfaces = new ArrayList<>();
|
||||
|
||||
for (var netInterface : netInterfaces) {
|
||||
if (netInterface.getDisplayName().toLowerCase().contains("bluetooth")) continue;
|
||||
if (netInterface.getDisplayName().toLowerCase().contains("virtual")) continue;
|
||||
if (netInterface.getDisplayName().toLowerCase().contains("loopback")) continue;
|
||||
if (!netInterface.isUp()) continue;
|
||||
goodInterfaces.add(netInterface);
|
||||
}
|
||||
return goodInterfaces;
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package com.chameleonvision.networktables;
|
||||
|
||||
import com.chameleonvision.config.ConfigManager;
|
||||
import com.chameleonvision.scripting.ScriptEventType;
|
||||
import com.chameleonvision.scripting.ScriptManager;
|
||||
import edu.wpi.first.networktables.LogMessage;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class NetworkTablesManager {
|
||||
|
||||
private NetworkTablesManager() {}
|
||||
|
||||
private static final NetworkTableInstance ntInstance = NetworkTableInstance.getDefault();
|
||||
|
||||
public static final String kRootTableName = "/chameleon-vision";
|
||||
public static final NetworkTable kRootTable = NetworkTableInstance.getDefault().getTable(kRootTableName);
|
||||
|
||||
public static boolean isServer = false;
|
||||
|
||||
private static int getTeamNumber() {
|
||||
return ConfigManager.settings.teamNumber;
|
||||
}
|
||||
|
||||
private static class NTLogger implements Consumer<LogMessage> {
|
||||
|
||||
private boolean hasReportedConnectionFailure = false;
|
||||
|
||||
@Override
|
||||
public void accept(LogMessage logMessage) {
|
||||
if (!hasReportedConnectionFailure && logMessage.message.contains("timed out")) {
|
||||
System.err.println("NT Connection has failed! Will retry in background.");
|
||||
hasReportedConnectionFailure = true;
|
||||
} else if (logMessage.message.contains("connected")) {
|
||||
System.out.println("NT Connected!");
|
||||
hasReportedConnectionFailure = false;
|
||||
ScriptManager.queueEvent(ScriptEventType.kNTConnected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
NetworkTableInstance.getDefault().addLogger(new NTLogger(), 0, 255); // to hide error messages
|
||||
}
|
||||
|
||||
public static void setClientMode(String host) {
|
||||
isServer = false;
|
||||
System.out.println("Starting NT Client");
|
||||
ntInstance.stopServer();
|
||||
if (host != null) {
|
||||
ntInstance.startClient(host);
|
||||
} else {
|
||||
ntInstance.startClientTeam(getTeamNumber());
|
||||
if(ntInstance.isConnected()) {
|
||||
System.out.println("[NetworkTablesManager] Connected to the robot!");
|
||||
} else {
|
||||
System.out.println("[NetworkTablesManager] Could NOT to the robot! Will retry in the background...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void setTeamClientMode() {
|
||||
setClientMode(null);
|
||||
}
|
||||
|
||||
public static void setServerMode() {
|
||||
isServer = true;
|
||||
System.out.println("Starting NT Server");
|
||||
ntInstance.stopClient();
|
||||
ntInstance.startServer();
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.chameleonvision.scripting;
|
||||
|
||||
public enum ScriptCommandType {
|
||||
kDefault(""),
|
||||
kBashScript("bash"),
|
||||
kPythonScript("python"),
|
||||
kPython3Script("python3");
|
||||
|
||||
public final String value;
|
||||
|
||||
ScriptCommandType(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package com.chameleonvision.scripting;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class ScriptConfig {
|
||||
public final ScriptEventType eventType;
|
||||
public final String command;
|
||||
|
||||
public ScriptConfig(ScriptEventType eventType) {
|
||||
this.eventType = eventType;
|
||||
this.command = "";
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public ScriptConfig(
|
||||
@JsonProperty("eventType") ScriptEventType eventType,
|
||||
@JsonProperty("command") String command
|
||||
) {
|
||||
this.eventType = eventType;
|
||||
this.command = command;
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package com.chameleonvision.scripting;
|
||||
|
||||
import com.chameleonvision.Debug;
|
||||
import com.chameleonvision.util.ShellExec;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ScriptEvent {
|
||||
private static final ShellExec executor = new ShellExec(true, true);
|
||||
|
||||
public final ScriptConfig config;
|
||||
|
||||
public ScriptEvent(ScriptConfig config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public int run() throws IOException {
|
||||
int retVal = executor.executeBashCommand(config.command);
|
||||
|
||||
String output = executor.getOutput();
|
||||
String error = executor.getError();
|
||||
|
||||
if (!error.isEmpty()) {
|
||||
System.err.printf("Error when running \"%s\" script: %s\n", config.eventType.name(), error);
|
||||
} else if (!output.isEmpty()) {
|
||||
Debug.printInfo(String.format("Output from \"%s\" script: %s\n", config.eventType.name(), output));
|
||||
}
|
||||
Debug.printInfo(String.format("Script for %s ran with command line: \"%s\", exit code: %d, output: %s, error: %s\n", config.eventType.name(), config.command, retVal, output, error));
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package com.chameleonvision.scripting;
|
||||
|
||||
public enum ScriptEventType {
|
||||
kProgramInit("Program Init"),
|
||||
kProgramExit("Program Exit"),
|
||||
kNTConnected("NT Connected"),
|
||||
kLEDOn("LED On"),
|
||||
kLEDOff("LED Off"),
|
||||
kEnterDriverMode("Enter Driver Mode"),
|
||||
kExitDriverMode("Exit Driver Mode"),
|
||||
kFoundTarget("Found Target"),
|
||||
kFoundMultipleTarget("Found Multiple Target"),
|
||||
kLostTarget("Lost Target"),
|
||||
kPipelineLag("Pipeline Lag");
|
||||
|
||||
public final String value;
|
||||
|
||||
ScriptEventType(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
package com.chameleonvision.scripting;
|
||||
|
||||
import com.chameleonvision.Debug;
|
||||
import com.chameleonvision.config.ConfigManager;
|
||||
import com.chameleonvision.util.JacksonHelper;
|
||||
import com.chameleonvision.util.LoopingRunnable;
|
||||
import com.chameleonvision.util.Platform;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
|
||||
public class ScriptManager {
|
||||
|
||||
private ScriptManager() {
|
||||
}
|
||||
|
||||
private static final List<ScriptEvent> events = new ArrayList<>();
|
||||
private static final LinkedBlockingDeque<ScriptEventType> queuedEvents = new LinkedBlockingDeque<>(25);
|
||||
|
||||
public static void initialize() {
|
||||
ScriptConfigManager.initialize();
|
||||
if (ScriptConfigManager.fileExists()) {
|
||||
for (ScriptConfig scriptConfig : ScriptConfigManager.loadConfig()) {
|
||||
ScriptEvent scriptEvent = new ScriptEvent(scriptConfig);
|
||||
events.add(scriptEvent);
|
||||
}
|
||||
|
||||
new Thread(new ScriptRunner(10L)).start();
|
||||
} else {
|
||||
System.err.println("Something went wrong initializing scripts! Events will not run.");
|
||||
}
|
||||
}
|
||||
|
||||
private static class ScriptRunner extends LoopingRunnable {
|
||||
|
||||
ScriptRunner(Long loopTimeMs) {
|
||||
super(loopTimeMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void process() {
|
||||
try {
|
||||
|
||||
handleEvent(queuedEvents.takeFirst());
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleEvent(ScriptEventType eventType) {
|
||||
var toRun = events.parallelStream().filter(e -> e.config.eventType == eventType).findFirst().orElse(null);
|
||||
if (toRun != null) {
|
||||
try {
|
||||
toRun.run();
|
||||
} catch (IOException e) {
|
||||
System.err.printf("Failed to run script for event: %s, exception below.\n%s\n", eventType.name(), e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class ScriptConfigManager {
|
||||
|
||||
protected static final Path scriptConfigPath = Paths.get(ConfigManager.SettingsPath.toString(), "scripts.json");
|
||||
|
||||
private ScriptConfigManager() {
|
||||
}
|
||||
|
||||
static boolean fileExists() {
|
||||
return Files.exists(scriptConfigPath);
|
||||
}
|
||||
|
||||
public static void initialize() {
|
||||
if (!fileExists()) {
|
||||
List<ScriptConfig> eventsConfig = new ArrayList<>();
|
||||
for (var eventType : ScriptEventType.values()) {
|
||||
eventsConfig.add(new ScriptConfig(eventType));
|
||||
}
|
||||
|
||||
try {
|
||||
JacksonHelper.serializer(scriptConfigPath, eventsConfig.toArray(new ScriptConfig[0]), true);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static List<ScriptConfig> loadConfig() {
|
||||
try {
|
||||
var raw = JacksonHelper.deserialize(scriptConfigPath, ScriptConfig[].class);
|
||||
if (raw != null) {
|
||||
return List.of(raw);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
protected static void deleteConfig() {
|
||||
try {
|
||||
Files.delete(scriptConfigPath);
|
||||
} catch (IOException e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void queueEvent(ScriptEventType eventType) {
|
||||
if (!Platform.getCurrentPlatform().isWindows()) {
|
||||
try {
|
||||
queuedEvents.putLast(eventType);
|
||||
Debug.printInfo("Queued event: " + eventType.name());
|
||||
} catch (InterruptedException e) {
|
||||
System.err.println("Failed to add event to queue: " + eventType.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.chameleonvision.server;
|
||||
|
||||
public class Main {
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package com.chameleonvision.util;
|
||||
|
||||
import com.chameleonvision.Debug;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.PosixFileAttributes;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class FileHelper {
|
||||
private FileHelper() {}
|
||||
|
||||
private static final Set<PosixFilePermission> allReadWriteExecutePerms = new HashSet<>(Arrays.asList(PosixFilePermission.values()));
|
||||
|
||||
public static void setFilePerms(Path path) throws IOException {
|
||||
if (!Platform.CurrentPlatform.isWindows()) {
|
||||
File thisFile = path.toFile();
|
||||
Set<PosixFilePermission> perms = Files.readAttributes(path, PosixFileAttributes.class).permissions();
|
||||
if (!perms.equals(allReadWriteExecutePerms)) {
|
||||
Debug.printInfo("Setting perms on" + path.toString());
|
||||
Files.setPosixFilePermissions(path, perms);
|
||||
if (thisFile.isDirectory()) {
|
||||
for (File subfile : thisFile.listFiles()) {
|
||||
setFilePerms(subfile.toPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void setAllPerms(Path path) {
|
||||
if (!Platform.CurrentPlatform.isWindows()) {
|
||||
String command = String.format("chmod 777 -R %s", path.toString());
|
||||
try {
|
||||
Process p = Runtime.getRuntime().exec(command);
|
||||
p.waitFor();
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
// TODO file perms on Windows
|
||||
System.out.println("File permission setting not available on Windows. Not changing file permissions.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package com.chameleonvision.util;
|
||||
|
||||
import edu.wpi.cscore.VideoMode;
|
||||
import org.opencv.core.Scalar;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class Helpers {
|
||||
private static final String kServicePath = "/etc/systemd/system/chameleonVision.service";
|
||||
private static final String kServiceString = "[Unit]\n" +
|
||||
"Description=chameleon vision\n" +
|
||||
"\n" +
|
||||
"[Service]\n" +
|
||||
"ExecStart=/usr/bin/java -jar %s \n" +
|
||||
"StandardOutput=file:/var/log/chameleon.out.txt\n" +
|
||||
"StandardError=file:/var/log/chameleon.err.txt\n" +
|
||||
"Type=simple\n" +
|
||||
"WorkingDirectory=/usr/local/bin\n" +
|
||||
"\n" +
|
||||
"[Install]\n" +
|
||||
"WantedBy=multi-user.target\n" +
|
||||
"\n";
|
||||
|
||||
|
||||
|
||||
private Helpers() {
|
||||
}
|
||||
|
||||
public static Scalar colorToScalar(Color color) {
|
||||
return new Scalar(color.getBlue(), color.getGreen(), color.getRed());
|
||||
}
|
||||
|
||||
public static HashMap VideoModeToHashMap(VideoMode videoMode) {
|
||||
return new HashMap<String, Object>() {{
|
||||
put("width", videoMode.width);
|
||||
put("height", videoMode.height);
|
||||
put("fps", videoMode.fps);
|
||||
put("pixelFormat", videoMode.pixelFormat.toString());
|
||||
}};
|
||||
}
|
||||
|
||||
public static void setService(Path filePath) throws IOException, InterruptedException {
|
||||
String newService = String.format(kServiceString, filePath.toString());
|
||||
File file = new File(kServicePath);
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
Writer writer = new FileWriter(file, false);
|
||||
writer.write(newService);
|
||||
writer.close();
|
||||
Process p = Runtime.getRuntime().exec("systemctl enable chameleonVision.service");
|
||||
p.waitFor();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package com.chameleonvision.util;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
|
||||
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class JacksonHelper {
|
||||
private JacksonHelper() {
|
||||
} // no construction, utility class
|
||||
|
||||
public static <T> void serializer(Path path, T object) throws IOException {
|
||||
serializer(path, object, false);
|
||||
}
|
||||
|
||||
public static <T> void serializer(Path path, T object, boolean forceSync) throws IOException {
|
||||
PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder().allowIfBaseType(object.getClass()).build();
|
||||
ObjectMapper objectMapper = JsonMapper.builder().activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT).build();
|
||||
String json = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
|
||||
saveJsonString(json, path, forceSync);
|
||||
}
|
||||
|
||||
public static <T> T deserialize(Path path, Class<T> ref) throws IOException {
|
||||
PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder().allowIfBaseType(ref).build();
|
||||
ObjectMapper objectMapper = JsonMapper.builder().activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT).build();
|
||||
File jsonFile = new File(path.toString());
|
||||
if (jsonFile.exists() && jsonFile.length() > 0) {
|
||||
return objectMapper.readValue(jsonFile, ref);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T> T deserialize(Path path, Class<T> ref, StdDeserializer<T> deserializer) throws IOException {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
SimpleModule module = new SimpleModule();
|
||||
module.addDeserializer(ref, deserializer);
|
||||
objectMapper.registerModule(module);
|
||||
|
||||
File jsonFile = new File(path.toString());
|
||||
if (jsonFile.exists() && jsonFile.length() > 0) {
|
||||
return objectMapper.readValue(jsonFile, ref);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static <T> void serialize(Path path, T object, Class<T> ref, StdSerializer<T> serializer) throws IOException {
|
||||
serialize(path, object, ref, serializer, false);
|
||||
}
|
||||
|
||||
public static <T> void serialize(Path path, T object, Class<T> ref, StdSerializer<T> serializer, boolean forceSync) throws IOException {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
SimpleModule module = new SimpleModule();
|
||||
module.addSerializer(ref, serializer);
|
||||
objectMapper.registerModule(module);
|
||||
String json = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
|
||||
saveJsonString(json, path, forceSync);
|
||||
}
|
||||
|
||||
private static void saveJsonString(String json, Path path, boolean forceSync) throws IOException {
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(path.toFile());
|
||||
fileOutputStream.write(json.getBytes());
|
||||
fileOutputStream.flush();
|
||||
if (forceSync) {
|
||||
FileDescriptor fileDescriptor = fileOutputStream.getFD();
|
||||
fileDescriptor.sync();
|
||||
}
|
||||
fileOutputStream.close();
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.chameleonvision.util;
|
||||
|
||||
/**
|
||||
* A thread that tries to run at a specified loop time
|
||||
*/
|
||||
public abstract class LoopingRunnable implements Runnable {
|
||||
protected volatile Long loopTimeMs;
|
||||
|
||||
protected abstract void process();
|
||||
|
||||
public LoopingRunnable(Long loopTimeMs) {
|
||||
this.loopTimeMs = loopTimeMs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while(!Thread.interrupted()) {
|
||||
var now = System.currentTimeMillis();
|
||||
|
||||
// Do the thing
|
||||
process();
|
||||
|
||||
// sleep for the remaining time
|
||||
var timeElapsed = System.currentTimeMillis() - now;
|
||||
var delta = loopTimeMs - timeElapsed;
|
||||
try {
|
||||
if(delta > 0.0) {
|
||||
|
||||
Thread.sleep(delta, 0);
|
||||
|
||||
} else {
|
||||
Thread.sleep(1);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package com.chameleonvision.util;
|
||||
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
|
||||
public class MathHandler {
|
||||
MathHandler() {}
|
||||
|
||||
public static double sigmoid(Number x){
|
||||
double bias = 0;
|
||||
double a = 5;
|
||||
double b = -0.05;
|
||||
double k = 200;
|
||||
|
||||
if (x.doubleValue() < 50){
|
||||
bias = -1.338;
|
||||
}
|
||||
|
||||
return ((k / (1 + Math.pow(Math.E,(a + (b * x.doubleValue()))))) + bias);
|
||||
}
|
||||
|
||||
public static double toSlope(Number angle){
|
||||
return FastMath.atan(FastMath.toRadians(angle.doubleValue() - 90));
|
||||
}
|
||||
|
||||
public static double roundTo(double value, int to) {
|
||||
double toMult = Math.pow(10, to);
|
||||
return (double)Math.round(value * toMult) / toMult;
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package com.chameleonvision.util;
|
||||
|
||||
public class MemoryManager {
|
||||
|
||||
private static final long MEGABYTE_FACTOR = 1024L * 1024L;
|
||||
|
||||
private int collectionThreshold;
|
||||
private long collectionPeriodMillis = -1;
|
||||
|
||||
private double lastUsedMb = 0;
|
||||
private long lastCollectionMillis = 0;
|
||||
|
||||
public MemoryManager(int collectionThreshold) {
|
||||
this.collectionThreshold = collectionThreshold;
|
||||
}
|
||||
|
||||
public MemoryManager(int collectionThreshold, long collectionPeriodMillis) {
|
||||
this.collectionThreshold = collectionThreshold;
|
||||
this.collectionPeriodMillis = collectionPeriodMillis;
|
||||
}
|
||||
|
||||
public void setCollectionThreshold(int collectionThreshold) {
|
||||
this.collectionThreshold = collectionThreshold;
|
||||
}
|
||||
|
||||
public void setCollectionPeriodMillis(long collectionPeriodMillis) {
|
||||
this.collectionPeriodMillis = collectionPeriodMillis;
|
||||
}
|
||||
|
||||
private static long getUsedMemory() {
|
||||
return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
|
||||
}
|
||||
|
||||
private static double getUsedMemoryMB() {
|
||||
return ((double) getUsedMemory() / MEGABYTE_FACTOR);
|
||||
}
|
||||
|
||||
private void collect() {
|
||||
System.gc();
|
||||
System.runFinalization();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
run(false);
|
||||
}
|
||||
|
||||
public void run(boolean print) {
|
||||
var usedMem = getUsedMemoryMB();
|
||||
|
||||
if (usedMem != lastUsedMb) {
|
||||
lastUsedMb = usedMem;
|
||||
if (print) System.out.printf("Memory usage: %.2fMB\n", usedMem);
|
||||
}
|
||||
|
||||
boolean collectionThresholdPassed = usedMem >= collectionThreshold;
|
||||
boolean collectionPeriodPassed = collectionPeriodMillis != -1 && (System.currentTimeMillis() - lastCollectionMillis >= collectionPeriodMillis);
|
||||
|
||||
if (collectionThresholdPassed || collectionPeriodPassed) {
|
||||
collect();
|
||||
lastCollectionMillis = System.currentTimeMillis();
|
||||
if (print) {
|
||||
System.out.printf("Garbage collected at %.2fMB\n", usedMem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
package com.chameleonvision.util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public enum Platform {
|
||||
WINDOWS_64("Windows x64"),
|
||||
LINUX_64("Linux x64"),
|
||||
LINUX_RASPBIAN("Linux Raspbian"),
|
||||
LINUX_ARM64("Linux ARM64"),
|
||||
MACOS_64("Mac OS x64"),
|
||||
UNSUPPORTED("Unsupported Platform");
|
||||
|
||||
public final String value;
|
||||
|
||||
Platform(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
private static final String OS_NAME = System.getProperty("os.name");
|
||||
private static final String OS_ARCH = System.getProperty("os.arch");
|
||||
public static final Platform CurrentPlatform = getCurrentPlatform();
|
||||
|
||||
public boolean isWindows() {
|
||||
return this == WINDOWS_64;
|
||||
}
|
||||
|
||||
public boolean isLinux() {
|
||||
return this == LINUX_64 || this == LINUX_RASPBIAN || this == LINUX_ARM64;
|
||||
}
|
||||
|
||||
public boolean isMac() {
|
||||
return this == MACOS_64;
|
||||
}
|
||||
|
||||
public static boolean isRaspberryPi() {
|
||||
return CurrentPlatform.equals(LINUX_RASPBIAN);
|
||||
}
|
||||
|
||||
private static ShellExec shell = new ShellExec(true, false);
|
||||
|
||||
public boolean isRoot() {
|
||||
if (isLinux() || isMac()) {
|
||||
try {
|
||||
shell.execute("id", null, true, "-u");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
while (!shell.isOutputCompleted()) {
|
||||
// ignored
|
||||
}
|
||||
if (shell.getExitCode() == 0) {
|
||||
var out = shell.getOutput();
|
||||
out = out.split("\n")[0];
|
||||
return out.equals("0");
|
||||
}
|
||||
} else if (isWindows()) {
|
||||
return true;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isRaspbian() {
|
||||
try (BufferedReader reader = Files.newBufferedReader(Paths.get("/etc/os-release"))) {
|
||||
String value = reader.readLine();
|
||||
return value.contains("Raspbian");
|
||||
} catch (IOException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static Platform getCurrentPlatform() {
|
||||
if (OS_NAME.contains("Windows")) {
|
||||
if (OS_ARCH.equals("amd64")) return Platform.WINDOWS_64;
|
||||
}
|
||||
|
||||
if (OS_NAME.contains("Linux")) {
|
||||
if (OS_ARCH.equals("amd64")) return Platform.LINUX_64;
|
||||
if (isRaspbian()) return Platform.LINUX_RASPBIAN;
|
||||
if (OS_ARCH.contains("aarch")) return Platform.LINUX_ARM64;
|
||||
}
|
||||
|
||||
if (OS_NAME.contains("Mac")) {
|
||||
if (OS_ARCH.equals("amd64")) return Platform.MACOS_64;
|
||||
}
|
||||
|
||||
System.out.printf("Unknown Platform! OS: %s, Architecture: %s", OS_NAME, OS_ARCH);
|
||||
return Platform.UNSUPPORTED;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
if (this.equals(UNSUPPORTED)) {
|
||||
return String.format("Unknown Platform. OS: %s, Architecture: %s", OS_NAME, OS_ARCH);
|
||||
} else {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.chameleonvision.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
public class ProgramDirectoryUtilities {
|
||||
private static String getJarName() {
|
||||
return new File(ProgramDirectoryUtilities.class.getProtectionDomain()
|
||||
.getCodeSource()
|
||||
.getLocation()
|
||||
.getPath())
|
||||
.getName();
|
||||
}
|
||||
|
||||
private static boolean runningFromJAR() {
|
||||
String jarName = getJarName();
|
||||
return jarName.contains(".jar");
|
||||
}
|
||||
|
||||
public static String getProgramDirectory() {
|
||||
if (runningFromJAR()) {
|
||||
return getCurrentJARDirectory();
|
||||
} else {
|
||||
return System.getProperty("user.dir");
|
||||
}
|
||||
}
|
||||
|
||||
private static String getCurrentJARDirectory() {
|
||||
try {
|
||||
return new File(ProgramDirectoryUtilities.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParent();
|
||||
} catch (URISyntaxException exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package com.chameleonvision.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Utilities {
|
||||
private Utilities() {}
|
||||
|
||||
public static boolean isValidIPV4(final String ip) {
|
||||
String PATTERN = "^((0|1\\d?\\d?|2[0-4]?\\d?|25[0-5]?|[3-9]\\d?)\\.){3}(0|1\\d?\\d?|2[0-4]?\\d?|25[0-5]?|[3-9]\\d?)$";
|
||||
|
||||
return ip.matches(PATTERN);
|
||||
}
|
||||
|
||||
public static List<Byte> getDigitBytes(int num) {
|
||||
List<Byte> digits = new ArrayList<>();
|
||||
collectDigitBytes(num, digits);
|
||||
return digits;
|
||||
}
|
||||
|
||||
private static void collectDigitBytes(int num, List<Byte> digits) {
|
||||
if (num / 10 > 0) {
|
||||
collectDigitBytes( num / 10, digits);
|
||||
}
|
||||
digits.add((byte) (num % 10));
|
||||
}
|
||||
|
||||
public static List<Integer> getDigits(int num) {
|
||||
List<Integer> digits = new ArrayList<>();
|
||||
collectDigits(num, digits);
|
||||
return digits;
|
||||
}
|
||||
|
||||
private static void collectDigits(int num, List<Integer> digits) {
|
||||
if(num / 10 > 0) {
|
||||
collectDigits(num / 10, digits);
|
||||
}
|
||||
digits.add(num % 10);
|
||||
}
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
package com.chameleonvision.vision;
|
||||
|
||||
import com.chameleonvision.config.CameraConfig;
|
||||
import com.chameleonvision.config.CameraJsonConfig;
|
||||
import com.chameleonvision.config.ConfigManager;
|
||||
import com.chameleonvision.config.FullCameraConfiguration;
|
||||
import com.chameleonvision.util.Helpers;
|
||||
import com.chameleonvision.util.Platform;
|
||||
import com.chameleonvision.vision.camera.USBCameraCapture;
|
||||
import com.chameleonvision.vision.pipeline.CVPipelineSettings;
|
||||
import edu.wpi.cscore.UsbCamera;
|
||||
import edu.wpi.cscore.UsbCameraInfo;
|
||||
import org.opencv.videoio.VideoCapture;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class VisionManager {
|
||||
private VisionManager() {}
|
||||
|
||||
private static final LinkedHashMap<String, UsbCameraInfo> usbCameraInfosByCameraName = new LinkedHashMap<>();
|
||||
private static final LinkedList<FullCameraConfiguration> loadedCameraConfigs = new LinkedList<>();
|
||||
private static final LinkedList<VisionProcessManageable> visionProcesses = new LinkedList<>();
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
private static class VisionProcessManageable {
|
||||
public final int index;
|
||||
public final String name;
|
||||
public final VisionProcess visionProcess;
|
||||
|
||||
public VisionProcessManageable(int index, String name, VisionProcess visionProcess) {
|
||||
this.index = index;
|
||||
this.name = name;
|
||||
this.visionProcess = visionProcess;
|
||||
}
|
||||
}
|
||||
|
||||
private static VisionProcess currentUIVisionProcess;
|
||||
|
||||
public static boolean initializeSources() {
|
||||
int suffix = 0;
|
||||
for (UsbCameraInfo info : UsbCamera.enumerateUsbCameras()) {
|
||||
VideoCapture cap = new VideoCapture(info.dev);
|
||||
if (cap.isOpened()) {
|
||||
cap.release();
|
||||
// Filter non-ascii characters because ext4 doesn't play nice with unicode in directory names
|
||||
String name = info.name.replaceAll("[^\\x00-\\x7F]", "");
|
||||
while (usbCameraInfosByCameraName.containsKey(name)) {
|
||||
suffix++;
|
||||
name = String.format("%s (%d)", name, suffix);
|
||||
}
|
||||
usbCameraInfosByCameraName.put(name, info);
|
||||
}
|
||||
}
|
||||
|
||||
if (usbCameraInfosByCameraName.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
System.out.printf("[VisionManager] Found %s cameras!\n", usbCameraInfosByCameraName.size());
|
||||
|
||||
// load the config
|
||||
List<CameraJsonConfig> preliminaryConfigs = new ArrayList<>();
|
||||
|
||||
usbCameraInfosByCameraName.forEach((suffixedName, cameraInfo) -> {
|
||||
String truePath;
|
||||
|
||||
if (Platform.CurrentPlatform.isWindows()) {
|
||||
truePath = cameraInfo.path;
|
||||
} else {
|
||||
truePath = Arrays.stream(cameraInfo.otherPaths).filter(x -> x.contains("/dev/v4l/by-path")).findFirst().orElse(cameraInfo.path);
|
||||
}
|
||||
|
||||
preliminaryConfigs.add(new CameraJsonConfig(truePath, suffixedName));
|
||||
});
|
||||
|
||||
loadedCameraConfigs.addAll(ConfigManager.initializeCameras(preliminaryConfigs));
|
||||
System.out.printf("[VisionManager] Loaded %s cameras!\n", loadedCameraConfigs.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean initializeProcesses() {
|
||||
for (int i = 0; i < loadedCameraConfigs.size(); i++) {
|
||||
FullCameraConfiguration config = loadedCameraConfigs.get(i);
|
||||
|
||||
CameraJsonConfig cameraJsonConfig = config.cameraConfig;
|
||||
|
||||
USBCameraCapture camera = new USBCameraCapture(config);
|
||||
VisionProcess process = new VisionProcess(camera, config);
|
||||
process.pipelineManager.driverModePipeline.settings = config.driverMode;
|
||||
visionProcesses.add(new VisionProcessManageable(i, cameraJsonConfig.name, process));
|
||||
}
|
||||
currentUIVisionProcess = getVisionProcessByIndex(0);
|
||||
ConfigManager.settings.currentCamera = visionProcesses.get(0).name;
|
||||
|
||||
System.out.printf("[VisionManager] Loaded %s vision processes! Current process: %s\n", visionProcesses.size(), visionProcesses.get(0).name);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void startProcesses() {
|
||||
visionProcesses.forEach((vpm) -> vpm.visionProcess.start());
|
||||
}
|
||||
|
||||
public static VisionProcess getCurrentUIVisionProcess() {
|
||||
return currentUIVisionProcess;
|
||||
}
|
||||
|
||||
public static CameraConfig getCurrentCameraConfig() {
|
||||
return getCameraConfig(currentUIVisionProcess);
|
||||
}
|
||||
|
||||
public static CameraConfig getCameraConfig(VisionProcess process) {
|
||||
String cameraName = process.getCamera().getProperties().name;
|
||||
return Objects.requireNonNull(loadedCameraConfigs.stream().filter(x -> x.cameraConfig.name.equals(cameraName)).findFirst().orElse(null)).fileConfig;
|
||||
}
|
||||
|
||||
public static void setCurrentProcessByIndex(int processIndex) {
|
||||
if (processIndex > visionProcesses.size() - 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentUIVisionProcess = getVisionProcessByIndex(processIndex);
|
||||
ConfigManager.settings.currentCamera = visionProcesses.get(processIndex).name;
|
||||
}
|
||||
|
||||
public static VisionProcess getVisionProcessByIndex(int processIndex) {
|
||||
if (processIndex > visionProcesses.size() - 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
VisionProcessManageable vpm = visionProcesses.stream().filter(manageable -> manageable.index == processIndex).findFirst().orElse(null);
|
||||
return vpm != null ? vpm.visionProcess : null;
|
||||
}
|
||||
|
||||
public static List<String> getAllCameraNicknames() {
|
||||
return visionProcesses.stream().map(vpm -> vpm.visionProcess.getCamera()
|
||||
.getProperties().getNickname()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static List<String> getCurrentCameraPipelineNicknames() {
|
||||
return currentUIVisionProcess.pipelineManager.pipelines.stream().map(cvPipeline -> cvPipeline.settings.nickname).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
public static void saveAllCameras() {
|
||||
visionProcesses.forEach((vpm) -> {
|
||||
VisionProcess process = vpm.visionProcess;
|
||||
String cameraName = process.getCamera().getProperties().name;
|
||||
List<CVPipelineSettings> pipelines = process.pipelineManager.pipelines.stream().map(cvPipeline -> cvPipeline.settings).collect(Collectors.toList());
|
||||
CVPipelineSettings driverMode = process.getDriverModeSettings();
|
||||
CameraJsonConfig config = CameraJsonConfig.fromVisionProcess(process);
|
||||
ConfigManager.saveCameraPipelines(cameraName, pipelines);
|
||||
ConfigManager.saveCameraDriverMode(cameraName, driverMode);
|
||||
ConfigManager.saveCameraConfig(cameraName, config);
|
||||
});
|
||||
}
|
||||
|
||||
private static String getCurrentCameraName() {
|
||||
return currentUIVisionProcess.getCamera().getProperties().name;
|
||||
}
|
||||
|
||||
public static void saveCurrentCameraSettings() {
|
||||
CameraJsonConfig config = CameraJsonConfig.fromVisionProcess(currentUIVisionProcess);
|
||||
ConfigManager.saveCameraConfig(getCurrentCameraName(), config);
|
||||
}
|
||||
|
||||
public static void saveCurrentCameraPipelines() {
|
||||
currentUIVisionProcess.pipelineManager.saveAllPipelines();
|
||||
}
|
||||
|
||||
public static void saveCurrentCameraDriverMode() {
|
||||
currentUIVisionProcess.pipelineManager.saveDriverModeConfig();
|
||||
}
|
||||
|
||||
private static List<HashMap> getCameraResolutionList(USBCameraCapture capture) {
|
||||
return capture.getProperties().getVideoModes().stream().map(Helpers::VideoModeToHashMap).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static List<HashMap> getCurrentCameraResolutionList() {
|
||||
return getCameraResolutionList(currentUIVisionProcess.getCamera());
|
||||
}
|
||||
|
||||
public static int getCurrentUIVisionProcessIndex() {
|
||||
VisionProcessManageable vpm = visionProcesses.stream().filter(v -> v.visionProcess == currentUIVisionProcess).findFirst().orElse(null);
|
||||
return vpm != null ? vpm.index : -1;
|
||||
}
|
||||
}
|
||||
@@ -1,389 +0,0 @@
|
||||
package com.chameleonvision.vision;
|
||||
|
||||
import com.chameleonvision.Debug;
|
||||
import com.chameleonvision.config.CameraCalibrationConfig;
|
||||
import com.chameleonvision.config.CameraConfig;
|
||||
import com.chameleonvision.config.ConfigManager;
|
||||
import com.chameleonvision.config.FullCameraConfiguration;
|
||||
import com.chameleonvision.networktables.NetworkTablesManager;
|
||||
import com.chameleonvision.scripting.ScriptEventType;
|
||||
import com.chameleonvision.scripting.ScriptManager;
|
||||
import com.chameleonvision.util.MathHandler;
|
||||
import com.chameleonvision.vision.camera.CameraStreamer;
|
||||
import com.chameleonvision.vision.camera.USBCameraCapture;
|
||||
import com.chameleonvision.vision.pipeline.CVPipelineResult;
|
||||
import com.chameleonvision.vision.pipeline.CVPipelineSettings;
|
||||
import com.chameleonvision.vision.pipeline.PipelineManager;
|
||||
import com.chameleonvision.vision.pipeline.impl.DriverVisionPipeline;
|
||||
import com.chameleonvision.vision.pipeline.impl.StandardCVPipeline;
|
||||
import com.chameleonvision.vision.pipeline.impl.StandardCVPipelineSettings;
|
||||
import com.chameleonvision.web.SocketHandler;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import edu.wpi.cscore.VideoMode;
|
||||
import edu.wpi.first.networktables.*;
|
||||
import edu.wpi.first.wpilibj.geometry.Pose2d;
|
||||
import edu.wpi.first.wpiutil.CircularBuffer;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class VisionProcess {
|
||||
|
||||
public final USBCameraCapture cameraCapture;
|
||||
private final VisionProcessRunnable visionRunnable;
|
||||
private final CameraConfig fileConfig;
|
||||
public final CameraStreamer cameraStreamer;
|
||||
public PipelineManager pipelineManager;
|
||||
|
||||
private volatile CVPipelineResult lastPipelineResult;
|
||||
|
||||
// network table stuff
|
||||
private final NetworkTable defaultTable;
|
||||
private NetworkTableInstance tableInstance;
|
||||
private NetworkTableEntry ntPipelineEntry;
|
||||
public NetworkTableEntry ntDriverModeEntry;
|
||||
private int ntDriveModeListenerID;
|
||||
private int ntPipelineListenerID;
|
||||
private NetworkTableEntry ntYawEntry;
|
||||
private NetworkTableEntry ntPitchEntry;
|
||||
private NetworkTableEntry ntAuxListEntry;
|
||||
private NetworkTableEntry ntAreaEntry;
|
||||
private NetworkTableEntry ntLatencyEntry;
|
||||
private NetworkTableEntry ntValidEntry;
|
||||
private NetworkTableEntry ntPoseEntry;
|
||||
private NetworkTableEntry ntFittedHeightEntry;
|
||||
private NetworkTableEntry ntFittedWidthEntry;
|
||||
private NetworkTableEntry ntBoundingHeightEntry;
|
||||
private NetworkTableEntry ntBoundingWidthEntry;
|
||||
private NetworkTableEntry ntTargetRotation;
|
||||
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
private long lastUIUpdateMs = 0;
|
||||
|
||||
VisionProcess(USBCameraCapture cameraCapture, FullCameraConfiguration config) {
|
||||
this.cameraCapture = cameraCapture;
|
||||
|
||||
fileConfig = config.fileConfig;
|
||||
|
||||
pipelineManager = new PipelineManager(this, config.pipelines);
|
||||
|
||||
// Thread to put frames on the dashboard
|
||||
this.cameraStreamer = new CameraStreamer(cameraCapture, config.cameraConfig.name, pipelineManager.getCurrentPipeline().settings.streamDivisor);
|
||||
|
||||
// Thread to process vision data
|
||||
this.visionRunnable = new VisionProcessRunnable();
|
||||
|
||||
// network table
|
||||
defaultTable = NetworkTableInstance.getDefault().getTable("/chameleon-vision/" + cameraCapture.getProperties().getNickname());
|
||||
}
|
||||
|
||||
public void start() {
|
||||
System.out.printf("[%s Process] Creating network table...\n", getCamera().getProperties().getNickname());
|
||||
initNT(defaultTable);
|
||||
|
||||
System.out.printf("[%s Process] Starting vision thread...\n", getCamera().getProperties().getNickname());
|
||||
var visionThread = new Thread(visionRunnable);
|
||||
visionThread.setName(getCamera().getProperties().name + " - Vision Thread");
|
||||
visionThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the old value change listeners
|
||||
* calls {@link #initNT}
|
||||
*
|
||||
* @param newTable passed to {@link #initNT}
|
||||
*/
|
||||
public void resetNT(NetworkTable newTable) {
|
||||
ntDriverModeEntry.removeListener(ntDriveModeListenerID);
|
||||
ntPipelineEntry.removeListener(ntPipelineListenerID);
|
||||
initNT(newTable);
|
||||
}
|
||||
|
||||
public void setCameraNickname(String newName) {
|
||||
getCamera().getProperties().setNickname(newName);
|
||||
NetworkTable camTable = NetworkTablesManager.kRootTable.getSubTable(newName);
|
||||
resetNT(camTable);
|
||||
}
|
||||
|
||||
private void initNT(NetworkTable camTable) {
|
||||
tableInstance = camTable.getInstance();
|
||||
ntPipelineEntry = camTable.getEntry("pipeline");
|
||||
ntDriverModeEntry = camTable.getEntry("driverMode");
|
||||
ntPitchEntry = camTable.getEntry("targetPitch");
|
||||
ntYawEntry = camTable.getEntry("targetYaw");
|
||||
ntAreaEntry = camTable.getEntry("targetArea");
|
||||
ntLatencyEntry = camTable.getEntry("latency");
|
||||
ntValidEntry = camTable.getEntry("isValid");
|
||||
ntAuxListEntry = camTable.getEntry("auxTargets");
|
||||
ntPoseEntry = camTable.getEntry("targetPose");
|
||||
ntFittedHeightEntry = camTable.getEntry("targetFittedHeight");
|
||||
ntFittedWidthEntry = camTable.getEntry("targetFittedWidth");
|
||||
ntBoundingHeightEntry = camTable.getEntry("targetBoundingHeight");
|
||||
ntBoundingWidthEntry = camTable.getEntry("targetBoundingWidth");
|
||||
ntTargetRotation = camTable.getEntry("targetRotation");
|
||||
ntDriveModeListenerID = ntDriverModeEntry.addListener(this::setDriverMode, EntryListenerFlags.kUpdate);
|
||||
ntPipelineListenerID = ntPipelineEntry.addListener(this::setPipeline, EntryListenerFlags.kUpdate);
|
||||
ntDriverModeEntry.setBoolean(false);
|
||||
ntPipelineEntry.setNumber(pipelineManager.getCurrentPipelineIndex());
|
||||
pipelineManager.ntIndexEntry = ntPipelineEntry;
|
||||
}
|
||||
|
||||
private void setDriverMode(EntryNotification driverModeEntryNotification) {
|
||||
setDriverMode(driverModeEntryNotification.value.getBoolean());
|
||||
}
|
||||
|
||||
public void setDriverMode(boolean driverMode) {
|
||||
pipelineManager.setDriverMode(driverMode);
|
||||
ScriptManager.queueEvent(driverMode ? ScriptEventType.kEnterDriverMode : ScriptEventType.kExitDriverMode);
|
||||
SocketHandler.sendFullSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called by the nt entry listener to update the next pipeline.
|
||||
*
|
||||
* @param notification the notification
|
||||
*/
|
||||
private void setPipeline(EntryNotification notification) {
|
||||
var wantedPipelineIndex = (int) notification.value.getDouble();
|
||||
if (pipelineManager.pipelines.size() - 1 < wantedPipelineIndex) {
|
||||
ntPipelineEntry.setDouble(pipelineManager.getCurrentPipelineIndex());
|
||||
} else {
|
||||
pipelineManager.setCurrentPipeline(wantedPipelineIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setDriverModeEntry(boolean isDriverMode) {
|
||||
// if it's null, we haven't even started the program yet, so just return
|
||||
// otherwise, set it.
|
||||
if (ntDriverModeEntry != null) {
|
||||
ntDriverModeEntry.setBoolean(isDriverMode);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateUI(CVPipelineResult data) {
|
||||
// 30 "FPS" update rate
|
||||
long currentMillis = System.currentTimeMillis();
|
||||
if (currentMillis - lastUIUpdateMs > 1000 / 30) {
|
||||
lastUIUpdateMs = currentMillis;
|
||||
|
||||
|
||||
if (cameraCapture.getProperties().name.equals(ConfigManager.settings.currentCamera)) {
|
||||
HashMap<String, Object> WebSend = new HashMap<>();
|
||||
HashMap<String, Object> point = new HashMap<>();
|
||||
HashMap<String, Object> pointMap = new HashMap<>();
|
||||
ArrayList<Object> webTargets = new ArrayList<>();
|
||||
List<Double> center = new ArrayList<>();
|
||||
|
||||
|
||||
if (data.hasTarget) {
|
||||
if (data instanceof StandardCVPipeline.StandardCVPipelineResult) {
|
||||
StandardCVPipeline.StandardCVPipelineResult result = (StandardCVPipeline.StandardCVPipelineResult) data;
|
||||
StandardCVPipeline.TrackedTarget bestTarget = result.targets.get(0);
|
||||
try {
|
||||
if (((StandardCVPipelineSettings) pipelineManager.getCurrentPipeline().settings).multiple) {
|
||||
for (var target : result.targets) {
|
||||
pointMap = new HashMap<>();
|
||||
pointMap.put("pitch", target.pitch);
|
||||
pointMap.put("yaw", target.yaw);
|
||||
pointMap.put("area", target.area);
|
||||
pointMap.put("pose", target.cameraRelativePose);
|
||||
webTargets.add(pointMap);
|
||||
}
|
||||
} else {
|
||||
pointMap.put("pitch", bestTarget.pitch);
|
||||
pointMap.put("yaw", bestTarget.yaw);
|
||||
pointMap.put("area", bestTarget.area);
|
||||
pointMap.put("pose", bestTarget.cameraRelativePose);
|
||||
webTargets.add(pointMap);
|
||||
}
|
||||
center.add(bestTarget.minAreaRect.center.x);
|
||||
center.add(bestTarget.minAreaRect.center.y);
|
||||
} catch (ClassCastException ignored) {
|
||||
|
||||
}
|
||||
} else {
|
||||
pointMap.put("pitch", null);
|
||||
pointMap.put("yaw", null);
|
||||
pointMap.put("area", null);
|
||||
pointMap.put("pose", new Pose2d());
|
||||
webTargets.add(pointMap);
|
||||
center.add(null);
|
||||
center.add(null);
|
||||
}
|
||||
|
||||
point.put("fps", visionRunnable.fps);
|
||||
point.put("targets", webTargets);
|
||||
point.put("rawPoint", center);
|
||||
} else {
|
||||
point.put("fps", visionRunnable.fps);
|
||||
}
|
||||
WebSend.put("point", point);
|
||||
SocketHandler.broadcastMessage(WebSend);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateNetworkTableData(CVPipelineResult data) {
|
||||
ntValidEntry.setBoolean(data.hasTarget);
|
||||
if (data.hasTarget && !(data instanceof DriverVisionPipeline.DriverPipelineResult)) {
|
||||
if (data instanceof StandardCVPipeline.StandardCVPipelineResult) {
|
||||
|
||||
//noinspection unchecked
|
||||
List<StandardCVPipeline.TrackedTarget> targets = (List<StandardCVPipeline.TrackedTarget>) data.targets;
|
||||
StandardCVPipeline.TrackedTarget bestTarget = targets.get(0);
|
||||
ntLatencyEntry.setDouble(MathHandler.roundTo(data.processTime * 1e-6, 3));
|
||||
ntPitchEntry.setDouble(bestTarget.pitch);
|
||||
ntYawEntry.setDouble(bestTarget.yaw);
|
||||
ntAreaEntry.setDouble(bestTarget.area);
|
||||
ntBoundingHeightEntry.setDouble(bestTarget.boundingRect.height);
|
||||
ntBoundingWidthEntry.setDouble(bestTarget.boundingRect.width);
|
||||
ntFittedHeightEntry.setDouble(bestTarget.minAreaRect.size.height);
|
||||
ntFittedWidthEntry.setDouble(bestTarget.minAreaRect.size.width);
|
||||
ntTargetRotation.setDouble(bestTarget.minAreaRect.angle);
|
||||
try {
|
||||
Pose2d targetPose = targets.get(0).cameraRelativePose;
|
||||
double[] targetArray = {targetPose.getTranslation().getX(), targetPose.getTranslation().getY(), targetPose.getRotation().getDegrees()};
|
||||
ntPoseEntry.setDoubleArray(targetArray);
|
||||
// ntPoseEntry.setString(objectMapper.writeValueAsString(targets.get(0).cameraRelativePose));
|
||||
ntAuxListEntry.setString(objectMapper.writeValueAsString(targets.stream()
|
||||
.map(it -> List.of(it.pitch, it.yaw, it.area, it.boundingRect.width, it.boundingRect.height, it.minAreaRect.size.width, it.minAreaRect.size.height, it.minAreaRect.angle, it.cameraRelativePose))
|
||||
.collect(Collectors.toList())));
|
||||
} catch (JsonProcessingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
ntPitchEntry.setDouble(0.0);
|
||||
ntYawEntry.setDouble(0.0);
|
||||
ntAreaEntry.setDouble(0.0);
|
||||
ntLatencyEntry.setDouble(0.0);
|
||||
ntAuxListEntry.setString("");
|
||||
}
|
||||
}
|
||||
tableInstance.flush();
|
||||
}
|
||||
|
||||
public void setVideoMode(VideoMode newMode) {
|
||||
cameraCapture.setVideoMode(newMode);
|
||||
cameraStreamer.setNewVideoMode(newMode);
|
||||
}
|
||||
|
||||
public VideoMode getCurrentVideoMode() {
|
||||
return cameraCapture.getCurrentVideoMode();
|
||||
}
|
||||
|
||||
public List<VideoMode> getPossibleVideoModes() {
|
||||
return cameraCapture.getProperties().videoModes;
|
||||
}
|
||||
|
||||
public USBCameraCapture getCamera() {
|
||||
return cameraCapture;
|
||||
}
|
||||
|
||||
public CVPipelineSettings getDriverModeSettings() {
|
||||
return pipelineManager.driverModePipeline.settings;
|
||||
}
|
||||
|
||||
public void addCalibration(CameraCalibrationConfig cal) {
|
||||
cameraCapture.addCalibrationData(cal);
|
||||
System.out.println("saving to file");
|
||||
fileConfig.saveCalibration(cameraCapture.getAllCalibrationData());
|
||||
}
|
||||
|
||||
public void setIs3d(Boolean value) {
|
||||
var settings = pipelineManager.getCurrentPipeline().settings;
|
||||
if (settings instanceof StandardCVPipelineSettings) {
|
||||
((StandardCVPipelineSettings) settings).is3D = value;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getIs3d() {
|
||||
var settings = pipelineManager.getCurrentPipeline().settings;
|
||||
if (settings instanceof StandardCVPipelineSettings) {
|
||||
return ((StandardCVPipelineSettings) settings).is3D;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* VisionProcessRunnable will process images as quickly as possible
|
||||
*/
|
||||
private class VisionProcessRunnable implements Runnable {
|
||||
|
||||
volatile Double fps = 0.0;
|
||||
private CircularBuffer fpsAveragingBuffer = new CircularBuffer(7);
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
var lastUpdateTimeNanos = System.nanoTime();
|
||||
var lastStreamTimeMs = System.currentTimeMillis();
|
||||
|
||||
System.out.printf("[%s Process] Vision Process Thread -- first run!\n", getCamera().getProperties().getNickname());
|
||||
|
||||
while (!Thread.interrupted()) {
|
||||
|
||||
// blocking call, will block until camera has a new frame.
|
||||
Pair<Mat, Long> camData = cameraCapture.getFrame();
|
||||
|
||||
Mat camFrame = camData.getLeft();
|
||||
if (camFrame.cols() > 0 && camFrame.rows() > 0) {
|
||||
CVPipelineResult result = null;
|
||||
try {
|
||||
result = pipelineManager.getCurrentPipeline().runPipeline(camFrame);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Exception in vision process " + getCamera().getProperties().getNickname() + "!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
camFrame.release();
|
||||
|
||||
if (result != null) {
|
||||
result.setTimestamp(camData.getRight());
|
||||
lastPipelineResult = result;
|
||||
updateNetworkTableData(lastPipelineResult);
|
||||
updateUI(lastPipelineResult);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
var currentTime = System.currentTimeMillis();
|
||||
if ((currentTime - lastStreamTimeMs) / 1000d > 1.0 / 30.0) {
|
||||
if(lastPipelineResult != null) {
|
||||
cameraStreamer.runStream(lastPipelineResult.outputMat);
|
||||
lastStreamTimeMs = currentTime;
|
||||
lastPipelineResult.outputMat.release();
|
||||
} else {
|
||||
System.err.printf("[%s Process] Last pipeline result was null!\n", getCamera().getProperties().getNickname());
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
// Debug.printInfo("Vision running faster than stream.");
|
||||
System.err.printf("[%s Process] Exception in vision thread!\n", getCamera().getProperties().getNickname());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
var deltaTimeNanos = System.nanoTime() - lastUpdateTimeNanos;
|
||||
fpsAveragingBuffer.addFirst(1.0 / (deltaTimeNanos * 1E-09));
|
||||
lastUpdateTimeNanos = System.nanoTime();
|
||||
fps = getAverageFPS();
|
||||
}
|
||||
}
|
||||
|
||||
double getAverageFPS() {
|
||||
var temp = 0.0;
|
||||
for (int i = 0; i < 7; i++) {
|
||||
temp += fpsAveragingBuffer.get(i);
|
||||
}
|
||||
temp /= 7.0;
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package com.chameleonvision.vision.camera;
|
||||
|
||||
import com.chameleonvision.config.CameraCalibrationConfig;
|
||||
import com.chameleonvision.vision.image.CaptureProperties;
|
||||
import com.chameleonvision.vision.image.ImageCapture;
|
||||
import edu.wpi.cscore.VideoMode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface CameraCapture extends ImageCapture {
|
||||
CaptureProperties getProperties();
|
||||
|
||||
VideoMode getCurrentVideoMode();
|
||||
|
||||
/**
|
||||
* Set the exposure of the camera
|
||||
* @param exposure the new exposure to set the camera to
|
||||
*/
|
||||
void setExposure(int exposure);
|
||||
|
||||
/**
|
||||
* Set the brightness of the camera
|
||||
* @param brightness the new brightness to set the camera to
|
||||
*/
|
||||
void setBrightness(int brightness);
|
||||
|
||||
/**
|
||||
* Set the video mode (fps and resolution) of the camera
|
||||
* @param mode the desired mode
|
||||
*/
|
||||
void setVideoMode(VideoMode mode);
|
||||
|
||||
/**
|
||||
* Set the video mode (fps and resolution) of the camera
|
||||
* @param index the index of the desired mode
|
||||
*/
|
||||
void setVideoMode(int index);
|
||||
|
||||
/**
|
||||
* Set the gain of the camera
|
||||
* NOTE - Not all cameras support this.
|
||||
* @param gain the new gain to set the camera to
|
||||
*/
|
||||
void setGain(int gain);
|
||||
|
||||
CameraCalibrationConfig getCurrentCalibrationData();
|
||||
List<CameraCalibrationConfig> getAllCalibrationData();
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
package com.chameleonvision.vision.camera;
|
||||
|
||||
import com.chameleonvision.vision.enums.StreamDivisor;
|
||||
import com.chameleonvision.web.SocketHandler;
|
||||
import edu.wpi.cscore.CvSource;
|
||||
import edu.wpi.cscore.MjpegServer;
|
||||
import edu.wpi.cscore.VideoMode;
|
||||
import edu.wpi.first.cameraserver.CameraServer;
|
||||
import org.opencv.core.CvType;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Size;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
|
||||
public class CameraStreamer {
|
||||
private final CameraCapture cameraCapture;
|
||||
private final String name;
|
||||
private StreamDivisor divisor;
|
||||
private CvSource cvSource;
|
||||
private final Object streamBufferLock = new Object();
|
||||
private Mat streamBuffer = new Mat();
|
||||
private Size size;
|
||||
|
||||
public CameraStreamer(CameraCapture cameraCapture, String name,StreamDivisor div) {
|
||||
this.divisor = div;
|
||||
this.cameraCapture = cameraCapture;
|
||||
this.name = name;
|
||||
this.cvSource = CameraServer.getInstance().putVideo(name,
|
||||
cameraCapture.getProperties().getStaticProperties().imageWidth / divisor.value,
|
||||
cameraCapture.getProperties().getStaticProperties().imageHeight / divisor.value);
|
||||
//noinspection IntegerDivisionInFloatingPointContext
|
||||
this.size = new Size(
|
||||
cameraCapture.getProperties().getStaticProperties().imageWidth / divisor.value,
|
||||
cameraCapture.getProperties().getStaticProperties().imageHeight / divisor.value
|
||||
);
|
||||
setDivisor(divisor, false);
|
||||
}
|
||||
|
||||
public void setDivisor(StreamDivisor newDivisor, boolean updateUI) {
|
||||
this.divisor = newDivisor;
|
||||
var camValues = cameraCapture.getProperties();
|
||||
var newWidth = camValues.getStaticProperties().imageWidth / newDivisor.value;
|
||||
var newHeight = camValues.getStaticProperties().imageHeight / newDivisor.value;
|
||||
this.size = new Size(newWidth, newHeight);
|
||||
synchronized (streamBufferLock) {
|
||||
this.streamBuffer = new Mat(newWidth, newHeight, CvType.CV_8UC3);
|
||||
VideoMode oldVideoMode = cvSource.getVideoMode();
|
||||
cvSource.setVideoMode(new VideoMode(oldVideoMode.pixelFormat,
|
||||
cameraCapture.getProperties().getStaticProperties().imageWidth / divisor.value,
|
||||
cameraCapture.getProperties().getStaticProperties().imageHeight / divisor.value,
|
||||
oldVideoMode.fps));
|
||||
}
|
||||
if (updateUI) {
|
||||
SocketHandler.sendFullSettings();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public StreamDivisor getDivisor() {
|
||||
return divisor;
|
||||
}
|
||||
|
||||
public void recalculateDivision() {
|
||||
setDivisor(this.divisor, false);
|
||||
}
|
||||
|
||||
public void setNewVideoMode(VideoMode newVideoMode) {
|
||||
// Trick to update cvSource and streamBuffer to the new resolution
|
||||
// Must change the cameraProcess resolution first
|
||||
setDivisor(divisor, true);
|
||||
}
|
||||
|
||||
public int getStreamPort() {
|
||||
var s = (MjpegServer) CameraServer.getInstance().getServer("serve_" + name);
|
||||
return s.getPort();
|
||||
}
|
||||
|
||||
public void runStream(Mat image) {
|
||||
synchronized (streamBufferLock) {
|
||||
image.copyTo(streamBuffer);
|
||||
}
|
||||
|
||||
if (divisor.value != 1) {
|
||||
// var camVal = cameraProcess.getProperties().staticProperties;
|
||||
// var newWidth = camVal.imageWidth / divisor.value;
|
||||
// var newHeight = camVal.imageHeight / divisor.value;
|
||||
// Size newSize = new Size(newWidth, newHeight);
|
||||
Imgproc.resize(streamBuffer, streamBuffer, this.size);
|
||||
}
|
||||
|
||||
var sourceVideoMode = cvSource.getVideoMode();
|
||||
var imageSize = streamBuffer.size();
|
||||
if(sourceVideoMode.width != (int) imageSize.width || sourceVideoMode.height != (int) imageSize.height) {
|
||||
synchronized (streamBufferLock) {
|
||||
cvSource.setVideoMode(new VideoMode(sourceVideoMode.pixelFormat,
|
||||
(int)imageSize.width,
|
||||
(int) imageSize.height,
|
||||
sourceVideoMode.fps));
|
||||
}
|
||||
}
|
||||
|
||||
cvSource.putFrame(streamBuffer);
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package com.chameleonvision.vision.camera;
|
||||
|
||||
import edu.wpi.cscore.VideoMode;
|
||||
import org.apache.commons.math3.fraction.Fraction;
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
|
||||
public class CaptureStaticProperties {
|
||||
|
||||
public final int imageWidth;
|
||||
public final int imageHeight;
|
||||
public final double fov;
|
||||
public final double imageArea;
|
||||
public final double centerX;
|
||||
public final double centerY;
|
||||
public final double horizontalFocalLength;
|
||||
public final double verticalFocalLength;
|
||||
public final VideoMode mode;
|
||||
|
||||
public CaptureStaticProperties(VideoMode mode, double fov) {
|
||||
this.mode = mode;
|
||||
this.imageWidth = mode.width;
|
||||
this.imageHeight = mode.height;
|
||||
this.fov = fov;
|
||||
imageArea = this.imageWidth * this.imageHeight;
|
||||
centerX = ((double) this.imageWidth / 2) - 0.5;
|
||||
centerY = ((double) this.imageHeight / 2) - 0.5;
|
||||
|
||||
// pinhole model calculations
|
||||
double diagonalView = FastMath.toRadians(this.fov);
|
||||
Fraction aspectFraction = new Fraction(this.imageWidth, this.imageHeight);
|
||||
int horizontalRatio = aspectFraction.getNumerator();
|
||||
int verticalRatio = aspectFraction.getDenominator();
|
||||
double diagonalAspect = FastMath.hypot(horizontalRatio, verticalRatio);
|
||||
double horizontalView = FastMath.atan(FastMath.tan(diagonalView / 2) * (horizontalRatio / diagonalAspect)) * 2;
|
||||
double verticalView = FastMath.atan(FastMath.tan(diagonalView / 2) * (verticalRatio / diagonalAspect)) * 2;
|
||||
horizontalFocalLength = this.imageWidth / (2 * FastMath.tan(horizontalView /2));
|
||||
verticalFocalLength = this.imageHeight / (2 * FastMath.tan(verticalView /2));
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
package com.chameleonvision.vision.camera;
|
||||
|
||||
import com.chameleonvision.config.CameraCalibrationConfig;
|
||||
import com.chameleonvision.config.FullCameraConfiguration;
|
||||
import com.chameleonvision.util.Helpers;
|
||||
import edu.wpi.cscore.CvSink;
|
||||
import edu.wpi.cscore.UsbCamera;
|
||||
import edu.wpi.cscore.VideoException;
|
||||
import edu.wpi.cscore.VideoMode;
|
||||
import edu.wpi.first.cameraserver.CameraServer;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Size;
|
||||
import org.opencv.imgcodecs.Imgcodecs;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class USBCameraCapture implements CameraCapture {
|
||||
private final UsbCamera baseCamera;
|
||||
private final CvSink cvSink;
|
||||
private List<CameraCalibrationConfig> calibrationList;
|
||||
private Mat imageBuffer = new Mat();
|
||||
private USBCaptureProperties properties;
|
||||
|
||||
public USBCameraCapture(FullCameraConfiguration fullCameraConfiguration) {
|
||||
var config = fullCameraConfiguration.cameraConfig;
|
||||
this.calibrationList = new ArrayList<>(); //fullCameraConfiguration.calibration;
|
||||
calibrationList.addAll(fullCameraConfiguration.calibration);
|
||||
baseCamera = new UsbCamera(config.name, config.path);
|
||||
cvSink = CameraServer.getInstance().getVideo(baseCamera);
|
||||
try {
|
||||
properties = new USBCaptureProperties(baseCamera, config);
|
||||
} catch(VideoException e) {
|
||||
System.err.println("Camera cannot be found on the saved USB port!" +
|
||||
" Ensure that the camera has not been plugged into a different USB port, and if so, correct it.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
var videoModes = properties.getVideoModes();
|
||||
if(videoModes.size() < 1) {
|
||||
throw new VideoException("0 video modes are valid! Full list provided by camera: \n\n"
|
||||
+ Arrays.stream(baseCamera.enumerateVideoModes()).map(Helpers::VideoModeToHashMap).toString() );
|
||||
}
|
||||
|
||||
int videoMode = properties.videoModes.size() - 1 <= config.videomode ? config.videomode : 0;
|
||||
setVideoMode(videoMode);
|
||||
}
|
||||
|
||||
public CameraCalibrationConfig getCalibration(Size size) {
|
||||
for(var calibration: calibrationList) {
|
||||
if(calibration.resolution.equals(size)) return calibration;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public CameraCalibrationConfig getCalibration(VideoMode mode) {
|
||||
return getCalibration(new Size(mode.width, mode.height));
|
||||
}
|
||||
|
||||
public void addCalibrationData(CameraCalibrationConfig newConfig) {
|
||||
calibrationList.removeIf(c -> newConfig.resolution.height == c.resolution.height && newConfig.resolution.width == c.resolution.width);
|
||||
calibrationList.add(newConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public USBCaptureProperties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VideoMode getCurrentVideoMode() {
|
||||
return baseCamera.getVideoMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<Mat, Long> getFrame() {
|
||||
Long deltaTime;
|
||||
// TODO: Why multiply by 1000 here?
|
||||
Mat tempMat = new Mat();
|
||||
deltaTime = cvSink.grabFrame(tempMat) * 1000L;
|
||||
// tempMat = Imgcodecs.imread("C:\\Users\\imadu\\Documents\\GitHub\\chameleon-vision\\chameleon-server\\testimages\\2020\\image.png");
|
||||
tempMat.copyTo(imageBuffer);
|
||||
tempMat.release();
|
||||
return Pair.of(imageBuffer, deltaTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExposure(int exposure) {
|
||||
try {
|
||||
baseCamera.setExposureManual(exposure);
|
||||
} catch (VideoException e) {
|
||||
System.err.println("Failed to change camera exposure!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBrightness(int brightness) {
|
||||
try {
|
||||
baseCamera.setBrightness(brightness);
|
||||
} catch (VideoException e) {
|
||||
System.err.println("Failed to change camera brightness!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVideoMode(VideoMode mode) {
|
||||
try {
|
||||
baseCamera.setVideoMode(mode);
|
||||
properties.updateVideoMode(mode);
|
||||
} catch (VideoException e) {
|
||||
System.err.println("Failed to change camera video mode!");
|
||||
}
|
||||
}
|
||||
|
||||
public void setVideoMode(int index){
|
||||
VideoMode mode = properties.getVideoModes().get(index);
|
||||
setVideoMode(mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGain(int gain) {
|
||||
if (properties.isPS3Eye) {
|
||||
try {
|
||||
baseCamera.getProperty("gain_automatic").set(0);
|
||||
baseCamera.getProperty("gain").set(gain);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to change camera gain!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraCalibrationConfig getCurrentCalibrationData() {
|
||||
return getCalibration(getCurrentVideoMode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CameraCalibrationConfig> getAllCalibrationData() {
|
||||
return calibrationList;
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
package com.chameleonvision.vision.camera;
|
||||
|
||||
import com.chameleonvision.config.CameraJsonConfig;
|
||||
import com.chameleonvision.util.Platform;
|
||||
import com.chameleonvision.vision.image.CaptureProperties;
|
||||
import edu.wpi.cscore.UsbCamera;
|
||||
import edu.wpi.cscore.VideoMode;
|
||||
import edu.wpi.first.wpilibj.geometry.Rotation2d;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class USBCaptureProperties extends CaptureProperties {
|
||||
public static final double DEFAULT_FOV = 70;
|
||||
private static final int DEFAULT_EXPOSURE = 50;
|
||||
private static final int DEFAULT_BRIGHTNESS = 50;
|
||||
private static final int MINIMUM_FPS = 21;
|
||||
private static final int MINIMUM_WIDTH = 320;
|
||||
private static final int MINIMUM_HEIGHT = 200;
|
||||
private static final int MAX_INIT_MS = 1500;
|
||||
|
||||
private static final int PS3EYE_VID = 0x1415;
|
||||
private static final int PS3EYE_PID = 0x2000;
|
||||
|
||||
private static final List<VideoMode.PixelFormat> ALLOWED_PIXEL_FORMATS = Arrays.asList(VideoMode.PixelFormat.kYUYV, VideoMode.PixelFormat.kMJPEG, VideoMode.PixelFormat.kBGR);
|
||||
|
||||
private static final Predicate<VideoMode> kMinFPSPredicate = (videoMode -> videoMode.fps >= MINIMUM_FPS);
|
||||
private static final Predicate<VideoMode> kMinSizePredicate = (videoMode -> videoMode.width >= MINIMUM_WIDTH && videoMode.height >= MINIMUM_HEIGHT);
|
||||
private static final Predicate<VideoMode> kPixelFormatPredicate = (videoMode -> ALLOWED_PIXEL_FORMATS.contains(videoMode.pixelFormat));
|
||||
|
||||
public final String name;
|
||||
public final String path;
|
||||
public final List<VideoMode> videoModes;
|
||||
|
||||
private final UsbCamera baseCamera;
|
||||
public final boolean isPS3Eye;
|
||||
|
||||
private String nickname;
|
||||
private double FOV;
|
||||
|
||||
USBCaptureProperties(UsbCamera baseCamera, CameraJsonConfig config) {
|
||||
FOV = config.fov;
|
||||
name = config.name;
|
||||
path = config.path;
|
||||
setTilt(Rotation2d.fromDegrees(config.tilt));
|
||||
nickname = config.nickname;
|
||||
this.baseCamera = baseCamera;
|
||||
|
||||
int usbVID = baseCamera.getInfo().vendorId;
|
||||
int usbPID = baseCamera.getInfo().productId;
|
||||
|
||||
// wait for camera USB init on Windows, Windows USB is slow...
|
||||
if (Platform.CurrentPlatform == Platform.WINDOWS_64 && !baseCamera.isConnected()) {
|
||||
System.out.print("Waiting on camera... ");
|
||||
long initTimeout = System.nanoTime();
|
||||
while (!baseCamera.isConnected()) {
|
||||
if (((System.nanoTime() - initTimeout) / 1e6) >= MAX_INIT_MS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
var initTimeMs = (System.nanoTime() - initTimeout) / 1e6;
|
||||
System.out.printf("USBCameraProcess initialized in %.2fms\n", initTimeMs);
|
||||
}
|
||||
|
||||
isPS3Eye = (usbVID == PS3EYE_VID && usbPID == PS3EYE_PID);
|
||||
videoModes = filterVideoModes(baseCamera.enumerateVideoModes());
|
||||
}
|
||||
|
||||
public void setFOV(double FOV) {
|
||||
if (this.FOV != FOV) {
|
||||
this.FOV = FOV;
|
||||
staticProperties = new CaptureStaticProperties(staticProperties.mode, FOV);
|
||||
}
|
||||
}
|
||||
|
||||
public double getFOV() {
|
||||
return FOV;
|
||||
}
|
||||
|
||||
public void setNickname(String nickname) {
|
||||
this.nickname = nickname;
|
||||
}
|
||||
|
||||
public String getNickname() {
|
||||
return nickname;
|
||||
}
|
||||
|
||||
private List<VideoMode> filterVideoModes(VideoMode[] videoModes) {
|
||||
Predicate<VideoMode> fullPredicate = kMinFPSPredicate.and(kMinSizePredicate).and(kPixelFormatPredicate);
|
||||
Stream<VideoMode> validModes = Arrays.stream(videoModes).filter(fullPredicate);
|
||||
return validModes.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
void updateVideoMode(VideoMode videoMode) {
|
||||
staticProperties = new CaptureStaticProperties(videoMode, FOV);
|
||||
}
|
||||
|
||||
public List<VideoMode> getVideoModes() {
|
||||
return videoModes;
|
||||
}
|
||||
|
||||
public VideoMode getVideoMode(int index){
|
||||
return videoModes.get(index);
|
||||
}
|
||||
|
||||
public VideoMode getCurrentVideoMode() { return staticProperties.mode; }
|
||||
|
||||
public int getCurrentVideoModeIndex(){
|
||||
return getVideoModes().indexOf(getCurrentVideoMode());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.chameleonvision.vision.enums;
|
||||
|
||||
public enum CalibrationMode {
|
||||
None,Single,Dual
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.chameleonvision.vision.enums;
|
||||
|
||||
public enum ImageFlipMode {
|
||||
NONE(Integer.MIN_VALUE),
|
||||
VERTICAL(1),
|
||||
HORIZONTAL(0),
|
||||
BOTH(-1);
|
||||
|
||||
public final int value;
|
||||
|
||||
ImageFlipMode(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.chameleonvision.vision.enums;
|
||||
|
||||
import org.opencv.core.Core;
|
||||
|
||||
public enum ImageRotationMode {
|
||||
DEG_0(-1),
|
||||
DEG_90(Core.ROTATE_90_CLOCKWISE),
|
||||
DEG_180(Core.ROTATE_180),
|
||||
DEG_270(Core.ROTATE_90_COUNTERCLOCKWISE);
|
||||
|
||||
public final int value;
|
||||
|
||||
ImageRotationMode(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public boolean isRotated(){return this.value==DEG_90.value||this.value==DEG_270.value;}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.chameleonvision.vision.enums;
|
||||
|
||||
public enum SortMode {
|
||||
Largest,Smallest,Highest,Lowest,Rightmost,Leftmost,Centermost
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.chameleonvision.vision.enums;
|
||||
|
||||
public enum StreamDivisor {
|
||||
NONE(1),
|
||||
HALF(2),
|
||||
QUARTER(4),
|
||||
SIXTH(6);
|
||||
|
||||
public final Integer value;
|
||||
|
||||
StreamDivisor(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package com.chameleonvision.vision.enums;
|
||||
|
||||
public enum TargetGroup {
|
||||
Single,
|
||||
Dual
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.chameleonvision.vision.enums;
|
||||
|
||||
public enum TargetIntersection {
|
||||
None,Up,Down,Left,Right
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.chameleonvision.vision.enums;
|
||||
|
||||
public enum TargetOrientation {
|
||||
Portrait, Landscape
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.chameleonvision.vision.enums;
|
||||
|
||||
public enum TargetRegion {
|
||||
Center, Top, Bottom, Left, Right
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package com.chameleonvision.vision.image;
|
||||
|
||||
import com.chameleonvision.vision.camera.CaptureStaticProperties;
|
||||
import edu.wpi.cscore.VideoMode;
|
||||
import edu.wpi.first.wpilibj.geometry.Rotation2d;
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
public class CaptureProperties {
|
||||
|
||||
protected CaptureStaticProperties staticProperties;
|
||||
private Rotation2d tilt = new Rotation2d();
|
||||
|
||||
protected CaptureProperties() {
|
||||
}
|
||||
|
||||
public CaptureProperties(VideoMode videoMode, double fov) {
|
||||
staticProperties = new CaptureStaticProperties(videoMode, fov);
|
||||
}
|
||||
public void setStaticProperties(CaptureStaticProperties staticProperties) {this.staticProperties = staticProperties;}
|
||||
public CaptureStaticProperties getStaticProperties() {
|
||||
return staticProperties;
|
||||
}
|
||||
|
||||
public Rotation2d getTilt() {
|
||||
return tilt;
|
||||
}
|
||||
|
||||
public void setTilt(Rotation2d tilt) {
|
||||
this.tilt = tilt;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.chameleonvision.vision.image;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
public interface ImageCapture {
|
||||
/**
|
||||
* Get the next camera frame
|
||||
* @return a Pair of the captured image and the Linux epoch of when the frame was grabbed (in uS)
|
||||
*/
|
||||
Pair<Mat, Long> getFrame();
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
package com.chameleonvision.vision.image;
|
||||
|
||||
import com.chameleonvision.config.CameraCalibrationConfig;
|
||||
import com.chameleonvision.vision.camera.CameraCapture;
|
||||
import com.chameleonvision.vision.camera.USBCaptureProperties;
|
||||
import edu.wpi.cscore.VideoMode;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.imgcodecs.Imgcodecs;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
public class StaticImageCapture implements CameraCapture {
|
||||
|
||||
private Mat image = new Mat();
|
||||
private final VideoMode fakeVideoMode;
|
||||
private final com.chameleonvision.vision.image.CaptureProperties properties;
|
||||
|
||||
public StaticImageCapture(Path imagePath) {
|
||||
this(imagePath, 70);
|
||||
}
|
||||
|
||||
public StaticImageCapture(Path imagePath, double FOV) {
|
||||
if (!Files.exists(imagePath)) throw new RuntimeException("Invalid path for image!");
|
||||
|
||||
Mat loadedImage = Imgcodecs.imread(imagePath.toString());
|
||||
loadedImage.copyTo(image);
|
||||
if (image.cols() > 0 && image.rows() > 0) {
|
||||
fakeVideoMode = new VideoMode(VideoMode.PixelFormat.kBGR, image.cols(), image.rows(), 60);
|
||||
} else {
|
||||
throw new RuntimeException("Failed to load image!");
|
||||
}
|
||||
|
||||
properties = new com.chameleonvision.vision.image.CaptureProperties(fakeVideoMode, FOV);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<Mat, Long> getFrame() {
|
||||
return Pair.of(image, System.nanoTime());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CaptureProperties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VideoMode getCurrentVideoMode() {
|
||||
return fakeVideoMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExposure(int exposure) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBrightness(int brightness) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVideoMode(VideoMode mode) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVideoMode(int index) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGain(int gain) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraCalibrationConfig getCurrentCalibrationData() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CameraCalibrationConfig> getAllCalibrationData() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.chameleonvision.vision.pipeline;
|
||||
|
||||
import com.chameleonvision.vision.camera.CameraCapture;
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <R> Pipeline result type
|
||||
*/
|
||||
public abstract class CVPipeline<R extends CVPipelineResult, S extends CVPipelineSettings> {
|
||||
protected Mat outputMat = new Mat();
|
||||
protected CameraCapture cameraCapture;
|
||||
public S settings;
|
||||
|
||||
protected CVPipeline(S settings) {
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
protected CVPipeline(String pipelineName, S settings) {
|
||||
this.settings = settings;
|
||||
settings.nickname = pipelineName;
|
||||
}
|
||||
|
||||
public void initPipeline(CameraCapture camera) {
|
||||
cameraCapture = camera;
|
||||
cameraCapture.setVideoMode(settings.videoModeIndex);
|
||||
cameraCapture.setExposure((int) settings.exposure);
|
||||
cameraCapture.setBrightness((int) settings.brightness);
|
||||
cameraCapture.setGain((int) settings.gain);
|
||||
}
|
||||
abstract public R runPipeline(Mat inputMat);
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.chameleonvision.vision.pipeline;
|
||||
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class CVPipelineResult<T> {
|
||||
public final List<T> targets;
|
||||
public final boolean hasTarget;
|
||||
public final Mat outputMat = new Mat();
|
||||
public final long processTime;
|
||||
public long imageTimestamp = 0;
|
||||
|
||||
public CVPipelineResult(List<T> targets, Mat outputMat, long processTime) {
|
||||
this.targets = targets;
|
||||
hasTarget = targets != null && !targets.isEmpty();
|
||||
// this.outputMat = outputMat;
|
||||
outputMat.copyTo(this.outputMat);
|
||||
outputMat.release();
|
||||
this.processTime = processTime;
|
||||
}
|
||||
|
||||
public void setTimestamp(long timestamp) {
|
||||
imageTimestamp = timestamp;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.chameleonvision.vision.pipeline;
|
||||
|
||||
import com.chameleonvision.vision.enums.ImageFlipMode;
|
||||
import com.chameleonvision.vision.enums.ImageRotationMode;
|
||||
import com.chameleonvision.vision.enums.StreamDivisor;
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
public class CVPipelineSettings {
|
||||
public int index = 0;
|
||||
public ImageFlipMode flipMode = ImageFlipMode.NONE;
|
||||
public ImageRotationMode rotationMode = ImageRotationMode.DEG_0;
|
||||
public String nickname = "New Pipeline";
|
||||
public double exposure = 50.0;
|
||||
public double brightness = 50.0;
|
||||
public double gain = 0;
|
||||
public int videoModeIndex = 0;
|
||||
public StreamDivisor streamDivisor = StreamDivisor.NONE;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.chameleonvision.vision.pipeline;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
public interface Pipe<I, O> {
|
||||
/**
|
||||
*
|
||||
* @param input Input object for pipe
|
||||
* @return Returns a Pair containing the process time in Nanoseconds,
|
||||
* and the output object
|
||||
*/
|
||||
Pair<O, Long> run(I input);
|
||||
}
|
||||
@@ -1,252 +0,0 @@
|
||||
package com.chameleonvision.vision.pipeline;
|
||||
|
||||
import com.chameleonvision.Exceptions.DuplicatedKeyException;
|
||||
import com.chameleonvision.config.CameraConfig;
|
||||
import com.chameleonvision.config.ConfigManager;
|
||||
import com.chameleonvision.scripting.ScriptEventType;
|
||||
import com.chameleonvision.scripting.ScriptManager;
|
||||
import com.chameleonvision.vision.VisionManager;
|
||||
import com.chameleonvision.vision.VisionProcess;
|
||||
import com.chameleonvision.vision.pipeline.impl.Calibrate3dPipeline;
|
||||
import com.chameleonvision.vision.pipeline.impl.DriverVisionPipeline;
|
||||
import com.chameleonvision.vision.pipeline.impl.StandardCVPipeline;
|
||||
import com.chameleonvision.vision.pipeline.impl.StandardCVPipelineSettings;
|
||||
import com.chameleonvision.web.SocketHandler;
|
||||
import edu.wpi.cscore.VideoMode;
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public class PipelineManager {
|
||||
|
||||
private static final int DRIVERMODE_INDEX = -1;
|
||||
private static final int CAL_3D_INDEX = -2;
|
||||
|
||||
public final LinkedList<CVPipeline> pipelines = new LinkedList<>();
|
||||
|
||||
public final CVPipeline driverModePipeline = new DriverVisionPipeline(new CVPipelineSettings());
|
||||
public final Calibrate3dPipeline calib3dPipe = new Calibrate3dPipeline(new StandardCVPipelineSettings());
|
||||
|
||||
private final VisionProcess parentProcess;
|
||||
private int lastPipelineIndex;
|
||||
private int currentPipelineIndex;
|
||||
public NetworkTableEntry ntIndexEntry;
|
||||
|
||||
public PipelineManager(VisionProcess visionProcess, List<CVPipelineSettings> loadedPipelineSettings) {
|
||||
parentProcess = visionProcess;
|
||||
if (loadedPipelineSettings == null || loadedPipelineSettings.size() == 0) {
|
||||
pipelines.add(new StandardCVPipeline("New Pipeline"));
|
||||
} else {
|
||||
for (CVPipelineSettings setting : loadedPipelineSettings) {
|
||||
addInternalPipeline(setting);
|
||||
}
|
||||
}
|
||||
driverModePipeline.initPipeline(visionProcess.getCamera());
|
||||
setCurrentPipeline(0);
|
||||
}
|
||||
|
||||
private void reassignIndexes() {
|
||||
pipelines.sort(IndexComparator);
|
||||
for (int i = 0; i < pipelines.size(); i++) {
|
||||
pipelines.get(i).settings.index = i;
|
||||
}
|
||||
}
|
||||
|
||||
private CameraConfig getConfig(VisionProcess process) {
|
||||
return VisionManager.getCameraConfig(process);
|
||||
}
|
||||
|
||||
private CameraConfig getConfig() {
|
||||
return getConfig(parentProcess);
|
||||
}
|
||||
|
||||
private void savePipelineConfig(CVPipelineSettings setting) {
|
||||
getConfig().pipelineConfig.save(setting);
|
||||
}
|
||||
|
||||
private void deletePipelineConfig(CVPipelineSettings setting) {
|
||||
getConfig().pipelineConfig.delete(setting);
|
||||
}
|
||||
|
||||
private void renamePipelineConfig(CVPipelineSettings setting, String newName) {
|
||||
getConfig().pipelineConfig.rename(setting, newName);
|
||||
}
|
||||
|
||||
public void saveAllPipelines() {
|
||||
pipelines.parallelStream().map(pipeline -> pipeline.settings).forEach(this::savePipelineConfig);
|
||||
}
|
||||
|
||||
private void addInternalPipeline(CVPipelineSettings setting) {
|
||||
if (setting instanceof StandardCVPipelineSettings) {
|
||||
pipelines.add(new StandardCVPipeline((StandardCVPipelineSettings) setting));
|
||||
} else {
|
||||
System.out.println("Non 2D/3D pipelines not supported!");
|
||||
}
|
||||
reassignIndexes();
|
||||
}
|
||||
|
||||
public void setDriverMode(boolean driverMode) {
|
||||
if (driverMode) setCurrentPipeline(DRIVERMODE_INDEX);
|
||||
else setCurrentPipeline(lastPipelineIndex);
|
||||
}
|
||||
|
||||
public void setCalibrationMode(boolean calibrationMode) {
|
||||
setCurrentPipeline((calibrationMode ? CAL_3D_INDEX : lastPipelineIndex));
|
||||
}
|
||||
|
||||
public void enableCalibrationMode(VideoMode mode) {
|
||||
parentProcess.setVideoMode(mode);
|
||||
calib3dPipe.setVideoMode(mode);
|
||||
setCalibrationMode(true);
|
||||
}
|
||||
|
||||
public boolean getDriverMode() {
|
||||
return currentPipelineIndex == DRIVERMODE_INDEX;
|
||||
}
|
||||
|
||||
public int getCurrentPipelineIndex() {
|
||||
return currentPipelineIndex;
|
||||
}
|
||||
|
||||
public CVPipeline getCurrentPipeline() {
|
||||
if (currentPipelineIndex == DRIVERMODE_INDEX) {
|
||||
return driverModePipeline;
|
||||
} else if (currentPipelineIndex <= CAL_3D_INDEX) {
|
||||
return calib3dPipe;
|
||||
} else {
|
||||
return pipelines.get(currentPipelineIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCurrentPipeline(int index) {
|
||||
CVPipeline newPipeline = null;
|
||||
|
||||
if (index == DRIVERMODE_INDEX) {
|
||||
ScriptManager.queueEvent(ScriptEventType.kLEDOff);
|
||||
newPipeline = driverModePipeline;
|
||||
|
||||
// if we're changing into driver mode, try to set the nt entry to true
|
||||
parentProcess.setDriverModeEntry(true);
|
||||
} else if (index == CAL_3D_INDEX) {
|
||||
parentProcess.setDriverModeEntry(true);
|
||||
|
||||
newPipeline = calib3dPipe;
|
||||
} else {
|
||||
if (index < pipelines.size()&&index>=0) {
|
||||
newPipeline = pipelines.get(index);
|
||||
|
||||
// if we're switching out of driver mode, try to set the nt entry to false
|
||||
parentProcess.setDriverModeEntry(false);
|
||||
ScriptManager.queueEvent(ScriptEventType.kLEDOn);
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO alert/warn user that pipeline doesnt exsits
|
||||
System.err.println("Index is out of bounds");
|
||||
}
|
||||
}
|
||||
if (newPipeline != null) {
|
||||
lastPipelineIndex = currentPipelineIndex;
|
||||
currentPipelineIndex = index;
|
||||
getCurrentPipeline().initPipeline(parentProcess.getCamera());
|
||||
|
||||
if (ConfigManager.settings.currentCamera.equals(parentProcess.getCamera().getProperties().name)) {
|
||||
ConfigManager.settings.currentPipeline = currentPipelineIndex;
|
||||
|
||||
HashMap<String, Object> pipeChange = new HashMap<>();
|
||||
pipeChange.put("currentPipeline", currentPipelineIndex);
|
||||
SocketHandler.broadcastMessage(pipeChange);
|
||||
try {
|
||||
SocketHandler.sendFullSettings();
|
||||
} catch (Exception e) {
|
||||
// avoid NullPointerException when run before threads start
|
||||
}
|
||||
}
|
||||
newPipeline.initPipeline(parentProcess.getCamera());
|
||||
if (parentProcess.cameraStreamer != null)
|
||||
parentProcess.cameraStreamer.setDivisor(newPipeline.settings.streamDivisor, true);
|
||||
if (ntIndexEntry != null) {
|
||||
ntIndexEntry.setDouble(index);
|
||||
}
|
||||
}
|
||||
|
||||
// gain setting quirk
|
||||
if (!parentProcess.cameraCapture.getProperties().isPS3Eye) {
|
||||
getCurrentPipeline().settings.gain = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public void addPipeline(CVPipelineSettings setting) {
|
||||
addInternalPipeline(setting);
|
||||
savePipelineConfig(setting);
|
||||
}
|
||||
|
||||
public void addPipeline(CVPipeline pipeline) {
|
||||
pipelines.add(pipeline);
|
||||
reassignIndexes();
|
||||
savePipelineConfig(pipeline.settings);
|
||||
}
|
||||
|
||||
public void addNewPipeline(String piplineName) {
|
||||
StandardCVPipeline newPipeline = new StandardCVPipeline();
|
||||
newPipeline.settings.nickname = piplineName;
|
||||
newPipeline.settings.index = pipelines.size();
|
||||
addPipeline(newPipeline);
|
||||
}
|
||||
|
||||
public CVPipeline getPipeline(int index) {
|
||||
return pipelines.get(index);
|
||||
}
|
||||
|
||||
public void duplicatePipeline(CVPipelineSettings pipeline) throws DuplicatedKeyException {
|
||||
duplicatePipeline(pipeline, parentProcess);
|
||||
}
|
||||
|
||||
public void duplicatePipeline(CVPipelineSettings pipeline, VisionProcess destinationProcess) throws DuplicatedKeyException {
|
||||
pipeline.index = destinationProcess.pipelineManager.pipelines.size();
|
||||
pipeline.nickname += "(Copy)";
|
||||
if (destinationProcess.pipelineManager.pipelines.stream().anyMatch(c -> c.settings.nickname.equals(pipeline.nickname))){
|
||||
throw new DuplicatedKeyException("key Already exists");
|
||||
} else{
|
||||
destinationProcess.pipelineManager.addPipeline(pipeline);
|
||||
}
|
||||
}
|
||||
|
||||
public void renameCurrentPipeline(String newName) {
|
||||
CVPipelineSettings settings = getCurrentPipeline().settings;
|
||||
renamePipelineConfig(settings, newName);
|
||||
}
|
||||
|
||||
public void deleteCurrentPipeline() {
|
||||
deletePipeline(currentPipelineIndex);
|
||||
}
|
||||
|
||||
private void deletePipeline(int index) {
|
||||
if (index == currentPipelineIndex) {
|
||||
currentPipelineIndex -= 1;
|
||||
}
|
||||
deletePipelineConfig(getPipeline(index).settings);
|
||||
pipelines.remove(index);
|
||||
reassignIndexes();
|
||||
}
|
||||
|
||||
public void saveDriverModeConfig() {
|
||||
getConfig().saveDriverMode(driverModePipeline.settings);
|
||||
}
|
||||
|
||||
private static final Comparator<CVPipeline> IndexComparator = (o1, o2) -> {
|
||||
int o1Index = o1.settings.index;
|
||||
int o2Index = o2.settings.index;
|
||||
|
||||
if (o1Index == o2Index) {
|
||||
return 0;
|
||||
} else if (o1Index < o2Index) {
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
};
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
package com.chameleonvision.vision.pipeline.impl;
|
||||
|
||||
import com.chameleonvision.config.CameraCalibrationConfig;
|
||||
import com.chameleonvision.config.ConfigManager;
|
||||
import com.chameleonvision.vision.VisionManager;
|
||||
import com.chameleonvision.vision.camera.CameraCapture;
|
||||
import com.chameleonvision.vision.pipeline.CVPipeline;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import edu.wpi.cscore.VideoMode;
|
||||
import edu.wpi.first.wpilibj.util.Units;
|
||||
import org.opencv.calib3d.Calib3d;
|
||||
import org.opencv.core.*;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Calibrate3dPipeline extends CVPipeline<DriverVisionPipeline.DriverPipelineResult, StandardCVPipelineSettings> {
|
||||
|
||||
private int checkerboardSquaresHigh = 7;
|
||||
private int checkerboardSquaresWide = 7;
|
||||
|
||||
private MatOfPoint3f objP;// new MatOfPoint3f(checkerboardSquaresHigh + checkerboardSquaresWide, 3);//(checkerboardSquaresWide * checkerboardSquaresHigh, 3);
|
||||
private Size patternSize = new Size(checkerboardSquaresHigh, checkerboardSquaresWide);
|
||||
private Size imageSize;
|
||||
double checkerboardSquareSize = 1; // inches!
|
||||
private MatOfPoint2f calibrationOutput = new MatOfPoint2f();
|
||||
private List<Mat> objpoints = new ArrayList<>();
|
||||
private List<Mat> imgpoints = new ArrayList<>();
|
||||
|
||||
public static double checkerboardSquareSizeUnits = Units.inchesToMeters(1.0);
|
||||
|
||||
public static final int MIN_COUNT = 15;
|
||||
private VideoMode calibrationMode;
|
||||
private final Size windowSize = new Size(11, 11);
|
||||
private final Size zeroZone = new Size(-1, -1);
|
||||
private TermCriteria criteria = new TermCriteria(3, 30, 0.001); //(Imgproc.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
|
||||
|
||||
private int captureCount = 0;
|
||||
private double calibrationAccuracy = 0;
|
||||
private boolean wantsSnapshot = false;
|
||||
private double squareSizeInches;
|
||||
|
||||
public Calibrate3dPipeline(StandardCVPipelineSettings settings) {
|
||||
super(settings);
|
||||
|
||||
objP = new MatOfPoint3f();
|
||||
|
||||
for(int i = 0; i < checkerboardSquaresHigh * checkerboardSquaresWide; i++) {
|
||||
objP.push_back(new MatOfPoint3f(new Point3(i / checkerboardSquaresWide, i % checkerboardSquaresHigh, 0.0f)));
|
||||
}
|
||||
|
||||
setSquareSize(checkerboardSquareSizeUnits);
|
||||
|
||||
objpoints.forEach(Mat::release);
|
||||
imgpoints.forEach(Mat::release);
|
||||
objpoints.clear();
|
||||
imgpoints.clear();
|
||||
}
|
||||
|
||||
public void setSquareSize(double size) {
|
||||
this.squareSizeInches = size;
|
||||
}
|
||||
|
||||
public void takeSnapshot() {
|
||||
wantsSnapshot = true;
|
||||
}
|
||||
|
||||
public boolean hasEnoughSnapshots() {
|
||||
return captureCount >= MIN_COUNT - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DriverVisionPipeline.DriverPipelineResult runPipeline(Mat inputMat) {
|
||||
|
||||
// look for checkerboard
|
||||
Imgproc.cvtColor(inputMat, inputMat, Imgproc.COLOR_BGR2GRAY);
|
||||
var checkerboardFound = Calib3d.findChessboardCorners(inputMat, patternSize, calibrationOutput);
|
||||
|
||||
|
||||
|
||||
if(!checkerboardFound) {
|
||||
Imgproc.cvtColor(inputMat, inputMat, Imgproc.COLOR_GRAY2BGR);
|
||||
|
||||
return new DriverVisionPipeline.DriverPipelineResult(null, inputMat, 0);
|
||||
}
|
||||
|
||||
// System.out.println("[SolvePNP] checkerboard found!!");
|
||||
|
||||
// cool we found a checkerboard
|
||||
// do corner subpixel
|
||||
Imgproc.cornerSubPix(inputMat, calibrationOutput, windowSize, zeroZone, criteria);
|
||||
|
||||
// convert back to BGR
|
||||
Imgproc.cvtColor(inputMat, inputMat, Imgproc.COLOR_GRAY2BGR);
|
||||
// draw the chessboard
|
||||
Calib3d.drawChessboardCorners(inputMat, patternSize, calibrationOutput, true);
|
||||
|
||||
if(wantsSnapshot) {
|
||||
this.imageSize = new Size(inputMat.width(), inputMat.height());
|
||||
|
||||
var mat = new MatOfPoint3f();
|
||||
calibrationOutput.copyTo(mat);
|
||||
this.objpoints.add(objP);
|
||||
imgpoints.add(mat);
|
||||
captureCount++;
|
||||
wantsSnapshot = false;
|
||||
}
|
||||
|
||||
imageSize = new Size(inputMat.width(), inputMat.height());
|
||||
|
||||
return new DriverVisionPipeline.DriverPipelineResult(null, inputMat, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initPipeline(CameraCapture camera) {
|
||||
super.initPipeline(camera);
|
||||
objpoints.clear();
|
||||
imgpoints.clear();
|
||||
captureCount = 0;
|
||||
}
|
||||
|
||||
public boolean tryCalibration() {
|
||||
if (!hasEnoughSnapshots()) return false;
|
||||
|
||||
Mat cameraMatrix = new Mat();
|
||||
Mat distortionCoeffs = new Mat();
|
||||
List<Mat> rvecs = new ArrayList<>();
|
||||
List<Mat> tvecs = new ArrayList<>();
|
||||
|
||||
try {
|
||||
calibrationAccuracy = Calib3d.calibrateCamera(objpoints, imgpoints, imageSize, cameraMatrix, distortionCoeffs, rvecs, tvecs);
|
||||
} catch(Exception e) {
|
||||
System.err.println("Camera calibration failed!");
|
||||
initPipeline(cameraCapture);
|
||||
return false;
|
||||
}
|
||||
|
||||
VideoMode currentVidMode = cameraCapture.getCurrentVideoMode();
|
||||
Size resolution = new Size(currentVidMode.width, currentVidMode.height);
|
||||
CameraCalibrationConfig cal = new CameraCalibrationConfig(resolution, cameraMatrix, distortionCoeffs, squareSizeInches);
|
||||
|
||||
VisionManager.getCurrentUIVisionProcess().addCalibration(cal);
|
||||
|
||||
try {
|
||||
System.out.printf("CALIBRATION SUCCESS (with accuracy %s)! camMatrix: \n%s\ndistortionCoeffs:\n%s\n",
|
||||
calibrationAccuracy, new ObjectMapper().writeValueAsString(cal.cameraMatrix), new ObjectMapper().writeValueAsString(cal.distortionCoeffs));
|
||||
} catch (JsonProcessingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
ConfigManager.saveGeneralSettings();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setVideoMode(VideoMode mode){
|
||||
this.calibrationMode = mode;
|
||||
}
|
||||
|
||||
public int getSnapshotCount() {
|
||||
return captureCount + 1;
|
||||
}
|
||||
|
||||
public double getCalibrationAccuracy(){
|
||||
return calibrationAccuracy;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user